mirror of
https://github.com/nzbget/nzbget.git
synced 2025-12-31 10:07:45 -05:00
3056 lines
82 KiB
C++
3056 lines
82 KiB
C++
/*
|
|
* This file is part of nzbget
|
|
*
|
|
* Copyright (C) 2007-2015 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*
|
|
* $Revision$
|
|
* $Date$
|
|
*
|
|
*/
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#ifdef WIN32
|
|
#include "win32.h"
|
|
#else
|
|
#include <pthread.h>
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/stat.h>
|
|
#include <stdio.h>
|
|
#include <time.h>
|
|
#include <stdarg.h>
|
|
#include <ctype.h>
|
|
#include <deque>
|
|
#include <algorithm>
|
|
|
|
#include "nzbget.h"
|
|
#include "DiskState.h"
|
|
#include "Options.h"
|
|
#include "Log.h"
|
|
#include "Util.h"
|
|
|
|
static const char* FORMATVERSION_SIGNATURE = "nzbget diskstate file version ";
|
|
|
|
#ifdef WIN32
|
|
// Windows doesn't have standard "vsscanf"
|
|
// Hack from http://stackoverflow.com/questions/2457331/replacement-for-vsscanf-on-msvc
|
|
int vsscanf(const char *s, const char *fmt, va_list ap)
|
|
{
|
|
// up to max 10 arguments
|
|
void *a[10];
|
|
for (int i = 0; i < sizeof(a)/sizeof(a[0]); i++)
|
|
{
|
|
a[i] = va_arg(ap, void*);
|
|
}
|
|
return sscanf(s, fmt, a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9]);
|
|
}
|
|
#endif
|
|
|
|
/* Parse signature and return format version number
|
|
*/
|
|
int ParseFormatVersion(const char* szFormatSignature)
|
|
{
|
|
if (strncmp(szFormatSignature, FORMATVERSION_SIGNATURE, strlen(FORMATVERSION_SIGNATURE)))
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
return atoi(szFormatSignature + strlen(FORMATVERSION_SIGNATURE));
|
|
}
|
|
|
|
|
|
class StateFile
|
|
{
|
|
private:
|
|
char m_szDestFilename[1024];
|
|
char m_szTempFilename[1024];
|
|
int m_iFormatVersion;
|
|
int m_iFileVersion;
|
|
FILE* m_pFile;
|
|
|
|
public:
|
|
StateFile(const char* szFilename, int iFormatVersion);
|
|
~StateFile();
|
|
void Discard();
|
|
bool FileExists();
|
|
FILE* BeginWriteTransaction();
|
|
bool FinishWriteTransaction();
|
|
FILE* BeginReadTransaction();
|
|
int GetFileVersion() { return m_iFileVersion; }
|
|
const char* GetDestFilename() { return m_szDestFilename; }
|
|
};
|
|
|
|
|
|
StateFile::StateFile(const char* szFilename, int iFormatVersion)
|
|
{
|
|
m_pFile = NULL;
|
|
|
|
m_iFormatVersion = iFormatVersion;
|
|
|
|
snprintf(m_szDestFilename, 1024, "%s%s", g_pOptions->GetQueueDir(), szFilename);
|
|
m_szDestFilename[1024-1] = '\0';
|
|
|
|
snprintf(m_szTempFilename, 1024, "%s%s.new", g_pOptions->GetQueueDir(), szFilename);
|
|
m_szTempFilename[1024-1] = '\0';
|
|
}
|
|
|
|
StateFile::~StateFile()
|
|
{
|
|
if (m_pFile)
|
|
{
|
|
fclose(m_pFile);
|
|
}
|
|
}
|
|
|
|
void StateFile::Discard()
|
|
{
|
|
remove(m_szDestFilename);
|
|
}
|
|
|
|
bool StateFile::FileExists()
|
|
{
|
|
return Util::FileExists(m_szDestFilename) || Util::FileExists(m_szTempFilename);
|
|
}
|
|
|
|
FILE* StateFile::BeginWriteTransaction()
|
|
{
|
|
m_pFile = fopen(m_szTempFilename, FOPEN_WB);
|
|
|
|
if (!m_pFile)
|
|
{
|
|
char szErrBuf[256];
|
|
Util::GetLastErrorMessage(szErrBuf, sizeof(szErrBuf));
|
|
error("Error saving diskstate: Could not create file %s: %s", m_szTempFilename, szErrBuf);
|
|
return NULL;
|
|
}
|
|
|
|
fprintf(m_pFile, "%s%i\n", FORMATVERSION_SIGNATURE, m_iFormatVersion);
|
|
|
|
return m_pFile;
|
|
}
|
|
|
|
bool StateFile::FinishWriteTransaction()
|
|
{
|
|
char szErrBuf[256];
|
|
|
|
// flush file content before renaming
|
|
if (g_pOptions->GetFlushQueue())
|
|
{
|
|
debug("Flushing data for file %s", Util::BaseFileName(m_szTempFilename));
|
|
fflush(m_pFile);
|
|
if (!Util::FlushFileBuffers(fileno(m_pFile), szErrBuf, sizeof(szErrBuf)))
|
|
{
|
|
warn("Could not flush file %s into disk: %s", m_szTempFilename, szErrBuf);
|
|
}
|
|
}
|
|
|
|
fclose(m_pFile);
|
|
m_pFile = NULL;
|
|
|
|
// now rename to dest file name
|
|
remove(m_szDestFilename);
|
|
if (rename(m_szTempFilename, m_szDestFilename))
|
|
{
|
|
Util::GetLastErrorMessage(szErrBuf, sizeof(szErrBuf));
|
|
error("Error saving diskstate: Could not rename file %s to %s: %s",
|
|
m_szTempFilename, m_szDestFilename, szErrBuf);
|
|
return false;
|
|
}
|
|
|
|
// flush directory buffer after renaming
|
|
if (g_pOptions->GetFlushQueue())
|
|
{
|
|
debug("Flushing directory for file %s", Util::BaseFileName(m_szDestFilename));
|
|
if (!Util::FlushDirBuffers(m_szDestFilename, szErrBuf, sizeof(szErrBuf)))
|
|
{
|
|
warn("Could not flush directory buffers for file %s into disk: %s", m_szDestFilename, szErrBuf);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
FILE* StateFile::BeginReadTransaction()
|
|
{
|
|
if (!Util::FileExists(m_szDestFilename) && Util::FileExists(m_szTempFilename))
|
|
{
|
|
// disaster recovery: temp-file exists but the dest-file doesn't
|
|
warn("Restoring diskstate file %s from %s", Util::BaseFileName(m_szDestFilename), Util::BaseFileName(m_szTempFilename));
|
|
if (rename(m_szTempFilename, m_szDestFilename))
|
|
{
|
|
char szErrBuf[256];
|
|
Util::GetLastErrorMessage(szErrBuf, sizeof(szErrBuf));
|
|
error("Error restoring diskstate: Could not rename file %s to %s: %s",
|
|
m_szTempFilename, m_szDestFilename, szErrBuf);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
m_pFile = fopen(m_szDestFilename, FOPEN_RB);
|
|
|
|
if (!m_pFile)
|
|
{
|
|
char szErrBuf[256];
|
|
Util::GetLastErrorMessage(szErrBuf, sizeof(szErrBuf));
|
|
error("Error reading diskstate: could not open file %s: %s", m_szDestFilename, szErrBuf);
|
|
return NULL;
|
|
}
|
|
|
|
char FileSignatur[128];
|
|
fgets(FileSignatur, sizeof(FileSignatur), m_pFile);
|
|
m_iFileVersion = ParseFormatVersion(FileSignatur);
|
|
if (m_iFileVersion > m_iFormatVersion)
|
|
{
|
|
error("Could not load diskstate due to file version mismatch");
|
|
fclose(m_pFile);
|
|
m_pFile = NULL;
|
|
return NULL;
|
|
}
|
|
|
|
return m_pFile;
|
|
}
|
|
|
|
/*
|
|
* Standard fscanf scans beoynd current line if the next line is empty.
|
|
* This wrapper fixes that.
|
|
*/
|
|
int DiskState::fscanf(FILE* infile, const char* Format, ...)
|
|
{
|
|
char szLine[1024];
|
|
if (!fgets(szLine, sizeof(szLine), infile))
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
va_list ap;
|
|
va_start(ap, Format);
|
|
int res = vsscanf(szLine, Format, ap);
|
|
va_end(ap);
|
|
|
|
return res;
|
|
}
|
|
|
|
/* Save Download Queue to Disk.
|
|
* The Disk State consists of file "queue", which contains the order of files,
|
|
* and of one diskstate-file for each file in download queue.
|
|
* This function saves file "queue" and files with NZB-info. It does not
|
|
* save file-infos.
|
|
*
|
|
* For safety:
|
|
* - first save to temp-file (queue.new)
|
|
* - then delete queue
|
|
* - then rename queue.new to queue
|
|
*/
|
|
bool DiskState::SaveDownloadQueue(DownloadQueue* pDownloadQueue)
|
|
{
|
|
debug("Saving queue to disk");
|
|
|
|
StateFile stateFile("queue", 54);
|
|
|
|
if (pDownloadQueue->GetQueue()->empty() &&
|
|
pDownloadQueue->GetHistory()->empty())
|
|
{
|
|
stateFile.Discard();
|
|
return true;
|
|
}
|
|
|
|
FILE* outfile = stateFile.BeginWriteTransaction();
|
|
if (!outfile)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// save nzb-infos
|
|
SaveNZBQueue(pDownloadQueue, outfile);
|
|
|
|
// save history
|
|
SaveHistory(pDownloadQueue, outfile);
|
|
|
|
// now rename to dest file name
|
|
return stateFile.FinishWriteTransaction();
|
|
}
|
|
|
|
bool DiskState::LoadDownloadQueue(DownloadQueue* pDownloadQueue, Servers* pServers)
|
|
{
|
|
debug("Loading queue from disk");
|
|
|
|
StateFile stateFile("queue", 54);
|
|
|
|
FILE* infile = stateFile.BeginReadTransaction();
|
|
if (!infile)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool bOK = false;
|
|
int iFormatVersion = stateFile.GetFileVersion();
|
|
|
|
NZBList nzbList(false);
|
|
NZBList sortList(false);
|
|
|
|
if (iFormatVersion < 43)
|
|
{
|
|
// load nzb-infos
|
|
if (!LoadNZBList(&nzbList, pServers, infile, iFormatVersion)) goto error;
|
|
|
|
// load file-infos
|
|
if (!LoadFileQueue12(&nzbList, &sortList, infile, iFormatVersion)) goto error;
|
|
}
|
|
else
|
|
{
|
|
if (!LoadNZBList(pDownloadQueue->GetQueue(), pServers, infile, iFormatVersion)) goto error;
|
|
}
|
|
|
|
if (iFormatVersion >= 7 && iFormatVersion < 45)
|
|
{
|
|
// load post-queue from v12
|
|
if (!LoadPostQueue12(pDownloadQueue, &nzbList, infile, iFormatVersion)) goto error;
|
|
}
|
|
else if (iFormatVersion < 7)
|
|
{
|
|
// load post-queue from v5
|
|
LoadPostQueue5(pDownloadQueue, &nzbList);
|
|
}
|
|
|
|
if (iFormatVersion >= 15 && iFormatVersion < 46)
|
|
{
|
|
// load url-queue
|
|
if (!LoadUrlQueue12(pDownloadQueue, infile, iFormatVersion)) goto error;
|
|
}
|
|
|
|
if (iFormatVersion >= 9)
|
|
{
|
|
// load history
|
|
if (!LoadHistory(pDownloadQueue, &nzbList, pServers, infile, iFormatVersion)) goto error;
|
|
}
|
|
|
|
if (iFormatVersion >= 9 && iFormatVersion < 43)
|
|
{
|
|
// load parked file-infos
|
|
if (!LoadFileQueue12(&nzbList, NULL, infile, iFormatVersion)) goto error;
|
|
}
|
|
|
|
if (iFormatVersion < 29)
|
|
{
|
|
CalcCriticalHealth(&nzbList);
|
|
}
|
|
|
|
if (iFormatVersion < 43)
|
|
{
|
|
// finalize queue reading
|
|
CompleteNZBList12(pDownloadQueue, &sortList, iFormatVersion);
|
|
}
|
|
|
|
if (iFormatVersion < 47)
|
|
{
|
|
CompleteDupList12(pDownloadQueue, iFormatVersion);
|
|
}
|
|
|
|
if (!LoadAllFileStates(pDownloadQueue, pServers)) goto error;
|
|
|
|
bOK = true;
|
|
|
|
error:
|
|
|
|
if (!bOK)
|
|
{
|
|
error("Error reading diskstate for download queue and history");
|
|
}
|
|
|
|
NZBInfo::ResetGenID(true);
|
|
FileInfo::ResetGenID(true);
|
|
|
|
if (iFormatVersion > 0)
|
|
{
|
|
CalcFileStats(pDownloadQueue, iFormatVersion);
|
|
}
|
|
|
|
return bOK;
|
|
}
|
|
|
|
void DiskState::CompleteNZBList12(DownloadQueue* pDownloadQueue, NZBList* pNZBList, int iFormatVersion)
|
|
{
|
|
// put all NZBs referenced from file queue into pDownloadQueue->GetQueue()
|
|
for (NZBList::iterator it = pNZBList->begin(); it != pNZBList->end(); it++)
|
|
{
|
|
NZBInfo* pNZBInfo = *it;
|
|
pDownloadQueue->GetQueue()->push_back(pNZBInfo);
|
|
}
|
|
|
|
if (31 <= iFormatVersion && iFormatVersion < 42)
|
|
{
|
|
// due to a bug in r811 (v12-testing) new NZBIDs were generated on each refresh of web-ui
|
|
// this resulted in very high numbers for NZBIDs
|
|
// here we renumber NZBIDs in order to keep them low.
|
|
NZBInfo::ResetGenID(false);
|
|
int iID = 1;
|
|
for (NZBList::iterator it = pNZBList->begin(); it != pNZBList->end(); it++)
|
|
{
|
|
NZBInfo* pNZBInfo = *it;
|
|
pNZBInfo->SetID(iID++);
|
|
}
|
|
}
|
|
}
|
|
|
|
void DiskState::CompleteDupList12(DownloadQueue* pDownloadQueue, int iFormatVersion)
|
|
{
|
|
NZBInfo::ResetGenID(true);
|
|
|
|
for (HistoryList::iterator it = pDownloadQueue->GetHistory()->begin(); it != pDownloadQueue->GetHistory()->end(); it++)
|
|
{
|
|
HistoryInfo* pHistoryInfo = *it;
|
|
|
|
if (pHistoryInfo->GetKind() == HistoryInfo::hkDup)
|
|
{
|
|
pHistoryInfo->GetDupInfo()->SetID(NZBInfo::GenerateID());
|
|
}
|
|
}
|
|
}
|
|
|
|
void DiskState::SaveNZBQueue(DownloadQueue* pDownloadQueue, FILE* outfile)
|
|
{
|
|
debug("Saving nzb list to disk");
|
|
|
|
fprintf(outfile, "%i\n", (int)pDownloadQueue->GetQueue()->size());
|
|
for (NZBList::iterator it = pDownloadQueue->GetQueue()->begin(); it != pDownloadQueue->GetQueue()->end(); it++)
|
|
{
|
|
NZBInfo* pNZBInfo = *it;
|
|
SaveNZBInfo(pNZBInfo, outfile);
|
|
}
|
|
}
|
|
|
|
bool DiskState::LoadNZBList(NZBList* pNZBList, Servers* pServers, FILE* infile, int iFormatVersion)
|
|
{
|
|
debug("Loading nzb list from disk");
|
|
|
|
// load nzb-infos
|
|
int size;
|
|
if (fscanf(infile, "%i\n", &size) != 1) goto error;
|
|
for (int i = 0; i < size; i++)
|
|
{
|
|
NZBInfo* pNZBInfo = new NZBInfo();
|
|
pNZBList->push_back(pNZBInfo);
|
|
if (!LoadNZBInfo(pNZBInfo, pServers, infile, iFormatVersion)) goto error;
|
|
}
|
|
|
|
return true;
|
|
|
|
error:
|
|
error("Error reading nzb list from disk");
|
|
return false;
|
|
}
|
|
|
|
void DiskState::SaveNZBInfo(NZBInfo* pNZBInfo, FILE* outfile)
|
|
{
|
|
fprintf(outfile, "%i\n", pNZBInfo->GetID());
|
|
fprintf(outfile, "%i\n", (int)pNZBInfo->GetKind());
|
|
fprintf(outfile, "%s\n", pNZBInfo->GetURL());
|
|
fprintf(outfile, "%s\n", pNZBInfo->GetFilename());
|
|
fprintf(outfile, "%s\n", pNZBInfo->GetDestDir());
|
|
fprintf(outfile, "%s\n", pNZBInfo->GetFinalDir());
|
|
fprintf(outfile, "%s\n", pNZBInfo->GetQueuedFilename());
|
|
fprintf(outfile, "%s\n", pNZBInfo->GetName());
|
|
fprintf(outfile, "%s\n", pNZBInfo->GetCategory());
|
|
fprintf(outfile, "%i,%i,%i,%i,%i\n", (int)pNZBInfo->GetPriority(),
|
|
pNZBInfo->GetPostInfo() ? (int)pNZBInfo->GetPostInfo()->GetStage() + 1 : 0,
|
|
(int)pNZBInfo->GetDeletePaused(), (int)pNZBInfo->GetManyDupeFiles(), pNZBInfo->GetFeedID());
|
|
fprintf(outfile, "%i,%i,%i,%i,%i,%i,%i\n", (int)pNZBInfo->GetParStatus(), (int)pNZBInfo->GetUnpackStatus(),
|
|
(int)pNZBInfo->GetMoveStatus(), (int)pNZBInfo->GetRenameStatus(), (int)pNZBInfo->GetDeleteStatus(),
|
|
(int)pNZBInfo->GetMarkStatus(), (int)pNZBInfo->GetUrlStatus());
|
|
fprintf(outfile, "%i,%i,%i\n", (int)pNZBInfo->GetUnpackCleanedUpDisk(), (int)pNZBInfo->GetHealthPaused(),
|
|
(int)pNZBInfo->GetAddUrlPaused());
|
|
fprintf(outfile, "%i,%i,%i\n", pNZBInfo->GetFileCount(), pNZBInfo->GetParkedFileCount(), pNZBInfo->GetMessageCount());
|
|
fprintf(outfile, "%i,%i\n", (int)pNZBInfo->GetMinTime(), (int)pNZBInfo->GetMaxTime());
|
|
fprintf(outfile, "%i,%i,%i\n", (int)pNZBInfo->GetParFull(),
|
|
pNZBInfo->GetPostInfo() ? (int)pNZBInfo->GetPostInfo()->GetForceParFull() : 0,
|
|
pNZBInfo->GetPostInfo() ? (int)pNZBInfo->GetPostInfo()->GetForceRepair() : 0);
|
|
|
|
fprintf(outfile, "%u,%u\n", pNZBInfo->GetFullContentHash(), pNZBInfo->GetFilteredContentHash());
|
|
|
|
unsigned long High1, Low1, High2, Low2, High3, Low3;
|
|
Util::SplitInt64(pNZBInfo->GetSize(), &High1, &Low1);
|
|
Util::SplitInt64(pNZBInfo->GetSuccessSize(), &High2, &Low2);
|
|
Util::SplitInt64(pNZBInfo->GetFailedSize(), &High3, &Low3);
|
|
fprintf(outfile, "%lu,%lu,%lu,%lu,%lu,%lu\n", High1, Low1, High2, Low2, High3, Low3);
|
|
|
|
Util::SplitInt64(pNZBInfo->GetParSize(), &High1, &Low1);
|
|
Util::SplitInt64(pNZBInfo->GetParSuccessSize(), &High2, &Low2);
|
|
Util::SplitInt64(pNZBInfo->GetParFailedSize(), &High3, &Low3);
|
|
fprintf(outfile, "%lu,%lu,%lu,%lu,%lu,%lu\n", High1, Low1, High2, Low2, High3, Low3);
|
|
|
|
fprintf(outfile, "%i,%i,%i\n", pNZBInfo->GetTotalArticles(), pNZBInfo->GetSuccessArticles(), pNZBInfo->GetFailedArticles());
|
|
|
|
fprintf(outfile, "%s\n", pNZBInfo->GetDupeKey());
|
|
fprintf(outfile, "%i,%i\n", (int)pNZBInfo->GetDupeMode(), pNZBInfo->GetDupeScore());
|
|
|
|
Util::SplitInt64(pNZBInfo->GetDownloadedSize(), &High1, &Low1);
|
|
fprintf(outfile, "%lu,%lu,%i,%i,%i,%i,%i\n", High1, Low1, pNZBInfo->GetDownloadSec(), pNZBInfo->GetPostTotalSec(),
|
|
pNZBInfo->GetParSec(), pNZBInfo->GetRepairSec(), pNZBInfo->GetUnpackSec());
|
|
|
|
fprintf(outfile, "%i\n", (int)pNZBInfo->GetCompletedFiles()->size());
|
|
for (CompletedFiles::iterator it = pNZBInfo->GetCompletedFiles()->begin(); it != pNZBInfo->GetCompletedFiles()->end(); it++)
|
|
{
|
|
CompletedFile* pCompletedFile = *it;
|
|
fprintf(outfile, "%i,%i,%lu,%s\n", pCompletedFile->GetID(), (int)pCompletedFile->GetStatus(),
|
|
pCompletedFile->GetCrc(), pCompletedFile->GetFileName());
|
|
}
|
|
|
|
fprintf(outfile, "%i\n", (int)pNZBInfo->GetParameters()->size());
|
|
for (NZBParameterList::iterator it = pNZBInfo->GetParameters()->begin(); it != pNZBInfo->GetParameters()->end(); it++)
|
|
{
|
|
NZBParameter* pParameter = *it;
|
|
fprintf(outfile, "%s=%s\n", pParameter->GetName(), pParameter->GetValue());
|
|
}
|
|
|
|
fprintf(outfile, "%i\n", (int)pNZBInfo->GetScriptStatuses()->size());
|
|
for (ScriptStatusList::iterator it = pNZBInfo->GetScriptStatuses()->begin(); it != pNZBInfo->GetScriptStatuses()->end(); it++)
|
|
{
|
|
ScriptStatus* pScriptStatus = *it;
|
|
fprintf(outfile, "%i,%s\n", pScriptStatus->GetStatus(), pScriptStatus->GetName());
|
|
}
|
|
|
|
SaveServerStats(pNZBInfo->GetServerStats(), outfile);
|
|
|
|
// save file-infos
|
|
int iSize = 0;
|
|
for (FileList::iterator it = pNZBInfo->GetFileList()->begin(); it != pNZBInfo->GetFileList()->end(); it++)
|
|
{
|
|
FileInfo* pFileInfo = *it;
|
|
if (!pFileInfo->GetDeleted())
|
|
{
|
|
iSize++;
|
|
}
|
|
}
|
|
fprintf(outfile, "%i\n", iSize);
|
|
for (FileList::iterator it = pNZBInfo->GetFileList()->begin(); it != pNZBInfo->GetFileList()->end(); it++)
|
|
{
|
|
FileInfo* pFileInfo = *it;
|
|
if (!pFileInfo->GetDeleted())
|
|
{
|
|
fprintf(outfile, "%i,%i,%i,%i\n", pFileInfo->GetID(), (int)pFileInfo->GetPaused(),
|
|
(int)pFileInfo->GetTime(), (int)pFileInfo->GetExtraPriority());
|
|
}
|
|
}
|
|
}
|
|
|
|
bool DiskState::LoadNZBInfo(NZBInfo* pNZBInfo, Servers* pServers, FILE* infile, int iFormatVersion)
|
|
{
|
|
char buf[10240];
|
|
|
|
if (iFormatVersion >= 24)
|
|
{
|
|
int iID;
|
|
if (fscanf(infile, "%i\n", &iID) != 1) goto error;
|
|
pNZBInfo->SetID(iID);
|
|
}
|
|
|
|
if (iFormatVersion >= 46)
|
|
{
|
|
int iKind;
|
|
if (fscanf(infile, "%i\n", &iKind) != 1) goto error;
|
|
pNZBInfo->SetKind((NZBInfo::EKind)iKind);
|
|
|
|
if (!fgets(buf, sizeof(buf), infile)) goto error;
|
|
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
|
|
pNZBInfo->SetURL(buf);
|
|
}
|
|
|
|
if (!fgets(buf, sizeof(buf), infile)) goto error;
|
|
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
|
|
pNZBInfo->SetFilename(buf);
|
|
|
|
if (!fgets(buf, sizeof(buf), infile)) goto error;
|
|
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
|
|
pNZBInfo->SetDestDir(buf);
|
|
|
|
if (iFormatVersion >= 27)
|
|
{
|
|
if (!fgets(buf, sizeof(buf), infile)) goto error;
|
|
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
|
|
pNZBInfo->SetFinalDir(buf);
|
|
}
|
|
|
|
if (iFormatVersion >= 5)
|
|
{
|
|
if (!fgets(buf, sizeof(buf), infile)) goto error;
|
|
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
|
|
pNZBInfo->SetQueuedFilename(buf);
|
|
}
|
|
|
|
if (iFormatVersion >= 13)
|
|
{
|
|
if (!fgets(buf, sizeof(buf), infile)) goto error;
|
|
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
|
|
if (strlen(buf) > 0)
|
|
{
|
|
pNZBInfo->SetName(buf);
|
|
}
|
|
}
|
|
|
|
if (iFormatVersion >= 4)
|
|
{
|
|
if (!fgets(buf, sizeof(buf), infile)) goto error;
|
|
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
|
|
pNZBInfo->SetCategory(buf);
|
|
}
|
|
|
|
if (true) // clang requires a block for goto to work
|
|
{
|
|
int iPriority = 0, iPostProcess = 0, iPostStage = 0, iDeletePaused = 0, iManyDupeFiles = 0, iFeedID = 0;
|
|
if (iFormatVersion >= 54)
|
|
{
|
|
if (fscanf(infile, "%i,%i,%i,%i,%i\n", &iPriority, &iPostStage, &iDeletePaused, &iManyDupeFiles, &iFeedID) != 5) goto error;
|
|
}
|
|
else if (iFormatVersion >= 45)
|
|
{
|
|
if (fscanf(infile, "%i,%i,%i,%i\n", &iPriority, &iPostStage, &iDeletePaused, &iManyDupeFiles) != 4) goto error;
|
|
}
|
|
else if (iFormatVersion >= 44)
|
|
{
|
|
if (fscanf(infile, "%i,%i,%i,%i\n", &iPriority, &iPostProcess, &iDeletePaused, &iManyDupeFiles) != 4) goto error;
|
|
}
|
|
else if (iFormatVersion >= 41)
|
|
{
|
|
if (fscanf(infile, "%i,%i,%i\n", &iPostProcess, &iDeletePaused, &iManyDupeFiles) != 3) goto error;
|
|
}
|
|
else if (iFormatVersion >= 40)
|
|
{
|
|
if (fscanf(infile, "%i,%i\n", &iPostProcess, &iDeletePaused) != 2) goto error;
|
|
}
|
|
else if (iFormatVersion >= 4)
|
|
{
|
|
if (fscanf(infile, "%i\n", &iPostProcess) != 1) goto error;
|
|
}
|
|
pNZBInfo->SetPriority(iPriority);
|
|
pNZBInfo->SetDeletePaused((bool)iDeletePaused);
|
|
pNZBInfo->SetManyDupeFiles((bool)iManyDupeFiles);
|
|
if (iPostStage > 0)
|
|
{
|
|
pNZBInfo->EnterPostProcess();
|
|
pNZBInfo->GetPostInfo()->SetStage((PostInfo::EStage)iPostStage);
|
|
}
|
|
pNZBInfo->SetFeedID(iFeedID);
|
|
}
|
|
|
|
if (iFormatVersion >= 8 && iFormatVersion < 18)
|
|
{
|
|
int iParStatus;
|
|
if (fscanf(infile, "%i\n", &iParStatus) != 1) goto error;
|
|
pNZBInfo->SetParStatus((NZBInfo::EParStatus)iParStatus);
|
|
}
|
|
|
|
if (iFormatVersion >= 9 && iFormatVersion < 18)
|
|
{
|
|
int iScriptStatus;
|
|
if (fscanf(infile, "%i\n", &iScriptStatus) != 1) goto error;
|
|
if (iScriptStatus > 1) iScriptStatus--;
|
|
pNZBInfo->GetScriptStatuses()->Add("SCRIPT", (ScriptStatus::EStatus)iScriptStatus);
|
|
}
|
|
|
|
if (iFormatVersion >= 18)
|
|
{
|
|
int iParStatus, iUnpackStatus, iScriptStatus, iMoveStatus = 0,
|
|
iRenameStatus = 0, iDeleteStatus = 0, iMarkStatus = 0, iUrlStatus = 0;
|
|
if (iFormatVersion >= 46)
|
|
{
|
|
if (fscanf(infile, "%i,%i,%i,%i,%i,%i,%i\n", &iParStatus, &iUnpackStatus, &iMoveStatus,
|
|
&iRenameStatus, &iDeleteStatus, &iMarkStatus, &iUrlStatus) != 7) goto error;
|
|
}
|
|
else if (iFormatVersion >= 37)
|
|
{
|
|
if (fscanf(infile, "%i,%i,%i,%i,%i,%i\n", &iParStatus, &iUnpackStatus,
|
|
&iMoveStatus, &iRenameStatus, &iDeleteStatus, &iMarkStatus) != 6) goto error;
|
|
}
|
|
else if (iFormatVersion >= 35)
|
|
{
|
|
if (fscanf(infile, "%i,%i,%i,%i,%i\n", &iParStatus, &iUnpackStatus,
|
|
&iMoveStatus, &iRenameStatus, &iDeleteStatus) != 5) goto error;
|
|
}
|
|
else if (iFormatVersion >= 23)
|
|
{
|
|
if (fscanf(infile, "%i,%i,%i,%i\n", &iParStatus, &iUnpackStatus,
|
|
&iMoveStatus, &iRenameStatus) != 4) goto error;
|
|
}
|
|
else if (iFormatVersion >= 21)
|
|
{
|
|
if (fscanf(infile, "%i,%i,%i,%i,%i\n", &iParStatus, &iUnpackStatus,
|
|
&iScriptStatus, &iMoveStatus, &iRenameStatus) != 5) goto error;
|
|
}
|
|
else if (iFormatVersion >= 20)
|
|
{
|
|
if (fscanf(infile, "%i,%i,%i,%i\n", &iParStatus, &iUnpackStatus,
|
|
&iScriptStatus, &iMoveStatus) != 4) goto error;
|
|
}
|
|
else
|
|
{
|
|
if (fscanf(infile, "%i,%i,%i\n", &iParStatus, &iUnpackStatus, &iScriptStatus) != 3) goto error;
|
|
}
|
|
pNZBInfo->SetParStatus((NZBInfo::EParStatus)iParStatus);
|
|
pNZBInfo->SetUnpackStatus((NZBInfo::EUnpackStatus)iUnpackStatus);
|
|
pNZBInfo->SetMoveStatus((NZBInfo::EMoveStatus)iMoveStatus);
|
|
pNZBInfo->SetRenameStatus((NZBInfo::ERenameStatus)iRenameStatus);
|
|
pNZBInfo->SetDeleteStatus((NZBInfo::EDeleteStatus)iDeleteStatus);
|
|
pNZBInfo->SetMarkStatus((NZBInfo::EMarkStatus)iMarkStatus);
|
|
if (pNZBInfo->GetKind() == NZBInfo::nkNzb ||
|
|
(NZBInfo::EUrlStatus)iUrlStatus >= NZBInfo::lsFailed ||
|
|
(NZBInfo::EUrlStatus)iUrlStatus >= NZBInfo::lsScanSkipped)
|
|
{
|
|
pNZBInfo->SetUrlStatus((NZBInfo::EUrlStatus)iUrlStatus);
|
|
}
|
|
if (iFormatVersion < 23)
|
|
{
|
|
if (iScriptStatus > 1) iScriptStatus--;
|
|
pNZBInfo->GetScriptStatuses()->Add("SCRIPT", (ScriptStatus::EStatus)iScriptStatus);
|
|
}
|
|
}
|
|
|
|
if (iFormatVersion >= 35)
|
|
{
|
|
int iUnpackCleanedUpDisk, iHealthPaused, iAddUrlPaused = 0;
|
|
if (iFormatVersion >= 46)
|
|
{
|
|
if (fscanf(infile, "%i,%i,%i\n", &iUnpackCleanedUpDisk, &iHealthPaused, &iAddUrlPaused) != 3) goto error;
|
|
}
|
|
else
|
|
{
|
|
if (fscanf(infile, "%i,%i\n", &iUnpackCleanedUpDisk, &iHealthPaused) != 2) goto error;
|
|
}
|
|
pNZBInfo->SetUnpackCleanedUpDisk((bool)iUnpackCleanedUpDisk);
|
|
pNZBInfo->SetHealthPaused((bool)iHealthPaused);
|
|
pNZBInfo->SetAddUrlPaused((bool)iAddUrlPaused);
|
|
}
|
|
else if (iFormatVersion >= 28)
|
|
{
|
|
int iDeleted, iUnpackCleanedUpDisk, iHealthPaused, iHealthDeleted;
|
|
if (fscanf(infile, "%i,%i,%i,%i\n", &iDeleted, &iUnpackCleanedUpDisk, &iHealthPaused, &iHealthDeleted) != 4) goto error;
|
|
pNZBInfo->SetUnpackCleanedUpDisk((bool)iUnpackCleanedUpDisk);
|
|
pNZBInfo->SetHealthPaused((bool)iHealthPaused);
|
|
pNZBInfo->SetDeleteStatus(iHealthDeleted ? NZBInfo::dsHealth : iDeleted ? NZBInfo::dsManual : NZBInfo::dsNone);
|
|
}
|
|
|
|
if (iFormatVersion >= 28)
|
|
{
|
|
int iFileCount, iParkedFileCount, iMessageCount = 0;
|
|
if (iFormatVersion >= 52)
|
|
{
|
|
if (fscanf(infile, "%i,%i,%i\n", &iFileCount, &iParkedFileCount, &iMessageCount) != 3) goto error;
|
|
}
|
|
else
|
|
{
|
|
if (fscanf(infile, "%i,%i\n", &iFileCount, &iParkedFileCount) != 2) goto error;
|
|
}
|
|
|
|
pNZBInfo->SetFileCount(iFileCount);
|
|
pNZBInfo->SetParkedFileCount(iParkedFileCount);
|
|
pNZBInfo->SetMessageCount(iMessageCount);
|
|
}
|
|
else
|
|
{
|
|
if (iFormatVersion >= 19)
|
|
{
|
|
int iUnpackCleanedUpDisk;
|
|
if (fscanf(infile, "%i\n", &iUnpackCleanedUpDisk) != 1) goto error;
|
|
pNZBInfo->SetUnpackCleanedUpDisk((bool)iUnpackCleanedUpDisk);
|
|
}
|
|
|
|
int iFileCount;
|
|
if (fscanf(infile, "%i\n", &iFileCount) != 1) goto error;
|
|
pNZBInfo->SetFileCount(iFileCount);
|
|
|
|
if (iFormatVersion >= 10)
|
|
{
|
|
if (fscanf(infile, "%i\n", &iFileCount) != 1) goto error;
|
|
pNZBInfo->SetParkedFileCount(iFileCount);
|
|
}
|
|
}
|
|
|
|
if (iFormatVersion >= 44)
|
|
{
|
|
int iMinTime, iMaxTime;
|
|
if (fscanf(infile, "%i,%i\n", &iMinTime, &iMaxTime) != 2) goto error;
|
|
pNZBInfo->SetMinTime((time_t)iMinTime);
|
|
pNZBInfo->SetMaxTime((time_t)iMaxTime);
|
|
}
|
|
|
|
if (iFormatVersion >= 51)
|
|
{
|
|
int iParFull, iForceParFull, iForceRepair;
|
|
if (fscanf(infile, "%i,%i,%i\n", &iParFull, &iForceParFull, &iForceRepair) != 3) goto error;
|
|
pNZBInfo->SetParFull((bool)iParFull);
|
|
if (pNZBInfo->GetPostInfo())
|
|
{
|
|
pNZBInfo->GetPostInfo()->SetForceParFull((bool)iForceParFull);
|
|
pNZBInfo->GetPostInfo()->SetForceRepair((bool)iForceRepair);
|
|
}
|
|
}
|
|
|
|
if (true) // clang requires a block for goto to work
|
|
{
|
|
unsigned int iFullContentHash = 0, iFilteredContentHash = 0;
|
|
if (iFormatVersion >= 34)
|
|
{
|
|
if (fscanf(infile, "%u,%u\n", &iFullContentHash, &iFilteredContentHash) != 2) goto error;
|
|
}
|
|
else if (iFormatVersion >= 32)
|
|
{
|
|
if (fscanf(infile, "%u\n", &iFullContentHash) != 1) goto error;
|
|
}
|
|
pNZBInfo->SetFullContentHash(iFullContentHash);
|
|
pNZBInfo->SetFilteredContentHash(iFilteredContentHash);
|
|
}
|
|
|
|
if (iFormatVersion >= 28)
|
|
{
|
|
unsigned long High1, Low1, High2, Low2, High3, Low3;
|
|
if (fscanf(infile, "%lu,%lu,%lu,%lu,%lu,%lu\n", &High1, &Low1, &High2, &Low2, &High3, &Low3) != 6) goto error;
|
|
pNZBInfo->SetSize(Util::JoinInt64(High1, Low1));
|
|
pNZBInfo->SetSuccessSize(Util::JoinInt64(High2, Low2));
|
|
pNZBInfo->SetFailedSize(Util::JoinInt64(High3, Low3));
|
|
pNZBInfo->SetCurrentSuccessSize(pNZBInfo->GetSuccessSize());
|
|
pNZBInfo->SetCurrentFailedSize(pNZBInfo->GetFailedSize());
|
|
|
|
if (fscanf(infile, "%lu,%lu,%lu,%lu,%lu,%lu\n", &High1, &Low1, &High2, &Low2, &High3, &Low3) != 6) goto error;
|
|
pNZBInfo->SetParSize(Util::JoinInt64(High1, Low1));
|
|
pNZBInfo->SetParSuccessSize(Util::JoinInt64(High2, Low2));
|
|
pNZBInfo->SetParFailedSize(Util::JoinInt64(High3, Low3));
|
|
pNZBInfo->SetParCurrentSuccessSize(pNZBInfo->GetParSuccessSize());
|
|
pNZBInfo->SetParCurrentFailedSize(pNZBInfo->GetParFailedSize());
|
|
}
|
|
else
|
|
{
|
|
unsigned long High, Low;
|
|
if (fscanf(infile, "%lu,%lu\n", &High, &Low) != 2) goto error;
|
|
pNZBInfo->SetSize(Util::JoinInt64(High, Low));
|
|
}
|
|
|
|
if (iFormatVersion >= 30)
|
|
{
|
|
int iTotalArticles, iSuccessArticles, iFailedArticles;
|
|
if (fscanf(infile, "%i,%i,%i\n", &iTotalArticles, &iSuccessArticles, &iFailedArticles) != 3) goto error;
|
|
pNZBInfo->SetTotalArticles(iTotalArticles);
|
|
pNZBInfo->SetSuccessArticles(iSuccessArticles);
|
|
pNZBInfo->SetFailedArticles(iFailedArticles);
|
|
pNZBInfo->SetCurrentSuccessArticles(iSuccessArticles);
|
|
pNZBInfo->SetCurrentFailedArticles(iFailedArticles);
|
|
}
|
|
|
|
if (iFormatVersion >= 31)
|
|
{
|
|
if (!fgets(buf, sizeof(buf), infile)) goto error;
|
|
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
|
|
if (iFormatVersion < 36) ConvertDupeKey(buf, sizeof(buf));
|
|
pNZBInfo->SetDupeKey(buf);
|
|
}
|
|
|
|
if (true) // clang requires a block for goto to work
|
|
{
|
|
int iDupeMode = 0, iDupeScore = 0;
|
|
if (iFormatVersion >= 39)
|
|
{
|
|
if (fscanf(infile, "%i,%i\n", &iDupeMode, &iDupeScore) != 2) goto error;
|
|
}
|
|
else if (iFormatVersion >= 31)
|
|
{
|
|
int iDupe;
|
|
if (fscanf(infile, "%i,%i,%i\n", &iDupe, &iDupeMode, &iDupeScore) != 3) goto error;
|
|
}
|
|
pNZBInfo->SetDupeMode((EDupeMode)iDupeMode);
|
|
pNZBInfo->SetDupeScore(iDupeScore);
|
|
}
|
|
|
|
if (iFormatVersion >= 48)
|
|
{
|
|
unsigned long High1, Low1, iDownloadSec, iPostTotalSec, iParSec, iRepairSec, iUnpackSec;
|
|
if (fscanf(infile, "%lu,%lu,%i,%i,%i,%i,%i\n", &High1, &Low1, &iDownloadSec, &iPostTotalSec, &iParSec, &iRepairSec, &iUnpackSec) != 7) goto error;
|
|
pNZBInfo->SetDownloadedSize(Util::JoinInt64(High1, Low1));
|
|
pNZBInfo->SetDownloadSec(iDownloadSec);
|
|
pNZBInfo->SetPostTotalSec(iPostTotalSec);
|
|
pNZBInfo->SetParSec(iParSec);
|
|
pNZBInfo->SetRepairSec(iRepairSec);
|
|
pNZBInfo->SetUnpackSec(iUnpackSec);
|
|
}
|
|
|
|
if (iFormatVersion >= 4)
|
|
{
|
|
int iFileCount;
|
|
if (fscanf(infile, "%i\n", &iFileCount) != 1) goto error;
|
|
for (int i = 0; i < iFileCount; i++)
|
|
{
|
|
if (!fgets(buf, sizeof(buf), infile)) goto error;
|
|
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
|
|
|
|
int iID = 0;
|
|
char* szFileName = buf;
|
|
int iStatus = 0;
|
|
unsigned long lCrc = 0;
|
|
|
|
if (iFormatVersion >= 49)
|
|
{
|
|
if (iFormatVersion >= 50)
|
|
{
|
|
if (sscanf(buf, "%i,%i,%lu", &iID, &iStatus, &lCrc) != 3) goto error;
|
|
szFileName = strchr(buf, ',');
|
|
if (szFileName) szFileName = strchr(szFileName+1, ',');
|
|
if (szFileName) szFileName = strchr(szFileName+1, ',');
|
|
}
|
|
else
|
|
{
|
|
if (sscanf(buf, "%i,%lu", &iStatus, &lCrc) != 2) goto error;
|
|
szFileName = strchr(buf + 2, ',');
|
|
}
|
|
if (szFileName)
|
|
{
|
|
szFileName++;
|
|
}
|
|
}
|
|
|
|
pNZBInfo->GetCompletedFiles()->push_back(new CompletedFile(iID, szFileName, (CompletedFile::EStatus)iStatus, lCrc));
|
|
}
|
|
}
|
|
|
|
if (iFormatVersion >= 6)
|
|
{
|
|
int iParameterCount;
|
|
if (fscanf(infile, "%i\n", &iParameterCount) != 1) goto error;
|
|
for (int i = 0; i < iParameterCount; i++)
|
|
{
|
|
if (!fgets(buf, sizeof(buf), infile)) goto error;
|
|
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
|
|
|
|
char* szValue = strchr(buf, '=');
|
|
if (szValue)
|
|
{
|
|
*szValue = '\0';
|
|
szValue++;
|
|
pNZBInfo->GetParameters()->SetParameter(buf, szValue);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (iFormatVersion >= 23)
|
|
{
|
|
int iScriptCount;
|
|
if (fscanf(infile, "%i\n", &iScriptCount) != 1) goto error;
|
|
for (int i = 0; i < iScriptCount; i++)
|
|
{
|
|
if (!fgets(buf, sizeof(buf), infile)) goto error;
|
|
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
|
|
|
|
char* szScriptName = strchr(buf, ',');
|
|
if (szScriptName)
|
|
{
|
|
szScriptName++;
|
|
int iStatus = atoi(buf);
|
|
if (iStatus > 1 && iFormatVersion < 25) iStatus--;
|
|
pNZBInfo->GetScriptStatuses()->Add(szScriptName, (ScriptStatus::EStatus)iStatus);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (iFormatVersion >= 30)
|
|
{
|
|
if (!LoadServerStats(pNZBInfo->GetServerStats(), pServers, infile)) goto error;
|
|
pNZBInfo->GetCurrentServerStats()->ListOp(pNZBInfo->GetServerStats(), ServerStatList::soSet);
|
|
}
|
|
|
|
if (iFormatVersion >= 11 && iFormatVersion < 52)
|
|
{
|
|
int iLogCount;
|
|
if (fscanf(infile, "%i\n", &iLogCount) != 1) goto error;
|
|
for (int i = 0; i < iLogCount; i++)
|
|
{
|
|
if (!fgets(buf, sizeof(buf), infile)) goto error;
|
|
}
|
|
}
|
|
|
|
if (iFormatVersion < 26)
|
|
{
|
|
NZBParameter* pUnpackParameter = pNZBInfo->GetParameters()->Find("*Unpack:", false);
|
|
if (!pUnpackParameter)
|
|
{
|
|
pNZBInfo->GetParameters()->SetParameter("*Unpack:", g_pOptions->GetUnpack() ? "yes" : "no");
|
|
}
|
|
}
|
|
|
|
if (iFormatVersion >= 43)
|
|
{
|
|
int iFileCount;
|
|
if (fscanf(infile, "%i\n", &iFileCount) != 1) goto error;
|
|
for (int i = 0; i < iFileCount; i++)
|
|
{
|
|
unsigned int id, paused, iTime = 0;
|
|
int iPriority = 0, iExtraPriority = 0;
|
|
|
|
if (iFormatVersion >= 44)
|
|
{
|
|
if (fscanf(infile, "%i,%i,%i,%i\n", &id, &paused, &iTime, &iExtraPriority) != 4) goto error;
|
|
}
|
|
else
|
|
{
|
|
if (fscanf(infile, "%i,%i,%i,%i,%i\n", &id, &paused, &iTime, &iPriority, &iExtraPriority) != 5) goto error;
|
|
pNZBInfo->SetPriority(iPriority);
|
|
}
|
|
|
|
char fileName[1024];
|
|
snprintf(fileName, 1024, "%s%i", g_pOptions->GetQueueDir(), id);
|
|
fileName[1024-1] = '\0';
|
|
FileInfo* pFileInfo = new FileInfo();
|
|
bool res = LoadFileInfo(pFileInfo, fileName, true, false);
|
|
if (res)
|
|
{
|
|
pFileInfo->SetID(id);
|
|
pFileInfo->SetPaused(paused);
|
|
pFileInfo->SetTime(iTime);
|
|
pFileInfo->SetExtraPriority(iExtraPriority != 0);
|
|
pFileInfo->SetNZBInfo(pNZBInfo);
|
|
if (iFormatVersion < 30)
|
|
{
|
|
pNZBInfo->SetTotalArticles(pNZBInfo->GetTotalArticles() + pFileInfo->GetTotalArticles());
|
|
}
|
|
pNZBInfo->GetFileList()->push_back(pFileInfo);
|
|
}
|
|
else
|
|
{
|
|
delete pFileInfo;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
|
|
error:
|
|
error("Error reading nzb info from disk");
|
|
return false;
|
|
}
|
|
|
|
bool DiskState::LoadFileQueue12(NZBList* pNZBList, NZBList* pSortList, FILE* infile, int iFormatVersion)
|
|
{
|
|
debug("Loading file queue from disk");
|
|
|
|
int size;
|
|
if (fscanf(infile, "%i\n", &size) != 1) goto error;
|
|
for (int i = 0; i < size; i++)
|
|
{
|
|
unsigned int id, iNZBIndex, paused;
|
|
unsigned int iTime = 0;
|
|
int iPriority = 0, iExtraPriority = 0;
|
|
if (iFormatVersion >= 17)
|
|
{
|
|
if (fscanf(infile, "%i,%i,%i,%i,%i,%i\n", &id, &iNZBIndex, &paused, &iTime, &iPriority, &iExtraPriority) != 6) goto error;
|
|
}
|
|
else if (iFormatVersion >= 14)
|
|
{
|
|
if (fscanf(infile, "%i,%i,%i,%i,%i\n", &id, &iNZBIndex, &paused, &iTime, &iPriority) != 5) goto error;
|
|
}
|
|
else if (iFormatVersion >= 12)
|
|
{
|
|
if (fscanf(infile, "%i,%i,%i,%i\n", &id, &iNZBIndex, &paused, &iTime) != 4) goto error;
|
|
}
|
|
else
|
|
{
|
|
if (fscanf(infile, "%i,%i,%i\n", &id, &iNZBIndex, &paused) != 3) goto error;
|
|
}
|
|
if (iNZBIndex > pNZBList->size()) goto error;
|
|
|
|
char fileName[1024];
|
|
snprintf(fileName, 1024, "%s%i", g_pOptions->GetQueueDir(), id);
|
|
fileName[1024-1] = '\0';
|
|
FileInfo* pFileInfo = new FileInfo();
|
|
bool res = LoadFileInfo(pFileInfo, fileName, true, false);
|
|
if (res)
|
|
{
|
|
NZBInfo* pNZBInfo = pNZBList->at(iNZBIndex - 1);
|
|
pNZBInfo->SetPriority(iPriority);
|
|
pFileInfo->SetID(id);
|
|
pFileInfo->SetPaused(paused);
|
|
pFileInfo->SetTime(iTime);
|
|
pFileInfo->SetExtraPriority(iExtraPriority != 0);
|
|
pFileInfo->SetNZBInfo(pNZBInfo);
|
|
if (iFormatVersion < 30)
|
|
{
|
|
pNZBInfo->SetTotalArticles(pNZBInfo->GetTotalArticles() + pFileInfo->GetTotalArticles());
|
|
}
|
|
pNZBInfo->GetFileList()->push_back(pFileInfo);
|
|
|
|
if (pSortList && std::find(pSortList->begin(), pSortList->end(), pNZBInfo) == pSortList->end())
|
|
{
|
|
pSortList->push_back(pNZBInfo);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
delete pFileInfo;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
|
|
error:
|
|
error("Error reading file queue from disk");
|
|
return false;
|
|
}
|
|
|
|
void DiskState::SaveServerStats(ServerStatList* pServerStatList, FILE* outfile)
|
|
{
|
|
fprintf(outfile, "%i\n", (int)pServerStatList->size());
|
|
for (ServerStatList::iterator it = pServerStatList->begin(); it != pServerStatList->end(); it++)
|
|
{
|
|
ServerStat* pServerStat = *it;
|
|
fprintf(outfile, "%i,%i,%i\n", pServerStat->GetServerID(), pServerStat->GetSuccessArticles(), pServerStat->GetFailedArticles());
|
|
}
|
|
}
|
|
|
|
bool DiskState::LoadServerStats(ServerStatList* pServerStatList, Servers* pServers, FILE* infile)
|
|
{
|
|
int iStatCount;
|
|
if (fscanf(infile, "%i\n", &iStatCount) != 1) goto error;
|
|
for (int i = 0; i < iStatCount; i++)
|
|
{
|
|
int iServerID, iSuccessArticles, iFailedArticles;
|
|
if (fscanf(infile, "%i,%i,%i\n", &iServerID, &iSuccessArticles, &iFailedArticles) != 3) goto error;
|
|
|
|
if (pServers)
|
|
{
|
|
// find server (id could change if config file was edited)
|
|
for (Servers::iterator it = pServers->begin(); it != pServers->end(); it++)
|
|
{
|
|
NewsServer* pNewsServer = *it;
|
|
if (pNewsServer->GetStateID() == iServerID)
|
|
{
|
|
pServerStatList->StatOp(pNewsServer->GetID(), iSuccessArticles, iFailedArticles, ServerStatList::soSet);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
|
|
error:
|
|
error("Error reading server stats from disk");
|
|
return false;
|
|
}
|
|
|
|
bool DiskState::SaveFile(FileInfo* pFileInfo)
|
|
{
|
|
char fileName[1024];
|
|
snprintf(fileName, 1024, "%s%i", g_pOptions->GetQueueDir(), pFileInfo->GetID());
|
|
fileName[1024-1] = '\0';
|
|
return SaveFileInfo(pFileInfo, fileName);
|
|
}
|
|
|
|
bool DiskState::SaveFileInfo(FileInfo* pFileInfo, const char* szFilename)
|
|
{
|
|
debug("Saving FileInfo to disk");
|
|
|
|
FILE* outfile = fopen(szFilename, FOPEN_WB);
|
|
|
|
if (!outfile)
|
|
{
|
|
error("Error saving diskstate: could not create file %s", szFilename);
|
|
return false;
|
|
}
|
|
|
|
fprintf(outfile, "%s%i\n", FORMATVERSION_SIGNATURE, 3);
|
|
|
|
fprintf(outfile, "%s\n", pFileInfo->GetSubject());
|
|
fprintf(outfile, "%s\n", pFileInfo->GetFilename());
|
|
|
|
unsigned long High, Low;
|
|
Util::SplitInt64(pFileInfo->GetSize(), &High, &Low);
|
|
fprintf(outfile, "%lu,%lu\n", High, Low);
|
|
|
|
Util::SplitInt64(pFileInfo->GetMissedSize(), &High, &Low);
|
|
fprintf(outfile, "%lu,%lu\n", High, Low);
|
|
|
|
fprintf(outfile, "%i\n", (int)pFileInfo->GetParFile());
|
|
fprintf(outfile, "%i,%i\n", pFileInfo->GetTotalArticles(), pFileInfo->GetMissedArticles());
|
|
|
|
fprintf(outfile, "%i\n", (int)pFileInfo->GetGroups()->size());
|
|
for (FileInfo::Groups::iterator it = pFileInfo->GetGroups()->begin(); it != pFileInfo->GetGroups()->end(); it++)
|
|
{
|
|
fprintf(outfile, "%s\n", *it);
|
|
}
|
|
|
|
fprintf(outfile, "%i\n", (int)pFileInfo->GetArticles()->size());
|
|
for (FileInfo::Articles::iterator it = pFileInfo->GetArticles()->begin(); it != pFileInfo->GetArticles()->end(); it++)
|
|
{
|
|
ArticleInfo* pArticleInfo = *it;
|
|
fprintf(outfile, "%i,%i\n", pArticleInfo->GetPartNumber(), pArticleInfo->GetSize());
|
|
fprintf(outfile, "%s\n", pArticleInfo->GetMessageID());
|
|
}
|
|
|
|
fclose(outfile);
|
|
return true;
|
|
}
|
|
|
|
bool DiskState::LoadArticles(FileInfo* pFileInfo)
|
|
{
|
|
char fileName[1024];
|
|
snprintf(fileName, 1024, "%s%i", g_pOptions->GetQueueDir(), pFileInfo->GetID());
|
|
fileName[1024-1] = '\0';
|
|
return LoadFileInfo(pFileInfo, fileName, false, true);
|
|
}
|
|
|
|
bool DiskState::LoadFileInfo(FileInfo* pFileInfo, const char * szFilename, bool bFileSummary, bool bArticles)
|
|
{
|
|
debug("Loading FileInfo from disk");
|
|
|
|
FILE* infile = fopen(szFilename, FOPEN_RB);
|
|
|
|
if (!infile)
|
|
{
|
|
error("Error reading diskstate: could not open file %s", szFilename);
|
|
return false;
|
|
}
|
|
|
|
char buf[1024];
|
|
int iFormatVersion = 0;
|
|
|
|
if (fgets(buf, sizeof(buf), infile))
|
|
{
|
|
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
|
|
iFormatVersion = ParseFormatVersion(buf);
|
|
if (iFormatVersion > 3)
|
|
{
|
|
error("Could not load diskstate due to file version mismatch");
|
|
goto error;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
if (iFormatVersion >= 2)
|
|
{
|
|
if (!fgets(buf, sizeof(buf), infile)) goto error;
|
|
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
|
|
}
|
|
|
|
if (bFileSummary) pFileInfo->SetSubject(buf);
|
|
|
|
if (!fgets(buf, sizeof(buf), infile)) goto error;
|
|
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
|
|
if (bFileSummary) pFileInfo->SetFilename(buf);
|
|
|
|
if (iFormatVersion < 2)
|
|
{
|
|
int iFilenameConfirmed;
|
|
if (fscanf(infile, "%i\n", &iFilenameConfirmed) != 1) goto error;
|
|
if (bFileSummary) pFileInfo->SetFilenameConfirmed(iFilenameConfirmed);
|
|
}
|
|
|
|
unsigned long High, Low;
|
|
if (fscanf(infile, "%lu,%lu\n", &High, &Low) != 2) goto error;
|
|
if (bFileSummary) pFileInfo->SetSize(Util::JoinInt64(High, Low));
|
|
if (bFileSummary) pFileInfo->SetRemainingSize(pFileInfo->GetSize());
|
|
|
|
if (iFormatVersion >= 2)
|
|
{
|
|
if (fscanf(infile, "%lu,%lu\n", &High, &Low) != 2) goto error;
|
|
if (bFileSummary) pFileInfo->SetMissedSize(Util::JoinInt64(High, Low));
|
|
if (bFileSummary) pFileInfo->SetRemainingSize(pFileInfo->GetSize() - pFileInfo->GetMissedSize());
|
|
|
|
int iParFile;
|
|
if (fscanf(infile, "%i\n", &iParFile) != 1) goto error;
|
|
if (bFileSummary) pFileInfo->SetParFile((bool)iParFile);
|
|
}
|
|
|
|
if (iFormatVersion >= 3)
|
|
{
|
|
int iTotalArticles, iMissedArticles;
|
|
if (fscanf(infile, "%i,%i\n", &iTotalArticles, &iMissedArticles) != 2) goto error;
|
|
if (bFileSummary) pFileInfo->SetTotalArticles(iTotalArticles);
|
|
if (bFileSummary) pFileInfo->SetMissedArticles(iMissedArticles);
|
|
}
|
|
|
|
int size;
|
|
if (fscanf(infile, "%i\n", &size) != 1) goto error;
|
|
for (int i = 0; i < size; i++)
|
|
{
|
|
if (!fgets(buf, sizeof(buf), infile)) goto error;
|
|
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
|
|
if (bFileSummary) pFileInfo->GetGroups()->push_back(strdup(buf));
|
|
}
|
|
|
|
if (fscanf(infile, "%i\n", &size) != 1) goto error;
|
|
|
|
if (iFormatVersion < 3 && bFileSummary)
|
|
{
|
|
pFileInfo->SetTotalArticles(size);
|
|
}
|
|
|
|
if (bArticles)
|
|
{
|
|
for (int i = 0; i < size; i++)
|
|
{
|
|
int PartNumber, PartSize;
|
|
if (fscanf(infile, "%i,%i\n", &PartNumber, &PartSize) != 2) goto error;
|
|
|
|
if (!fgets(buf, sizeof(buf), infile)) goto error;
|
|
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
|
|
|
|
ArticleInfo* pArticleInfo = new ArticleInfo();
|
|
pArticleInfo->SetPartNumber(PartNumber);
|
|
pArticleInfo->SetSize(PartSize);
|
|
pArticleInfo->SetMessageID(buf);
|
|
pFileInfo->GetArticles()->push_back(pArticleInfo);
|
|
}
|
|
}
|
|
|
|
fclose(infile);
|
|
return true;
|
|
|
|
error:
|
|
fclose(infile);
|
|
error("Error reading diskstate for file %s", szFilename);
|
|
return false;
|
|
}
|
|
|
|
bool DiskState::SaveFileState(FileInfo* pFileInfo, bool bCompleted)
|
|
{
|
|
debug("Saving FileState to disk");
|
|
|
|
char szFilename[1024];
|
|
snprintf(szFilename, 1024, "%s%i%s", g_pOptions->GetQueueDir(), pFileInfo->GetID(), bCompleted ? "c" : "s");
|
|
szFilename[1024-1] = '\0';
|
|
|
|
FILE* outfile = fopen(szFilename, FOPEN_WB);
|
|
|
|
if (!outfile)
|
|
{
|
|
error("Error saving diskstate: could not create file %s", szFilename);
|
|
return false;
|
|
}
|
|
|
|
fprintf(outfile, "%s%i\n", FORMATVERSION_SIGNATURE, 2);
|
|
|
|
fprintf(outfile, "%i,%i\n", pFileInfo->GetSuccessArticles(), pFileInfo->GetFailedArticles());
|
|
|
|
unsigned long High1, Low1, High2, Low2, High3, Low3;
|
|
Util::SplitInt64(pFileInfo->GetRemainingSize(), &High1, &Low1);
|
|
Util::SplitInt64(pFileInfo->GetSuccessSize(), &High2, &Low2);
|
|
Util::SplitInt64(pFileInfo->GetFailedSize(), &High3, &Low3);
|
|
fprintf(outfile, "%lu,%lu,%lu,%lu,%lu,%lu\n", High1, Low1, High2, Low2, High3, Low3);
|
|
|
|
SaveServerStats(pFileInfo->GetServerStats(), outfile);
|
|
|
|
fprintf(outfile, "%i\n", (int)pFileInfo->GetArticles()->size());
|
|
for (FileInfo::Articles::iterator it = pFileInfo->GetArticles()->begin(); it != pFileInfo->GetArticles()->end(); it++)
|
|
{
|
|
ArticleInfo* pArticleInfo = *it;
|
|
fprintf(outfile, "%i,%lu,%i,%lu\n", (int)pArticleInfo->GetStatus(), (unsigned long)pArticleInfo->GetSegmentOffset(),
|
|
pArticleInfo->GetSegmentSize(), (unsigned long)pArticleInfo->GetCrc());
|
|
}
|
|
|
|
fclose(outfile);
|
|
return true;
|
|
}
|
|
|
|
bool DiskState::LoadFileState(FileInfo* pFileInfo, Servers* pServers, bool bCompleted)
|
|
{
|
|
char szFilename[1024];
|
|
snprintf(szFilename, 1024, "%s%i%s", g_pOptions->GetQueueDir(), pFileInfo->GetID(), bCompleted ? "c" : "s");
|
|
szFilename[1024-1] = '\0';
|
|
|
|
bool bHasArticles = !pFileInfo->GetArticles()->empty();
|
|
|
|
FILE* infile = fopen(szFilename, FOPEN_RB);
|
|
|
|
if (!infile)
|
|
{
|
|
error("Error reading diskstate: could not open file %s", szFilename);
|
|
return false;
|
|
}
|
|
|
|
char buf[1024];
|
|
int iFormatVersion = 0;
|
|
|
|
if (fgets(buf, sizeof(buf), infile))
|
|
{
|
|
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
|
|
iFormatVersion = ParseFormatVersion(buf);
|
|
if (iFormatVersion > 2)
|
|
{
|
|
error("Could not load diskstate due to file version mismatch");
|
|
goto error;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
int iSuccessArticles, iFailedArticles;
|
|
if (fscanf(infile, "%i,%i\n", &iSuccessArticles, &iFailedArticles) != 2) goto error;
|
|
pFileInfo->SetSuccessArticles(iSuccessArticles);
|
|
pFileInfo->SetFailedArticles(iFailedArticles);
|
|
|
|
unsigned long High1, Low1, High2, Low2, High3, Low3;
|
|
if (fscanf(infile, "%lu,%lu,%lu,%lu,%lu,%lu\n", &High1, &Low1, &High2, &Low2, &High3, &Low3) != 6) goto error;
|
|
pFileInfo->SetRemainingSize(Util::JoinInt64(High1, Low1));
|
|
pFileInfo->SetSuccessSize(Util::JoinInt64(High2, Low3));
|
|
pFileInfo->SetFailedSize(Util::JoinInt64(High3, Low3));
|
|
|
|
if (!LoadServerStats(pFileInfo->GetServerStats(), pServers, infile)) goto error;
|
|
|
|
int iCompletedArticles;
|
|
iCompletedArticles = 0; //clang requires initialization in a separate line (due to goto statements)
|
|
|
|
int size;
|
|
if (fscanf(infile, "%i\n", &size) != 1) goto error;
|
|
for (int i = 0; i < size; i++)
|
|
{
|
|
if (!bHasArticles)
|
|
{
|
|
pFileInfo->GetArticles()->push_back(new ArticleInfo());
|
|
}
|
|
ArticleInfo* pa = pFileInfo->GetArticles()->at(i);
|
|
|
|
int iStatus;
|
|
|
|
if (iFormatVersion >= 2)
|
|
{
|
|
unsigned long iSegmentOffset, iCrc;
|
|
int iSegmentSize;
|
|
if (fscanf(infile, "%i,%lu,%i,%lu\n", &iStatus, &iSegmentOffset, &iSegmentSize, &iCrc) != 4) goto error;
|
|
pa->SetSegmentOffset(iSegmentOffset);
|
|
pa->SetSegmentSize(iSegmentSize);
|
|
pa->SetCrc(iCrc);
|
|
}
|
|
else
|
|
{
|
|
if (fscanf(infile, "%i\n", &iStatus) != 1) goto error;
|
|
}
|
|
|
|
ArticleInfo::EStatus eStatus = (ArticleInfo::EStatus)iStatus;
|
|
|
|
if (eStatus == ArticleInfo::aiRunning)
|
|
{
|
|
eStatus = ArticleInfo::aiUndefined;
|
|
}
|
|
|
|
// don't allow all articles be completed or the file will stuck.
|
|
// such states should never be saved on disk but just in case.
|
|
if (iCompletedArticles == size - 1 && !bCompleted)
|
|
{
|
|
eStatus = ArticleInfo::aiUndefined;
|
|
}
|
|
if (eStatus != ArticleInfo::aiUndefined)
|
|
{
|
|
iCompletedArticles++;
|
|
}
|
|
|
|
pa->SetStatus(eStatus);
|
|
}
|
|
|
|
pFileInfo->SetCompletedArticles(iCompletedArticles);
|
|
|
|
fclose(infile);
|
|
return true;
|
|
|
|
error:
|
|
fclose(infile);
|
|
error("Error reading diskstate for file %s", szFilename);
|
|
return false;
|
|
}
|
|
|
|
void DiskState::DiscardFiles(NZBInfo* pNZBInfo)
|
|
{
|
|
for (FileList::iterator it = pNZBInfo->GetFileList()->begin(); it != pNZBInfo->GetFileList()->end(); it++)
|
|
{
|
|
FileInfo* pFileInfo = *it;
|
|
DiscardFile(pFileInfo, true, true, true);
|
|
}
|
|
|
|
char szFilename[1024];
|
|
|
|
for (CompletedFiles::iterator it = pNZBInfo->GetCompletedFiles()->begin(); it != pNZBInfo->GetCompletedFiles()->end(); it++)
|
|
{
|
|
CompletedFile* pCompletedFile = *it;
|
|
if (pCompletedFile->GetStatus() != CompletedFile::cfSuccess && pCompletedFile->GetID() > 0)
|
|
{
|
|
snprintf(szFilename, 1024, "%s%i", g_pOptions->GetQueueDir(), pCompletedFile->GetID());
|
|
szFilename[1024-1] = '\0';
|
|
remove(szFilename);
|
|
|
|
snprintf(szFilename, 1024, "%s%is", g_pOptions->GetQueueDir(), pCompletedFile->GetID());
|
|
szFilename[1024-1] = '\0';
|
|
remove(szFilename);
|
|
|
|
snprintf(szFilename, 1024, "%s%ic", g_pOptions->GetQueueDir(), pCompletedFile->GetID());
|
|
szFilename[1024-1] = '\0';
|
|
remove(szFilename);
|
|
}
|
|
}
|
|
|
|
snprintf(szFilename, 1024, "%sn%i.log", g_pOptions->GetQueueDir(), pNZBInfo->GetID());
|
|
szFilename[1024-1] = '\0';
|
|
remove(szFilename);
|
|
}
|
|
|
|
bool DiskState::LoadPostQueue12(DownloadQueue* pDownloadQueue, NZBList* pNZBList, FILE* infile, int iFormatVersion)
|
|
{
|
|
debug("Loading post-queue from disk");
|
|
|
|
int size;
|
|
char buf[10240];
|
|
|
|
// load post-infos
|
|
if (fscanf(infile, "%i\n", &size) != 1) goto error;
|
|
for (int i = 0; i < size; i++)
|
|
{
|
|
PostInfo* pPostInfo = NULL;
|
|
int iNZBID = 0;
|
|
unsigned int iNZBIndex = 0, iStage, iDummy;
|
|
if (iFormatVersion < 19)
|
|
{
|
|
if (fscanf(infile, "%i,%i,%i,%i\n", &iNZBIndex, &iDummy, &iDummy, &iStage) != 4) goto error;
|
|
}
|
|
else if (iFormatVersion < 22)
|
|
{
|
|
if (fscanf(infile, "%i,%i,%i,%i\n", &iNZBIndex, &iDummy, &iDummy, &iStage) != 4) goto error;
|
|
}
|
|
else if (iFormatVersion < 43)
|
|
{
|
|
if (fscanf(infile, "%i,%i\n", &iNZBIndex, &iStage) != 2) goto error;
|
|
}
|
|
else
|
|
{
|
|
if (fscanf(infile, "%i,%i\n", &iNZBID, &iStage) != 2) goto error;
|
|
}
|
|
if (iFormatVersion < 18 && iStage > (int)PostInfo::ptVerifyingRepaired) iStage++;
|
|
if (iFormatVersion < 21 && iStage > (int)PostInfo::ptVerifyingRepaired) iStage++;
|
|
if (iFormatVersion < 20 && iStage > (int)PostInfo::ptUnpacking) iStage++;
|
|
|
|
NZBInfo* pNZBInfo = NULL;
|
|
|
|
if (iFormatVersion < 43)
|
|
{
|
|
pNZBInfo = pNZBList->at(iNZBIndex - 1);
|
|
if (!pNZBInfo) goto error;
|
|
}
|
|
else
|
|
{
|
|
pNZBInfo = FindNZBInfo(pDownloadQueue, iNZBID);
|
|
if (!pNZBInfo) goto error;
|
|
}
|
|
|
|
pNZBInfo->EnterPostProcess();
|
|
pPostInfo = pNZBInfo->GetPostInfo();
|
|
|
|
pPostInfo->SetStage((PostInfo::EStage)iStage);
|
|
|
|
// InfoName, ignore
|
|
if (!fgets(buf, sizeof(buf), infile)) goto error;
|
|
|
|
if (iFormatVersion < 22)
|
|
{
|
|
// ParFilename, ignore
|
|
if (!fgets(buf, sizeof(buf), infile)) goto error;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
|
|
error:
|
|
error("Error reading diskstate for post-processor queue");
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
* Loads post-queue created with older versions of nzbget.
|
|
* Returns true if successful, false if not
|
|
*/
|
|
bool DiskState::LoadPostQueue5(DownloadQueue* pDownloadQueue, NZBList* pNZBList)
|
|
{
|
|
debug("Loading post-queue from disk");
|
|
|
|
char fileName[1024];
|
|
snprintf(fileName, 1024, "%s%s", g_pOptions->GetQueueDir(), "postq");
|
|
fileName[1024-1] = '\0';
|
|
|
|
if (!Util::FileExists(fileName))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
FILE* infile = fopen(fileName, FOPEN_RB);
|
|
|
|
if (!infile)
|
|
{
|
|
error("Error reading diskstate: could not open file %s", fileName);
|
|
return false;
|
|
}
|
|
|
|
char FileSignatur[128];
|
|
fgets(FileSignatur, sizeof(FileSignatur), infile);
|
|
int iFormatVersion = ParseFormatVersion(FileSignatur);
|
|
if (iFormatVersion < 3 || iFormatVersion > 7)
|
|
{
|
|
error("Could not load diskstate due to file version mismatch");
|
|
fclose(infile);
|
|
return false;
|
|
}
|
|
|
|
int size;
|
|
char buf[10240];
|
|
int iIntValue;
|
|
|
|
// load file-infos
|
|
if (fscanf(infile, "%i\n", &size) != 1) goto error;
|
|
for (int i = 0; i < size; i++)
|
|
{
|
|
if (!fgets(buf, sizeof(buf), infile)) goto error;
|
|
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
|
|
|
|
// find NZBInfo based on NZBFilename
|
|
NZBInfo* pNZBInfo = NULL;
|
|
for (NZBList::iterator it = pNZBList->begin(); it != pNZBList->end(); it++)
|
|
{
|
|
NZBInfo* pNZBInfo2 = *it;
|
|
if (!strcmp(pNZBInfo2->GetFilename(), buf))
|
|
{
|
|
pNZBInfo = pNZBInfo2;
|
|
break;
|
|
}
|
|
}
|
|
|
|
bool bNewNZBInfo = !pNZBInfo;
|
|
if (bNewNZBInfo)
|
|
{
|
|
pNZBInfo = new NZBInfo();
|
|
pNZBList->push_front(pNZBInfo);
|
|
pNZBInfo->SetFilename(buf);
|
|
}
|
|
|
|
pNZBInfo->EnterPostProcess();
|
|
PostInfo* pPostInfo = pNZBInfo->GetPostInfo();
|
|
|
|
if (!fgets(buf, sizeof(buf), infile)) goto error;
|
|
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
|
|
if (bNewNZBInfo)
|
|
{
|
|
pNZBInfo->SetDestDir(buf);
|
|
}
|
|
|
|
// ParFilename, ignore
|
|
if (!fgets(buf, sizeof(buf), infile)) goto error;
|
|
|
|
// InfoName, ignore
|
|
if (!fgets(buf, sizeof(buf), infile)) goto error;
|
|
|
|
if (iFormatVersion >= 4)
|
|
{
|
|
if (!fgets(buf, sizeof(buf), infile)) goto error;
|
|
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
|
|
if (bNewNZBInfo)
|
|
{
|
|
pNZBInfo->SetCategory(buf);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (bNewNZBInfo)
|
|
{
|
|
pNZBInfo->SetCategory("");
|
|
}
|
|
}
|
|
|
|
if (iFormatVersion >= 5)
|
|
{
|
|
if (!fgets(buf, sizeof(buf), infile)) goto error;
|
|
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
|
|
if (bNewNZBInfo)
|
|
{
|
|
pNZBInfo->SetQueuedFilename(buf);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (bNewNZBInfo)
|
|
{
|
|
pNZBInfo->SetQueuedFilename("");
|
|
}
|
|
}
|
|
|
|
int iParCheck;
|
|
if (fscanf(infile, "%i\n", &iParCheck) != 1) goto error; // ParCheck
|
|
|
|
if (fscanf(infile, "%i\n", &iIntValue) != 1) goto error;
|
|
pNZBInfo->SetParStatus(iParCheck ? (NZBInfo::EParStatus)iIntValue : NZBInfo::psSkipped);
|
|
|
|
if (iFormatVersion < 7)
|
|
{
|
|
// skip old field ParFailed, not used anymore
|
|
if (fscanf(infile, "%i\n", &iIntValue) != 1) goto error;
|
|
}
|
|
|
|
if (fscanf(infile, "%i\n", &iIntValue) != 1) goto error;
|
|
pPostInfo->SetStage((PostInfo::EStage)iIntValue);
|
|
|
|
if (iFormatVersion >= 6)
|
|
{
|
|
int iParameterCount;
|
|
if (fscanf(infile, "%i\n", &iParameterCount) != 1) goto error;
|
|
for (int i = 0; i < iParameterCount; i++)
|
|
{
|
|
if (!fgets(buf, sizeof(buf), infile)) goto error;
|
|
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
|
|
|
|
char* szValue = strchr(buf, '=');
|
|
if (szValue)
|
|
{
|
|
*szValue = '\0';
|
|
szValue++;
|
|
if (bNewNZBInfo)
|
|
{
|
|
pNZBInfo->GetParameters()->SetParameter(buf, szValue);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fclose(infile);
|
|
return true;
|
|
|
|
error:
|
|
fclose(infile);
|
|
error("Error reading diskstate for file %s", fileName);
|
|
return false;
|
|
}
|
|
|
|
bool DiskState::LoadUrlQueue12(DownloadQueue* pDownloadQueue, FILE* infile, int iFormatVersion)
|
|
{
|
|
debug("Loading url-queue from disk");
|
|
int size;
|
|
|
|
// load url-infos
|
|
if (fscanf(infile, "%i\n", &size) != 1) goto error;
|
|
|
|
for (int i = 0; i < size; i++)
|
|
{
|
|
NZBInfo* pNZBInfo = new NZBInfo();
|
|
if (!LoadUrlInfo12(pNZBInfo, infile, iFormatVersion)) goto error;
|
|
pDownloadQueue->GetQueue()->push_back(pNZBInfo);
|
|
}
|
|
|
|
return true;
|
|
|
|
error:
|
|
error("Error reading diskstate for url-queue");
|
|
return false;
|
|
}
|
|
|
|
bool DiskState::LoadUrlInfo12(NZBInfo* pNZBInfo, FILE* infile, int iFormatVersion)
|
|
{
|
|
char buf[10240];
|
|
|
|
if (iFormatVersion >= 24)
|
|
{
|
|
int iID;
|
|
if (fscanf(infile, "%i\n", &iID) != 1) goto error;
|
|
pNZBInfo->SetID(iID);
|
|
}
|
|
|
|
int iStatusDummy, iPriority;
|
|
if (fscanf(infile, "%i,%i\n", &iStatusDummy, &iPriority) != 2) goto error;
|
|
pNZBInfo->SetPriority(iPriority);
|
|
|
|
if (iFormatVersion >= 16)
|
|
{
|
|
int iAddTopDummy, iAddPaused;
|
|
if (fscanf(infile, "%i,%i\n", &iAddTopDummy, &iAddPaused) != 2) goto error;
|
|
pNZBInfo->SetAddUrlPaused(iAddPaused);
|
|
}
|
|
|
|
if (!fgets(buf, sizeof(buf), infile)) goto error;
|
|
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
|
|
pNZBInfo->SetURL(buf);
|
|
|
|
if (!fgets(buf, sizeof(buf), infile)) goto error;
|
|
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
|
|
pNZBInfo->SetFilename(buf);
|
|
|
|
if (!fgets(buf, sizeof(buf), infile)) goto error;
|
|
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
|
|
pNZBInfo->SetCategory(buf);
|
|
|
|
if (iFormatVersion >= 31)
|
|
{
|
|
if (!fgets(buf, sizeof(buf), infile)) goto error;
|
|
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
|
|
if (iFormatVersion < 36) ConvertDupeKey(buf, sizeof(buf));
|
|
pNZBInfo->SetDupeKey(buf);
|
|
|
|
int iDupeMode, iDupeScore;
|
|
if (fscanf(infile, "%i,%i\n", &iDupeMode, &iDupeScore) != 2) goto error;
|
|
pNZBInfo->SetDupeMode((EDupeMode)iDupeMode);
|
|
pNZBInfo->SetDupeScore(iDupeScore);
|
|
}
|
|
|
|
return true;
|
|
|
|
error:
|
|
return false;
|
|
}
|
|
|
|
void DiskState::SaveDupInfo(DupInfo* pDupInfo, FILE* outfile)
|
|
{
|
|
unsigned long High, Low;
|
|
Util::SplitInt64(pDupInfo->GetSize(), &High, &Low);
|
|
fprintf(outfile, "%i,%lu,%lu,%u,%u,%i,%i\n", (int)pDupInfo->GetStatus(), High, Low,
|
|
pDupInfo->GetFullContentHash(), pDupInfo->GetFilteredContentHash(),
|
|
pDupInfo->GetDupeScore(), (int)pDupInfo->GetDupeMode());
|
|
fprintf(outfile, "%s\n", pDupInfo->GetName());
|
|
fprintf(outfile, "%s\n", pDupInfo->GetDupeKey());
|
|
}
|
|
|
|
bool DiskState::LoadDupInfo(DupInfo* pDupInfo, FILE* infile, int iFormatVersion)
|
|
{
|
|
char buf[1024];
|
|
|
|
int iStatus;
|
|
unsigned long High, Low;
|
|
unsigned int iFullContentHash, iFilteredContentHash = 0;
|
|
int iDupeScore, iDupe = 0, iDupeMode = 0;
|
|
|
|
if (iFormatVersion >= 39)
|
|
{
|
|
if (fscanf(infile, "%i,%lu,%lu,%u,%u,%i,%i\n", &iStatus, &High, &Low, &iFullContentHash, &iFilteredContentHash, &iDupeScore, &iDupeMode) != 7) goto error;
|
|
}
|
|
else if (iFormatVersion >= 38)
|
|
{
|
|
if (fscanf(infile, "%i,%lu,%lu,%u,%u,%i,%i,%i\n", &iStatus, &High, &Low, &iFullContentHash, &iFilteredContentHash, &iDupeScore, &iDupe, &iDupeMode) != 8) goto error;
|
|
}
|
|
else if (iFormatVersion >= 37)
|
|
{
|
|
if (fscanf(infile, "%i,%lu,%lu,%u,%u,%i,%i\n", &iStatus, &High, &Low, &iFullContentHash, &iFilteredContentHash, &iDupeScore, &iDupe) != 7) goto error;
|
|
}
|
|
else if (iFormatVersion >= 34)
|
|
{
|
|
if (fscanf(infile, "%i,%lu,%lu,%u,%u,%i\n", &iStatus, &High, &Low, &iFullContentHash, &iFilteredContentHash, &iDupeScore) != 6) goto error;
|
|
}
|
|
else
|
|
{
|
|
if (fscanf(infile, "%i,%lu,%lu,%u,%i\n", &iStatus, &High, &Low, &iFullContentHash, &iDupeScore) != 5) goto error;
|
|
}
|
|
|
|
pDupInfo->SetStatus((DupInfo::EStatus)iStatus);
|
|
pDupInfo->SetFullContentHash(iFullContentHash);
|
|
pDupInfo->SetFilteredContentHash(iFilteredContentHash);
|
|
pDupInfo->SetSize(Util::JoinInt64(High, Low));
|
|
pDupInfo->SetDupeScore(iDupeScore);
|
|
pDupInfo->SetDupeMode((EDupeMode)iDupeMode);
|
|
|
|
if (!fgets(buf, sizeof(buf), infile)) goto error;
|
|
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
|
|
pDupInfo->SetName(buf);
|
|
|
|
if (!fgets(buf, sizeof(buf), infile)) goto error;
|
|
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
|
|
if (iFormatVersion < 36) ConvertDupeKey(buf, sizeof(buf));
|
|
pDupInfo->SetDupeKey(buf);
|
|
|
|
return true;
|
|
|
|
error:
|
|
return false;
|
|
}
|
|
|
|
void DiskState::SaveHistory(DownloadQueue* pDownloadQueue, FILE* outfile)
|
|
{
|
|
debug("Saving history to disk");
|
|
|
|
fprintf(outfile, "%i\n", (int)pDownloadQueue->GetHistory()->size());
|
|
for (HistoryList::iterator it = pDownloadQueue->GetHistory()->begin(); it != pDownloadQueue->GetHistory()->end(); it++)
|
|
{
|
|
HistoryInfo* pHistoryInfo = *it;
|
|
|
|
fprintf(outfile, "%i,%i,%i\n", pHistoryInfo->GetID(), (int)pHistoryInfo->GetKind(), (int)pHistoryInfo->GetTime());
|
|
|
|
if (pHistoryInfo->GetKind() == HistoryInfo::hkNzb || pHistoryInfo->GetKind() == HistoryInfo::hkUrl)
|
|
{
|
|
SaveNZBInfo(pHistoryInfo->GetNZBInfo(), outfile);
|
|
}
|
|
else if (pHistoryInfo->GetKind() == HistoryInfo::hkDup)
|
|
{
|
|
SaveDupInfo(pHistoryInfo->GetDupInfo(), outfile);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool DiskState::LoadHistory(DownloadQueue* pDownloadQueue, NZBList* pNZBList, Servers* pServers, FILE* infile, int iFormatVersion)
|
|
{
|
|
debug("Loading history from disk");
|
|
|
|
int size;
|
|
if (fscanf(infile, "%i\n", &size) != 1) goto error;
|
|
for (int i = 0; i < size; i++)
|
|
{
|
|
HistoryInfo* pHistoryInfo = NULL;
|
|
HistoryInfo::EKind eKind = HistoryInfo::hkNzb;
|
|
int iID = 0;
|
|
int iTime;
|
|
|
|
if (iFormatVersion >= 33)
|
|
{
|
|
int iKind = 0;
|
|
if (fscanf(infile, "%i,%i,%i\n", &iID, &iKind, &iTime) != 3) goto error;
|
|
eKind = (HistoryInfo::EKind)iKind;
|
|
}
|
|
else
|
|
{
|
|
if (iFormatVersion >= 24)
|
|
{
|
|
if (fscanf(infile, "%i\n", &iID) != 1) goto error;
|
|
}
|
|
|
|
if (iFormatVersion >= 15)
|
|
{
|
|
int iKind = 0;
|
|
if (fscanf(infile, "%i\n", &iKind) != 1) goto error;
|
|
eKind = (HistoryInfo::EKind)iKind;
|
|
}
|
|
}
|
|
|
|
if (eKind == HistoryInfo::hkNzb)
|
|
{
|
|
NZBInfo* pNZBInfo = NULL;
|
|
|
|
if (iFormatVersion < 43)
|
|
{
|
|
unsigned int iNZBIndex;
|
|
if (fscanf(infile, "%i\n", &iNZBIndex) != 1) goto error;
|
|
pNZBInfo = pNZBList->at(iNZBIndex - 1);
|
|
}
|
|
else
|
|
{
|
|
pNZBInfo = new NZBInfo();
|
|
if (!LoadNZBInfo(pNZBInfo, pServers, infile, iFormatVersion)) goto error;
|
|
pNZBInfo->LeavePostProcess();
|
|
}
|
|
|
|
pHistoryInfo = new HistoryInfo(pNZBInfo);
|
|
|
|
if (iFormatVersion < 28 && pNZBInfo->GetParStatus() == 0 &&
|
|
pNZBInfo->GetUnpackStatus() == 0 && pNZBInfo->GetMoveStatus() == 0)
|
|
{
|
|
pNZBInfo->SetDeleteStatus(NZBInfo::dsManual);
|
|
}
|
|
}
|
|
else if (eKind == HistoryInfo::hkUrl)
|
|
{
|
|
NZBInfo* pNZBInfo = new NZBInfo();
|
|
if (iFormatVersion >= 46)
|
|
{
|
|
if (!LoadNZBInfo(pNZBInfo, pServers, infile, iFormatVersion)) goto error;
|
|
}
|
|
else
|
|
{
|
|
if (!LoadUrlInfo12(pNZBInfo, infile, iFormatVersion)) goto error;
|
|
}
|
|
pHistoryInfo = new HistoryInfo(pNZBInfo);
|
|
}
|
|
else if (eKind == HistoryInfo::hkDup)
|
|
{
|
|
DupInfo* pDupInfo = new DupInfo();
|
|
if (!LoadDupInfo(pDupInfo, infile, iFormatVersion)) goto error;
|
|
if (iFormatVersion >= 47)
|
|
{
|
|
pDupInfo->SetID(iID);
|
|
}
|
|
pHistoryInfo = new HistoryInfo(pDupInfo);
|
|
}
|
|
|
|
if (iFormatVersion < 33)
|
|
{
|
|
if (fscanf(infile, "%i\n", &iTime) != 1) goto error;
|
|
}
|
|
|
|
pHistoryInfo->SetTime((time_t)iTime);
|
|
|
|
pDownloadQueue->GetHistory()->push_back(pHistoryInfo);
|
|
}
|
|
|
|
return true;
|
|
|
|
error:
|
|
error("Error reading diskstate for history");
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
* Find index of nzb-info.
|
|
*/
|
|
int DiskState::FindNZBInfoIndex(NZBList* pNZBList, NZBInfo* pNZBInfo)
|
|
{
|
|
int iNZBIndex = 0;
|
|
for (NZBList::iterator it = pNZBList->begin(); it != pNZBList->end(); it++)
|
|
{
|
|
NZBInfo* pNZBInfo2 = *it;
|
|
iNZBIndex++;
|
|
if (pNZBInfo2 == pNZBInfo)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
return iNZBIndex;
|
|
}
|
|
|
|
/*
|
|
* Find nzb-info by id.
|
|
*/
|
|
NZBInfo* DiskState::FindNZBInfo(DownloadQueue* pDownloadQueue, int iID)
|
|
{
|
|
for (NZBList::iterator it = pDownloadQueue->GetQueue()->begin(); it != pDownloadQueue->GetQueue()->end(); it++)
|
|
{
|
|
NZBInfo* pNZBInfo = *it;
|
|
if (pNZBInfo->GetID() == iID)
|
|
{
|
|
return pNZBInfo;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* Deletes whole download queue including history.
|
|
*/
|
|
void DiskState::DiscardDownloadQueue()
|
|
{
|
|
debug("Discarding queue");
|
|
|
|
char szFullFilename[1024];
|
|
snprintf(szFullFilename, 1024, "%s%s", g_pOptions->GetQueueDir(), "queue");
|
|
szFullFilename[1024-1] = '\0';
|
|
remove(szFullFilename);
|
|
|
|
DirBrowser dir(g_pOptions->GetQueueDir());
|
|
while (const char* filename = dir.Next())
|
|
{
|
|
// delete all files whose names have only characters '0'..'9'
|
|
bool bOnlyNums = true;
|
|
for (const char* p = filename; *p != '\0'; p++)
|
|
{
|
|
if (!('0' <= *p && *p <= '9'))
|
|
{
|
|
bOnlyNums = false;
|
|
break;
|
|
}
|
|
}
|
|
if (bOnlyNums)
|
|
{
|
|
snprintf(szFullFilename, 1024, "%s%s", g_pOptions->GetQueueDir(), filename);
|
|
szFullFilename[1024-1] = '\0';
|
|
remove(szFullFilename);
|
|
|
|
// delete file state file
|
|
snprintf(szFullFilename, 1024, "%s%ss", g_pOptions->GetQueueDir(), filename);
|
|
szFullFilename[1024-1] = '\0';
|
|
remove(szFullFilename);
|
|
|
|
// delete failed info file
|
|
snprintf(szFullFilename, 1024, "%s%sc", g_pOptions->GetQueueDir(), filename);
|
|
szFullFilename[1024-1] = '\0';
|
|
remove(szFullFilename);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool DiskState::DownloadQueueExists()
|
|
{
|
|
debug("Checking if a saved queue exists on disk");
|
|
|
|
char fileName[1024];
|
|
snprintf(fileName, 1024, "%s%s", g_pOptions->GetQueueDir(), "queue");
|
|
fileName[1024-1] = '\0';
|
|
return Util::FileExists(fileName);
|
|
}
|
|
|
|
void DiskState::DiscardFile(FileInfo* pFileInfo, bool bDeleteData, bool bDeletePartialState, bool bDeleteCompletedState)
|
|
{
|
|
char fileName[1024];
|
|
|
|
// info and articles file
|
|
if (bDeleteData)
|
|
{
|
|
snprintf(fileName, 1024, "%s%i", g_pOptions->GetQueueDir(), pFileInfo->GetID());
|
|
fileName[1024-1] = '\0';
|
|
remove(fileName);
|
|
}
|
|
|
|
// partial state file
|
|
if (bDeletePartialState)
|
|
{
|
|
snprintf(fileName, 1024, "%s%is", g_pOptions->GetQueueDir(), pFileInfo->GetID());
|
|
fileName[1024-1] = '\0';
|
|
remove(fileName);
|
|
}
|
|
|
|
// completed state file
|
|
if (bDeleteCompletedState)
|
|
{
|
|
snprintf(fileName, 1024, "%s%ic", g_pOptions->GetQueueDir(), pFileInfo->GetID());
|
|
fileName[1024-1] = '\0';
|
|
remove(fileName);
|
|
}
|
|
}
|
|
|
|
void DiskState::CleanupTempDir(DownloadQueue* pDownloadQueue)
|
|
{
|
|
DirBrowser dir(g_pOptions->GetTempDir());
|
|
while (const char* filename = dir.Next())
|
|
{
|
|
int id, part;
|
|
if (strstr(filename, ".tmp") || strstr(filename, ".dec") ||
|
|
(sscanf(filename, "%i.%i", &id, &part) == 2))
|
|
{
|
|
char szFullFilename[1024];
|
|
snprintf(szFullFilename, 1024, "%s%s", g_pOptions->GetTempDir(), filename);
|
|
szFullFilename[1024-1] = '\0';
|
|
remove(szFullFilename);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* For safety:
|
|
* - first save to temp-file (feeds.new)
|
|
* - then delete feeds
|
|
* - then rename feeds.new to feeds
|
|
*/
|
|
bool DiskState::SaveFeeds(Feeds* pFeeds, FeedHistory* pFeedHistory)
|
|
{
|
|
debug("Saving feeds state to disk");
|
|
|
|
StateFile stateFile("feeds", 3);
|
|
|
|
if (pFeeds->empty() && pFeedHistory->empty())
|
|
{
|
|
stateFile.Discard();
|
|
return true;
|
|
}
|
|
|
|
FILE* outfile = stateFile.BeginWriteTransaction();
|
|
if (!outfile)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// save status
|
|
SaveFeedStatus(pFeeds, outfile);
|
|
|
|
// save history
|
|
SaveFeedHistory(pFeedHistory, outfile);
|
|
|
|
// now rename to dest file name
|
|
return stateFile.FinishWriteTransaction();
|
|
}
|
|
|
|
bool DiskState::LoadFeeds(Feeds* pFeeds, FeedHistory* pFeedHistory)
|
|
{
|
|
debug("Loading feeds state from disk");
|
|
|
|
StateFile stateFile("feeds", 3);
|
|
|
|
if (!stateFile.FileExists())
|
|
{
|
|
return true;
|
|
}
|
|
|
|
FILE* infile = stateFile.BeginReadTransaction();
|
|
if (!infile)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool bOK = false;
|
|
int iFormatVersion = stateFile.GetFileVersion();
|
|
|
|
// load feed status
|
|
if (!LoadFeedStatus(pFeeds, infile, iFormatVersion)) goto error;
|
|
|
|
// load feed history
|
|
if (!LoadFeedHistory(pFeedHistory, infile, iFormatVersion)) goto error;
|
|
|
|
bOK = true;
|
|
|
|
error:
|
|
|
|
if (!bOK)
|
|
{
|
|
error("Error reading diskstate for feeds");
|
|
}
|
|
|
|
return bOK;
|
|
}
|
|
|
|
bool DiskState::SaveFeedStatus(Feeds* pFeeds, FILE* outfile)
|
|
{
|
|
debug("Saving feed status to disk");
|
|
|
|
fprintf(outfile, "%i\n", (int)pFeeds->size());
|
|
for (Feeds::iterator it = pFeeds->begin(); it != pFeeds->end(); it++)
|
|
{
|
|
FeedInfo* pFeedInfo = *it;
|
|
|
|
fprintf(outfile, "%s\n", pFeedInfo->GetUrl());
|
|
fprintf(outfile, "%u\n", pFeedInfo->GetFilterHash());
|
|
fprintf(outfile, "%i\n", (int)pFeedInfo->GetLastUpdate());
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool DiskState::LoadFeedStatus(Feeds* pFeeds, FILE* infile, int iFormatVersion)
|
|
{
|
|
debug("Loading feed status from disk");
|
|
|
|
int size;
|
|
if (fscanf(infile, "%i\n", &size) != 1) goto error;
|
|
for (int i = 0; i < size; i++)
|
|
{
|
|
char szUrl[1024];
|
|
if (!fgets(szUrl, sizeof(szUrl), infile)) goto error;
|
|
if (szUrl[0] != 0) szUrl[strlen(szUrl)-1] = 0; // remove traling '\n'
|
|
|
|
char szFilter[1024];
|
|
if (iFormatVersion == 2)
|
|
{
|
|
if (!fgets(szFilter, sizeof(szFilter), infile)) goto error;
|
|
if (szFilter[0] != 0) szFilter[strlen(szFilter)-1] = 0; // remove traling '\n'
|
|
}
|
|
|
|
unsigned int iFilterHash = 0;
|
|
if (iFormatVersion >= 3)
|
|
{
|
|
if (fscanf(infile, "%u\n", &iFilterHash) != 1) goto error;
|
|
}
|
|
|
|
int iLastUpdate = 0;
|
|
if (fscanf(infile, "%i\n", &iLastUpdate) != 1) goto error;
|
|
|
|
for (Feeds::iterator it = pFeeds->begin(); it != pFeeds->end(); it++)
|
|
{
|
|
FeedInfo* pFeedInfo = *it;
|
|
|
|
if (!strcmp(pFeedInfo->GetUrl(), szUrl) &&
|
|
((iFormatVersion == 1) ||
|
|
(iFormatVersion == 2 && !strcmp(pFeedInfo->GetFilter(), szFilter)) ||
|
|
(iFormatVersion >= 3 && pFeedInfo->GetFilterHash() == iFilterHash)))
|
|
{
|
|
pFeedInfo->SetLastUpdate((time_t)iLastUpdate);
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
|
|
error:
|
|
error("Error reading feed status from disk");
|
|
return false;
|
|
}
|
|
|
|
bool DiskState::SaveFeedHistory(FeedHistory* pFeedHistory, FILE* outfile)
|
|
{
|
|
debug("Saving feed history to disk");
|
|
|
|
fprintf(outfile, "%i\n", (int)pFeedHistory->size());
|
|
for (FeedHistory::iterator it = pFeedHistory->begin(); it != pFeedHistory->end(); it++)
|
|
{
|
|
FeedHistoryInfo* pFeedHistoryInfo = *it;
|
|
|
|
fprintf(outfile, "%i,%i\n", (int)pFeedHistoryInfo->GetStatus(), (int)pFeedHistoryInfo->GetLastSeen());
|
|
fprintf(outfile, "%s\n", pFeedHistoryInfo->GetUrl());
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool DiskState::LoadFeedHistory(FeedHistory* pFeedHistory, FILE* infile, int iFormatVersion)
|
|
{
|
|
debug("Loading feed history from disk");
|
|
|
|
int size;
|
|
if (fscanf(infile, "%i\n", &size) != 1) goto error;
|
|
for (int i = 0; i < size; i++)
|
|
{
|
|
int iStatus = 0;
|
|
int iLastSeen = 0;
|
|
int r = fscanf(infile, "%i,%i\n", &iStatus, &iLastSeen);
|
|
if (r != 2) goto error;
|
|
|
|
char szUrl[1024];
|
|
if (!fgets(szUrl, sizeof(szUrl), infile)) goto error;
|
|
if (szUrl[0] != 0) szUrl[strlen(szUrl)-1] = 0; // remove traling '\n'
|
|
|
|
pFeedHistory->Add(szUrl, (FeedHistoryInfo::EStatus)(iStatus), (time_t)(iLastSeen));
|
|
}
|
|
|
|
return true;
|
|
|
|
error:
|
|
error("Error reading feed history from disk");
|
|
return false;
|
|
}
|
|
|
|
// calculate critical health for old NZBs
|
|
void DiskState::CalcCriticalHealth(NZBList* pNZBList)
|
|
{
|
|
// build list of old NZBs which do not have critical health calculated
|
|
for (NZBList::iterator it = pNZBList->begin(); it != pNZBList->end(); it++)
|
|
{
|
|
NZBInfo* pNZBInfo = *it;
|
|
if (pNZBInfo->CalcCriticalHealth(false) == 1000)
|
|
{
|
|
debug("Calculating critical health for %s", pNZBInfo->GetName());
|
|
|
|
for (FileList::iterator it = pNZBInfo->GetFileList()->begin(); it != pNZBInfo->GetFileList()->end(); it++)
|
|
{
|
|
FileInfo* pFileInfo = *it;
|
|
|
|
char szLoFileName[1024];
|
|
strncpy(szLoFileName, pFileInfo->GetFilename(), 1024);
|
|
szLoFileName[1024-1] = '\0';
|
|
for (char* p = szLoFileName; *p; p++) *p = tolower(*p); // convert string to lowercase
|
|
bool bParFile = strstr(szLoFileName, ".par2");
|
|
|
|
pFileInfo->SetParFile(bParFile);
|
|
if (bParFile)
|
|
{
|
|
pNZBInfo->SetParSize(pNZBInfo->GetParSize() + pFileInfo->GetSize());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void DiskState::CalcFileStats(DownloadQueue* pDownloadQueue, int iFormatVersion)
|
|
{
|
|
for (NZBList::iterator it = pDownloadQueue->GetQueue()->begin(); it != pDownloadQueue->GetQueue()->end(); it++)
|
|
{
|
|
NZBInfo* pNZBInfo = *it;
|
|
CalcNZBFileStats(pNZBInfo, iFormatVersion);
|
|
}
|
|
|
|
for (HistoryList::iterator it = pDownloadQueue->GetHistory()->begin(); it != pDownloadQueue->GetHistory()->end(); it++)
|
|
{
|
|
HistoryInfo* pHistoryInfo = *it;
|
|
if (pHistoryInfo->GetKind() == HistoryInfo::hkNzb)
|
|
{
|
|
CalcNZBFileStats(pHistoryInfo->GetNZBInfo(), iFormatVersion);
|
|
}
|
|
}
|
|
}
|
|
|
|
void DiskState::CalcNZBFileStats(NZBInfo* pNZBInfo, int iFormatVersion)
|
|
{
|
|
int iPausedFileCount = 0;
|
|
int iRemainingParCount = 0;
|
|
int iSuccessArticles = 0;
|
|
int iFailedArticles = 0;
|
|
long long lRemainingSize = 0;
|
|
long long lPausedSize = 0;
|
|
long long lSuccessSize = 0;
|
|
long long lFailedSize = 0;
|
|
long long lParSuccessSize = 0;
|
|
long long lParFailedSize = 0;
|
|
|
|
for (FileList::iterator it2 = pNZBInfo->GetFileList()->begin(); it2 != pNZBInfo->GetFileList()->end(); it2++)
|
|
{
|
|
FileInfo* pFileInfo = *it2;
|
|
|
|
lRemainingSize += pFileInfo->GetRemainingSize();
|
|
iSuccessArticles += pFileInfo->GetSuccessArticles();
|
|
iFailedArticles += pFileInfo->GetFailedArticles();
|
|
lSuccessSize += pFileInfo->GetSuccessSize();
|
|
lFailedSize += pFileInfo->GetFailedSize();
|
|
|
|
if (pFileInfo->GetPaused())
|
|
{
|
|
lPausedSize += pFileInfo->GetRemainingSize();
|
|
iPausedFileCount++;
|
|
}
|
|
if (pFileInfo->GetParFile())
|
|
{
|
|
iRemainingParCount++;
|
|
lParSuccessSize += pFileInfo->GetSuccessSize();
|
|
lParFailedSize += pFileInfo->GetFailedSize();
|
|
}
|
|
|
|
pNZBInfo->GetCurrentServerStats()->ListOp(pFileInfo->GetServerStats(), ServerStatList::soAdd);
|
|
}
|
|
|
|
pNZBInfo->SetRemainingSize(lRemainingSize);
|
|
pNZBInfo->SetPausedSize(lPausedSize);
|
|
pNZBInfo->SetPausedFileCount(iPausedFileCount);
|
|
pNZBInfo->SetRemainingParCount(iRemainingParCount);
|
|
|
|
pNZBInfo->SetCurrentSuccessArticles(pNZBInfo->GetSuccessArticles() + iSuccessArticles);
|
|
pNZBInfo->SetCurrentFailedArticles(pNZBInfo->GetFailedArticles() + iFailedArticles);
|
|
pNZBInfo->SetCurrentSuccessSize(pNZBInfo->GetSuccessSize() + lSuccessSize);
|
|
pNZBInfo->SetCurrentFailedSize(pNZBInfo->GetFailedSize() + lFailedSize);
|
|
pNZBInfo->SetParCurrentSuccessSize(pNZBInfo->GetParSuccessSize() + lParSuccessSize);
|
|
pNZBInfo->SetParCurrentFailedSize(pNZBInfo->GetParFailedSize() + lParFailedSize);
|
|
|
|
if (iFormatVersion < 44)
|
|
{
|
|
pNZBInfo->UpdateMinMaxTime();
|
|
}
|
|
}
|
|
|
|
bool DiskState::LoadAllFileStates(DownloadQueue* pDownloadQueue, Servers* pServers)
|
|
{
|
|
char szCacheFlagFilename[1024];
|
|
snprintf(szCacheFlagFilename, 1024, "%s%s", g_pOptions->GetQueueDir(), "acache");
|
|
szCacheFlagFilename[1024-1] = '\0';
|
|
|
|
bool bCacheWasActive = Util::FileExists(szCacheFlagFilename);
|
|
|
|
DirBrowser dir(g_pOptions->GetQueueDir());
|
|
while (const char* filename = dir.Next())
|
|
{
|
|
int id;
|
|
char suffix;
|
|
if (sscanf(filename, "%i%c", &id, &suffix) == 2 && suffix == 's')
|
|
{
|
|
if (g_pOptions->GetContinuePartial() && !bCacheWasActive)
|
|
{
|
|
for (NZBList::iterator it = pDownloadQueue->GetQueue()->begin(); it != pDownloadQueue->GetQueue()->end(); it++)
|
|
{
|
|
NZBInfo* pNZBInfo = *it;
|
|
for (FileList::iterator it2 = pNZBInfo->GetFileList()->begin(); it2 != pNZBInfo->GetFileList()->end(); it2++)
|
|
{
|
|
FileInfo* pFileInfo = *it2;
|
|
if (pFileInfo->GetID() == id)
|
|
{
|
|
if (!LoadArticles(pFileInfo)) goto error;
|
|
if (!LoadFileState(pFileInfo, pServers, false)) goto error;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
char szFullFilename[1024];
|
|
snprintf(szFullFilename, 1024, "%s%s", g_pOptions->GetQueueDir(), filename);
|
|
szFullFilename[1024-1] = '\0';
|
|
remove(szFullFilename);
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
|
|
error:
|
|
return false;
|
|
}
|
|
|
|
void DiskState::ConvertDupeKey(char* buf, int bufsize)
|
|
{
|
|
if (strncmp(buf, "rageid=", 7))
|
|
{
|
|
return;
|
|
}
|
|
|
|
int iRageId = atoi(buf + 7);
|
|
int iSeason = 0;
|
|
int iEpisode = 0;
|
|
char* p = strchr(buf + 7, ',');
|
|
if (p)
|
|
{
|
|
iSeason = atoi(p + 1);
|
|
p = strchr(p + 1, ',');
|
|
if (p)
|
|
{
|
|
iEpisode = atoi(p + 1);
|
|
}
|
|
}
|
|
|
|
if (iRageId != 0 && iSeason != 0 && iEpisode != 0)
|
|
{
|
|
snprintf(buf, bufsize, "rageid=%i-S%02i-E%02i", iRageId, iSeason, iEpisode);
|
|
}
|
|
}
|
|
|
|
bool DiskState::SaveStats(Servers* pServers, ServerVolumes* pServerVolumes)
|
|
{
|
|
debug("Saving stats to disk");
|
|
|
|
StateFile stateFile("stats", 3);
|
|
|
|
if (pServers->empty())
|
|
{
|
|
stateFile.Discard();
|
|
return true;
|
|
}
|
|
|
|
FILE* outfile = stateFile.BeginWriteTransaction();
|
|
if (!outfile)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// save server names
|
|
SaveServerInfo(pServers, outfile);
|
|
|
|
// save stat
|
|
SaveVolumeStat(pServerVolumes, outfile);
|
|
|
|
// now rename to dest file name
|
|
return stateFile.FinishWriteTransaction();
|
|
}
|
|
|
|
bool DiskState::LoadStats(Servers* pServers, ServerVolumes* pServerVolumes, bool* pPerfectMatch)
|
|
{
|
|
debug("Loading stats from disk");
|
|
|
|
StateFile stateFile("stats", 3);
|
|
|
|
if (!stateFile.FileExists())
|
|
{
|
|
return true;
|
|
}
|
|
|
|
FILE* infile = stateFile.BeginReadTransaction();
|
|
if (!infile)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool bOK = false;
|
|
int iFormatVersion = stateFile.GetFileVersion();
|
|
|
|
if (!LoadServerInfo(pServers, infile, iFormatVersion, pPerfectMatch)) goto error;
|
|
|
|
if (iFormatVersion >=2)
|
|
{
|
|
if (!LoadVolumeStat(pServers, pServerVolumes, infile, iFormatVersion)) goto error;
|
|
}
|
|
|
|
bOK = true;
|
|
|
|
error:
|
|
|
|
if (!bOK)
|
|
{
|
|
error("Error reading diskstate for statistics");
|
|
}
|
|
|
|
return bOK;
|
|
}
|
|
|
|
bool DiskState::SaveServerInfo(Servers* pServers, FILE* outfile)
|
|
{
|
|
debug("Saving server info to disk");
|
|
|
|
fprintf(outfile, "%i\n", (int)pServers->size());
|
|
for (Servers::iterator it = pServers->begin(); it != pServers->end(); it++)
|
|
{
|
|
NewsServer* pNewsServer = *it;
|
|
|
|
fprintf(outfile, "%s\n", pNewsServer->GetName());
|
|
fprintf(outfile, "%s\n", pNewsServer->GetHost());
|
|
fprintf(outfile, "%i\n", pNewsServer->GetPort());
|
|
fprintf(outfile, "%s\n", pNewsServer->GetUser());
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
***************************************************************************************
|
|
* Server matching
|
|
*/
|
|
|
|
class ServerRef
|
|
{
|
|
public:
|
|
int m_iStateID;
|
|
char* m_szName;
|
|
char* m_szHost;
|
|
int m_iPort;
|
|
char* m_szUser;
|
|
bool m_bMatched;
|
|
bool m_bPerfect;
|
|
|
|
~ServerRef();
|
|
int GetStateID() { return m_iStateID; }
|
|
const char* GetName() { return m_szName; }
|
|
const char* GetHost() { return m_szHost; }
|
|
int GetPort() { return m_iPort; }
|
|
const char* GetUser() { return m_szUser; }
|
|
bool GetMatched() { return m_bMatched; }
|
|
void SetMatched(bool bMatched) { m_bMatched = bMatched; }
|
|
bool GetPerfect() { return m_bPerfect; }
|
|
void SetPerfect(bool bPerfect) { m_bPerfect = bPerfect; }
|
|
};
|
|
|
|
typedef std::deque<ServerRef*> ServerRefList;
|
|
|
|
ServerRef::~ServerRef()
|
|
{
|
|
free(m_szName);
|
|
free(m_szHost);
|
|
free(m_szUser);
|
|
}
|
|
|
|
enum ECriteria
|
|
{
|
|
eName,
|
|
eHost,
|
|
ePort,
|
|
eUser
|
|
};
|
|
|
|
void FindCandidates(NewsServer* pNewsServer, ServerRefList* pRefs, ECriteria eCriteria, bool bKeepIfNothing)
|
|
{
|
|
ServerRefList originalRefs;
|
|
originalRefs.insert(originalRefs.begin(), pRefs->begin(), pRefs->end());
|
|
|
|
int index = 0;
|
|
for (ServerRefList::iterator it = pRefs->begin(); it != pRefs->end(); )
|
|
{
|
|
ServerRef* pRef = *it;
|
|
bool bMatch = false;
|
|
switch(eCriteria)
|
|
{
|
|
case eName:
|
|
bMatch = !strcasecmp(pNewsServer->GetName(), pRef->GetName());
|
|
break;
|
|
case eHost:
|
|
bMatch = !strcasecmp(pNewsServer->GetHost(), pRef->GetHost());
|
|
break;
|
|
case ePort:
|
|
bMatch = pNewsServer->GetPort() == pRef->GetPort();
|
|
break;
|
|
case eUser:
|
|
bMatch = !strcasecmp(pNewsServer->GetUser(), pRef->GetUser());
|
|
break;
|
|
}
|
|
if (bMatch && !pRef->GetMatched())
|
|
{
|
|
it++;
|
|
index++;
|
|
}
|
|
else
|
|
{
|
|
pRefs->erase(it);
|
|
it = pRefs->begin() + index;
|
|
}
|
|
}
|
|
|
|
if (pRefs->size() == 0 && bKeepIfNothing)
|
|
{
|
|
pRefs->insert(pRefs->begin(), originalRefs.begin(), originalRefs.end());
|
|
}
|
|
}
|
|
|
|
void MatchServers(Servers* pServers, ServerRefList* pServerRefs)
|
|
{
|
|
// Step 1: trying perfect match
|
|
for (Servers::iterator it = pServers->begin(); it != pServers->end(); it++)
|
|
{
|
|
NewsServer* pNewsServer = *it;
|
|
ServerRefList matchedRefs;
|
|
matchedRefs.insert(matchedRefs.begin(), pServerRefs->begin(), pServerRefs->end());
|
|
FindCandidates(pNewsServer, &matchedRefs, eName, false);
|
|
FindCandidates(pNewsServer, &matchedRefs, eHost, false);
|
|
FindCandidates(pNewsServer, &matchedRefs, ePort, false);
|
|
FindCandidates(pNewsServer, &matchedRefs, eUser, false);
|
|
|
|
if (matchedRefs.size() == 1)
|
|
{
|
|
ServerRef* pRef = matchedRefs.front();
|
|
pNewsServer->SetStateID(pRef->GetStateID());
|
|
pRef->SetMatched(true);
|
|
pRef->SetPerfect(true);
|
|
}
|
|
}
|
|
|
|
// Step 2: matching host, port, username and server-name
|
|
for (Servers::iterator it = pServers->begin(); it != pServers->end(); it++)
|
|
{
|
|
NewsServer* pNewsServer = *it;
|
|
if (!pNewsServer->GetStateID())
|
|
{
|
|
ServerRefList matchedRefs;
|
|
matchedRefs.insert(matchedRefs.begin(), pServerRefs->begin(), pServerRefs->end());
|
|
|
|
FindCandidates(pNewsServer, &matchedRefs, eHost, false);
|
|
|
|
if (matchedRefs.size() > 1)
|
|
{
|
|
FindCandidates(pNewsServer, &matchedRefs, eName, true);
|
|
}
|
|
|
|
if (matchedRefs.size() > 1)
|
|
{
|
|
FindCandidates(pNewsServer, &matchedRefs, eUser, true);
|
|
}
|
|
|
|
if (matchedRefs.size() > 1)
|
|
{
|
|
FindCandidates(pNewsServer, &matchedRefs, ePort, true);
|
|
}
|
|
|
|
if (!matchedRefs.empty())
|
|
{
|
|
ServerRef* pRef = matchedRefs.front();
|
|
pNewsServer->SetStateID(pRef->GetStateID());
|
|
pRef->SetMatched(true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* END: Server matching
|
|
***************************************************************************************
|
|
*/
|
|
|
|
bool DiskState::LoadServerInfo(Servers* pServers, FILE* infile, int iFormatVersion, bool* pPerfectMatch)
|
|
{
|
|
debug("Loading server info from disk");
|
|
|
|
ServerRefList serverRefs;
|
|
*pPerfectMatch = true;
|
|
|
|
int size;
|
|
if (fscanf(infile, "%i\n", &size) != 1) goto error;
|
|
for (int i = 0; i < size; i++)
|
|
{
|
|
char szName[1024];
|
|
if (!fgets(szName, sizeof(szName), infile)) goto error;
|
|
if (szName[0] != 0) szName[strlen(szName)-1] = 0; // remove traling '\n'
|
|
|
|
char szHost[200];
|
|
if (!fgets(szHost, sizeof(szHost), infile)) goto error;
|
|
if (szHost[0] != 0) szHost[strlen(szHost)-1] = 0; // remove traling '\n'
|
|
|
|
int iPort;
|
|
if (fscanf(infile, "%i\n", &iPort) != 1) goto error;
|
|
|
|
char szUser[100];
|
|
if (!fgets(szUser, sizeof(szUser), infile)) goto error;
|
|
if (szUser[0] != 0) szUser[strlen(szUser)-1] = 0; // remove traling '\n'
|
|
|
|
ServerRef* pRef = new ServerRef();
|
|
pRef->m_iStateID = i + 1;
|
|
pRef->m_szName = strdup(szName);
|
|
pRef->m_szHost = strdup(szHost);
|
|
pRef->m_iPort = iPort;
|
|
pRef->m_szUser = strdup(szUser);
|
|
pRef->m_bMatched = false;
|
|
pRef->m_bPerfect = false;
|
|
serverRefs.push_back(pRef);
|
|
}
|
|
|
|
MatchServers(pServers, &serverRefs);
|
|
|
|
for (ServerRefList::iterator it = serverRefs.begin(); it != serverRefs.end(); it++)
|
|
{
|
|
ServerRef* pRef = *it;
|
|
*pPerfectMatch = *pPerfectMatch && pRef->GetPerfect();
|
|
delete *it;
|
|
}
|
|
|
|
debug("******** MATCHING NEWS-SERVERS **********");
|
|
for (Servers::iterator it = pServers->begin(); it != pServers->end(); it++)
|
|
{
|
|
NewsServer* pNewsServer = *it;
|
|
*pPerfectMatch = *pPerfectMatch && pNewsServer->GetStateID();
|
|
debug("Server %i -> %i", pNewsServer->GetID(), pNewsServer->GetStateID());
|
|
debug("Server %i.Name: %s", pNewsServer->GetID(), pNewsServer->GetName());
|
|
debug("Server %i.Host: %s:%i", pNewsServer->GetID(), pNewsServer->GetHost(), pNewsServer->GetPort());
|
|
}
|
|
|
|
debug("All servers perfectly matched: %s", *pPerfectMatch ? "yes" : "no");
|
|
|
|
return true;
|
|
|
|
error:
|
|
error("Error reading server info from disk");
|
|
|
|
for (ServerRefList::iterator it = serverRefs.begin(); it != serverRefs.end(); it++)
|
|
{
|
|
delete *it;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool DiskState::SaveVolumeStat(ServerVolumes* pServerVolumes, FILE* outfile)
|
|
{
|
|
debug("Saving volume stats to disk");
|
|
|
|
fprintf(outfile, "%i\n", (int)pServerVolumes->size());
|
|
for (ServerVolumes::iterator it = pServerVolumes->begin(); it != pServerVolumes->end(); it++)
|
|
{
|
|
ServerVolume* pServerVolume = *it;
|
|
|
|
fprintf(outfile, "%i,%i,%i\n", pServerVolume->GetFirstDay(), (int)pServerVolume->GetDataTime(), (int)pServerVolume->GetCustomTime());
|
|
|
|
unsigned long High1, Low1, High2, Low2;
|
|
Util::SplitInt64(pServerVolume->GetTotalBytes(), &High1, &Low1);
|
|
Util::SplitInt64(pServerVolume->GetCustomBytes(), &High2, &Low2);
|
|
fprintf(outfile, "%lu,%lu,%lu,%lu\n", High1, Low1, High2, Low2);
|
|
|
|
ServerVolume::VolumeArray* VolumeArrays[] = { pServerVolume->BytesPerSeconds(),
|
|
pServerVolume->BytesPerMinutes(), pServerVolume->BytesPerHours(), pServerVolume->BytesPerDays() };
|
|
for (int i=0; i < 4; i++)
|
|
{
|
|
ServerVolume::VolumeArray* pVolumeArray = VolumeArrays[i];
|
|
|
|
fprintf(outfile, "%i\n", (int)pVolumeArray->size());
|
|
for (ServerVolume::VolumeArray::iterator it2 = pVolumeArray->begin(); it2 != pVolumeArray->end(); it2++)
|
|
{
|
|
long long lBytes = *it2;
|
|
Util::SplitInt64(lBytes, &High1, &Low1);
|
|
fprintf(outfile, "%lu,%lu\n", High1, Low1);
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool DiskState::LoadVolumeStat(Servers* pServers, ServerVolumes* pServerVolumes, FILE* infile, int iFormatVersion)
|
|
{
|
|
debug("Loading volume stats from disk");
|
|
|
|
int size;
|
|
if (fscanf(infile, "%i\n", &size) != 1) goto error;
|
|
for (int i = 0; i < size; i++)
|
|
{
|
|
ServerVolume* pServerVolume = NULL;
|
|
|
|
if (i == 0)
|
|
{
|
|
pServerVolume = pServerVolumes->at(0);
|
|
}
|
|
else
|
|
{
|
|
for (Servers::iterator it = pServers->begin(); it != pServers->end(); it++)
|
|
{
|
|
NewsServer* pNewsServer = *it;
|
|
if (pNewsServer->GetStateID() == i)
|
|
{
|
|
pServerVolume = pServerVolumes->at(pNewsServer->GetID());
|
|
}
|
|
}
|
|
}
|
|
|
|
int iFirstDay, iDataTime, iCustomTime;
|
|
unsigned long High1, Low1, High2 = 0, Low2 = 0;
|
|
if (iFormatVersion >= 3)
|
|
{
|
|
if (fscanf(infile, "%i,%i,%i\n", &iFirstDay, &iDataTime,&iCustomTime) != 3) goto error;
|
|
if (fscanf(infile, "%lu,%lu,%lu,%lu\n", &High1, &Low1, &High2, &Low2) != 4) goto error;
|
|
if (pServerVolume) pServerVolume->SetCustomTime((time_t)iCustomTime);
|
|
}
|
|
else
|
|
{
|
|
if (fscanf(infile, "%i,%i\n", &iFirstDay, &iDataTime) != 2) goto error;
|
|
if (fscanf(infile, "%lu,%lu\n", &High1, &Low1) != 2) goto error;
|
|
}
|
|
if (pServerVolume) pServerVolume->SetFirstDay(iFirstDay);
|
|
if (pServerVolume) pServerVolume->SetDataTime((time_t)iDataTime);
|
|
if (pServerVolume) pServerVolume->SetTotalBytes(Util::JoinInt64(High1, Low1));
|
|
if (pServerVolume) pServerVolume->SetCustomBytes(Util::JoinInt64(High2, Low2));
|
|
|
|
ServerVolume::VolumeArray* VolumeArrays[] = { pServerVolume ? pServerVolume->BytesPerSeconds() : NULL,
|
|
pServerVolume ? pServerVolume->BytesPerMinutes() : NULL,
|
|
pServerVolume ? pServerVolume->BytesPerHours() : NULL,
|
|
pServerVolume ? pServerVolume->BytesPerDays() : NULL };
|
|
for (int k=0; k < 4; k++)
|
|
{
|
|
ServerVolume::VolumeArray* pVolumeArray = VolumeArrays[k];
|
|
|
|
int iArrSize;
|
|
if (fscanf(infile, "%i\n", &iArrSize) != 1) goto error;
|
|
if (pVolumeArray) pVolumeArray->resize(iArrSize);
|
|
|
|
for (int j = 0; j < iArrSize; j++)
|
|
{
|
|
if (fscanf(infile, "%lu,%lu\n", &High1, &Low1) != 2) goto error;
|
|
if (pVolumeArray) (*pVolumeArray)[j] = Util::JoinInt64(High1, Low1);
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
|
|
error:
|
|
error("Error reading volume stats from disk");
|
|
|
|
return false;
|
|
}
|
|
|
|
void DiskState::WriteCacheFlag()
|
|
{
|
|
char szFlagFilename[1024];
|
|
snprintf(szFlagFilename, 1024, "%s%s", g_pOptions->GetQueueDir(), "acache");
|
|
szFlagFilename[1024-1] = '\0';
|
|
|
|
FILE* outfile = fopen(szFlagFilename, FOPEN_WB);
|
|
if (!outfile)
|
|
{
|
|
error("Error saving diskstate: Could not create file %s", szFlagFilename);
|
|
return;
|
|
}
|
|
|
|
fclose(outfile);
|
|
}
|
|
|
|
void DiskState::DeleteCacheFlag()
|
|
{
|
|
char szFlagFilename[1024];
|
|
snprintf(szFlagFilename, 1024, "%s%s", g_pOptions->GetQueueDir(), "acache");
|
|
szFlagFilename[1024-1] = '\0';
|
|
|
|
remove(szFlagFilename);
|
|
}
|
|
|
|
void DiskState::AppendNZBMessage(int iNZBID, Message::EKind eKind, const char* szText)
|
|
{
|
|
char szLogFilename[1024];
|
|
snprintf(szLogFilename, 1024, "%sn%i.log", g_pOptions->GetQueueDir(), iNZBID);
|
|
szLogFilename[1024-1] = '\0';
|
|
|
|
FILE* outfile = fopen(szLogFilename, FOPEN_ABP);
|
|
if (!outfile)
|
|
{
|
|
error("Error saving log: Could not create file %s", szLogFilename);
|
|
return;
|
|
}
|
|
|
|
const char* szMessageType[] = { "INFO", "WARNING", "ERROR", "DEBUG", "DETAIL"};
|
|
|
|
char tmp2[1024];
|
|
strncpy(tmp2, szText, 1024);
|
|
tmp2[1024-1] = '\0';
|
|
|
|
// replace bad chars
|
|
for (char* p = tmp2; *p; p++)
|
|
{
|
|
char ch = *p;
|
|
if (ch == '\n' || ch == '\r' || ch == '\t')
|
|
{
|
|
*p = ' ';
|
|
}
|
|
}
|
|
|
|
time_t tm = time(NULL);
|
|
time_t rawtime = tm + g_pOptions->GetTimeCorrection();
|
|
|
|
char szTime[50];
|
|
#ifdef HAVE_CTIME_R_3
|
|
ctime_r(&rawtime, szTime, 50);
|
|
#else
|
|
ctime_r(&rawtime, szTime);
|
|
#endif
|
|
szTime[50-1] = '\0';
|
|
szTime[strlen(szTime) - 1] = '\0'; // trim LF
|
|
|
|
fprintf(outfile, "%s\t%u\t%s\t%s%s", szTime, (int)tm, szMessageType[eKind], tmp2, LINE_ENDING);
|
|
|
|
fclose(outfile);
|
|
}
|
|
|
|
void DiskState::LoadNZBMessages(int iNZBID, MessageList* pMessages)
|
|
{
|
|
// Important:
|
|
// - Other threads may be writing into the log-file at any time;
|
|
// - The log-file may also be deleted from another thread;
|
|
|
|
char szLogFilename[1024];
|
|
snprintf(szLogFilename, 1024, "%sn%i.log", g_pOptions->GetQueueDir(), iNZBID);
|
|
szLogFilename[1024-1] = '\0';
|
|
|
|
if (!Util::FileExists(szLogFilename))
|
|
{
|
|
return;
|
|
}
|
|
|
|
FILE* infile = fopen(szLogFilename, FOPEN_RB);
|
|
if (!infile)
|
|
{
|
|
error("Error reading log: could not open file %s", szLogFilename);
|
|
return;
|
|
}
|
|
|
|
int iID = 0;
|
|
char szLine[1024];
|
|
while (fgets(szLine, sizeof(szLine), infile))
|
|
{
|
|
Util::TrimRight(szLine);
|
|
|
|
// time (skip formatted time first)
|
|
char* p = strchr(szLine, '\t');
|
|
if (!p) goto exit;
|
|
int iTime = atoi(p + 1);
|
|
|
|
// kind
|
|
p = strchr(p + 1, '\t');
|
|
if (!p) goto exit;
|
|
char* szKind = p + 1;
|
|
|
|
Message::EKind eKind = Message::mkError;
|
|
if (!strncmp(szKind, "INFO", 4))
|
|
{
|
|
eKind = Message::mkInfo;
|
|
}
|
|
else if (!strncmp(szKind, "WARNING", 7))
|
|
{
|
|
eKind = Message::mkWarning;
|
|
}
|
|
else if (!strncmp(szKind, "ERROR", 5))
|
|
{
|
|
eKind = Message::mkError;
|
|
}
|
|
else if (!strncmp(szKind, "DETAIL", 6))
|
|
{
|
|
eKind = Message::mkDetail;
|
|
}
|
|
else if (!strncmp(szKind, "DEBUG", 5))
|
|
{
|
|
eKind = Message::mkDebug;
|
|
}
|
|
|
|
// text
|
|
p = strchr(p + 1, '\t');
|
|
if (!p) goto exit;
|
|
char* szText = p + 1;
|
|
|
|
Message* pMessage = new Message(++iID, eKind, (time_t)iTime, szText);
|
|
pMessages->push_back(pMessage);
|
|
}
|
|
|
|
exit:
|
|
fclose(infile);
|
|
return;
|
|
}
|