180 lines
4.8 KiB
C++
180 lines
4.8 KiB
C++
/*
|
|
Copyright© 2021 John Sennesael
|
|
|
|
UsenetSearch is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
UsenetSearch 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 UsenetSearch. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <filesystem>
|
|
#include <fstream>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include "usenetsearch/StringUtils.h"
|
|
#include "usenetsearch/UsenetClient.h"
|
|
|
|
#include "usenetsearch/Database.h"
|
|
|
|
namespace usenetsearch {
|
|
|
|
Database::~Database()
|
|
{
|
|
if (m_newsGroupFileInput.is_open())
|
|
{
|
|
m_newsGroupFileInput.close();
|
|
}
|
|
if (m_newsGroupFileOutput.is_open())
|
|
{
|
|
m_newsGroupFileOutput.close();
|
|
}
|
|
}
|
|
|
|
std::filesystem::path Database::GetArticleFilePath(
|
|
const std::wstring& newsgroup)
|
|
{
|
|
const auto groupFile = StringHash(m_conv.to_bytes(newsgroup)) + ".db";
|
|
return m_databasePath / groupFile;
|
|
}
|
|
|
|
std::unique_ptr<std::vector<std::wstring>> Database::LoadArticleList(
|
|
const std::wstring& newsgroup)
|
|
{
|
|
const auto articleFile = GetArticleFilePath(newsgroup);
|
|
if (!std::filesystem::exists(articleFile))
|
|
{
|
|
throw DatabaseException(ENOTFOUND,
|
|
"No article list found for newsgroup " + m_conv.to_bytes(newsgroup)
|
|
);
|
|
}
|
|
std::ifstream io;
|
|
io.open(articleFile, std::ios::binary);
|
|
std::uint64_t articleCount;
|
|
io.read(
|
|
reinterpret_cast<char*>(&articleCount),
|
|
sizeof(articleCount)
|
|
);
|
|
auto result = std::make_unique<std::vector<std::wstring>>();
|
|
for (std::uint64_t i = 0; i != articleCount; ++i)
|
|
{
|
|
std::wstring articleId;
|
|
io >> articleId;
|
|
result->emplace_back(articleId);
|
|
}
|
|
io.close();
|
|
return result;
|
|
}
|
|
|
|
std::unique_ptr<std::vector<NntpListEntry>> Database::LoadNewsgroupList()
|
|
{
|
|
OpenNewsGroupFile();
|
|
|
|
std::uint64_t dbVersion{0};
|
|
m_newsGroupFileInput.read(
|
|
reinterpret_cast<char*>(&dbVersion),
|
|
sizeof(dbVersion)
|
|
);
|
|
if (dbVersion != m_databaseVersion)
|
|
{
|
|
throw DatabaseException(EINVAL,
|
|
"The loaded database version (" + std::to_string(dbVersion)
|
|
+ ") does not match the current database version ("
|
|
+ std::to_string(m_databaseVersion) + ")");
|
|
}
|
|
|
|
size_t newsGroupCount{0};
|
|
m_newsGroupFileInput.read(
|
|
reinterpret_cast<char*>(&newsGroupCount),
|
|
sizeof(newsGroupCount)
|
|
);
|
|
|
|
auto result = std::make_unique<std::vector<NntpListEntry>>();
|
|
for (size_t numLoaded = 0; numLoaded != newsGroupCount; ++numLoaded)
|
|
{
|
|
NntpListEntry entry;
|
|
m_newsGroupFileInput >> entry;
|
|
result->emplace_back(entry);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
void Database::Open(std::filesystem::path dbPath)
|
|
{
|
|
m_databasePath = dbPath;
|
|
if (!std::filesystem::exists(dbPath))
|
|
{
|
|
std::filesystem::create_directory(dbPath);
|
|
}
|
|
OpenNewsGroupFile();
|
|
}
|
|
|
|
void Database::OpenNewsGroupFile()
|
|
{
|
|
if (m_newsGroupFileInput.is_open() && m_newsGroupFileOutput.is_open())
|
|
{
|
|
return;
|
|
}
|
|
const std::filesystem::path newsGroupFilePath =
|
|
m_databasePath / "newsgroups.db";
|
|
if (!m_newsGroupFileInput.is_open())
|
|
{
|
|
m_newsGroupFileInput.open(newsGroupFilePath, std::ios::binary);
|
|
}
|
|
if (!m_newsGroupFileOutput.is_open())
|
|
{
|
|
m_newsGroupFileOutput.open(newsGroupFilePath, std::ios::binary);
|
|
}
|
|
}
|
|
|
|
void Database::UpdateArticleList(
|
|
const std::wstring& newsgroup,
|
|
const std::vector<std::wstring>& articleIds)
|
|
{
|
|
const auto articleFile = GetArticleFilePath(newsgroup);
|
|
std::ofstream io;
|
|
io.open(articleFile, std::ios::binary);
|
|
const std::uint64_t articleCount = articleIds.size();
|
|
io.write(
|
|
reinterpret_cast<const char*>(&articleCount),
|
|
sizeof(articleCount)
|
|
);
|
|
for (const auto& id: articleIds)
|
|
{
|
|
io << id;
|
|
}
|
|
io.close();
|
|
}
|
|
|
|
void Database::UpdateNewsgroupList(const std::vector<NntpListEntry>& list)
|
|
{
|
|
OpenNewsGroupFile();
|
|
|
|
m_newsGroupFileOutput.write(
|
|
reinterpret_cast<const char*>(&m_databaseVersion),
|
|
sizeof(m_databaseVersion)
|
|
);
|
|
|
|
const std::uint64_t newsGroupCount = list.size();
|
|
m_newsGroupFileOutput.write(
|
|
reinterpret_cast<const char*>(&newsGroupCount),
|
|
sizeof(newsGroupCount)
|
|
);
|
|
|
|
for (const auto& entry: list)
|
|
{
|
|
m_newsGroupFileOutput << entry;
|
|
}
|
|
m_newsGroupFileOutput.flush();
|
|
}
|
|
|
|
} // namespace usenetsearch
|