Actual search results! Safer cleanup when interrupted. Consistent include order. And a bunch of other polish
This commit is contained in:
parent
f7831afc9d
commit
6b5bdadeb5
|
@ -56,6 +56,8 @@ class Application
|
||||||
std::string m_configFile{"usenetsearch.conf"};
|
std::string m_configFile{"usenetsearch.conf"};
|
||||||
Database m_db;
|
Database m_db;
|
||||||
Filter m_filter;
|
Filter m_filter;
|
||||||
|
/// Holds a pointer to the singleton instance.
|
||||||
|
static std::unique_ptr<Application> m_instance;
|
||||||
|
|
||||||
void ExecuteCustomOption(
|
void ExecuteCustomOption(
|
||||||
std::shared_ptr<CommandLineOption>&,
|
std::shared_ptr<CommandLineOption>&,
|
||||||
|
@ -93,10 +95,15 @@ public:
|
||||||
std::string defaultValue = ""
|
std::string defaultValue = ""
|
||||||
);
|
);
|
||||||
bool CanRun() const;
|
bool CanRun() const;
|
||||||
|
/**
|
||||||
|
* Gets an instance to the application singleton.
|
||||||
|
*/
|
||||||
|
static Application& Get();
|
||||||
Configuration& GetConfig();
|
Configuration& GetConfig();
|
||||||
Database& GetDb();
|
Database& GetDb();
|
||||||
Filter& GetFilter();
|
Filter& GetFilter();
|
||||||
bool Init(int argc, char* argv[]);
|
bool Init(int argc, char* argv[]);
|
||||||
|
bool ShouldStop() const;
|
||||||
void Usage(const std::string& programName);
|
void Usage(const std::string& programName);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -27,8 +27,8 @@ namespace usenetsearch {
|
||||||
|
|
||||||
struct ConfigurationException: public UsenetSearchException
|
struct ConfigurationException: public UsenetSearchException
|
||||||
{
|
{
|
||||||
ConfigurationException(int errorCode, const std::string& message):
|
ConfigurationException(const std::string& message):
|
||||||
UsenetSearchException(errorCode, message){}
|
UsenetSearchException(message){}
|
||||||
|
|
||||||
virtual ~ConfigurationException() = default;
|
virtual ~ConfigurationException() = default;
|
||||||
};
|
};
|
||||||
|
|
|
@ -47,15 +47,14 @@ std::fstream& operator>>(std::fstream& in, ArticleEntry& obj);
|
||||||
|
|
||||||
struct DatabaseException: public UsenetSearchException
|
struct DatabaseException: public UsenetSearchException
|
||||||
{
|
{
|
||||||
DatabaseException(int errorCode, const std::string& message):
|
DatabaseException(const std::string& message):
|
||||||
UsenetSearchException(errorCode, message){}
|
UsenetSearchException(message){}
|
||||||
|
|
||||||
virtual ~DatabaseException() = default;
|
virtual ~DatabaseException() = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Database
|
class Database
|
||||||
{
|
{
|
||||||
Application& m_app;
|
|
||||||
std::filesystem::path m_databasePath;
|
std::filesystem::path m_databasePath;
|
||||||
std::uint64_t m_databaseVersion{DatabaseVersion};
|
std::uint64_t m_databaseVersion{DatabaseVersion};
|
||||||
std::vector<std::filesystem::path> m_lockedFiles;
|
std::vector<std::filesystem::path> m_lockedFiles;
|
||||||
|
@ -91,8 +90,9 @@ class Database
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
explicit Database(Application& app);
|
Database() = default;
|
||||||
~Database();
|
~Database() = default;
|
||||||
|
std::unique_ptr<NntpListEntry> FindNntpEntry(std::uint64_t id);
|
||||||
std::unique_ptr<NntpListEntry> FindNntpEntry(const std::string& subject);
|
std::unique_ptr<NntpListEntry> FindNntpEntry(const std::string& subject);
|
||||||
std::uint32_t GetLastIndexedArticle(std::uint64_t newsgroupID);
|
std::uint32_t GetLastIndexedArticle(std::uint64_t newsgroupID);
|
||||||
std::unique_ptr<std::vector<NntpListEntry>> LoadNewsgroupList();
|
std::unique_ptr<std::vector<NntpListEntry>> LoadNewsgroupList();
|
||||||
|
|
|
@ -29,8 +29,8 @@ namespace usenetsearch {
|
||||||
|
|
||||||
struct DnsResolveException: public UsenetSearchException
|
struct DnsResolveException: public UsenetSearchException
|
||||||
{
|
{
|
||||||
DnsResolveException(int errorCode, const std::string& message):
|
DnsResolveException(const std::string& message):
|
||||||
UsenetSearchException(errorCode, message){}
|
UsenetSearchException(message){}
|
||||||
|
|
||||||
virtual ~DnsResolveException() = default;
|
virtual ~DnsResolveException() = default;
|
||||||
};
|
};
|
||||||
|
|
|
@ -23,15 +23,12 @@ namespace usenetsearch {
|
||||||
|
|
||||||
class UsenetSearchException: public std::exception
|
class UsenetSearchException: public std::exception
|
||||||
{
|
{
|
||||||
int m_errorCode;
|
|
||||||
std::string m_message;
|
std::string m_message;
|
||||||
public:
|
public:
|
||||||
|
|
||||||
UsenetSearchException(int errorCode, const std::string& message);
|
UsenetSearchException(const std::string& message);
|
||||||
|
|
||||||
virtual ~UsenetSearchException() = default;
|
virtual ~UsenetSearchException() = default;
|
||||||
|
|
||||||
int Code() const;
|
|
||||||
|
|
||||||
virtual const char* what() const noexcept override;
|
virtual const char* what() const noexcept override;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "usenetsearch/Application.h"
|
#include "usenetsearch/Database.h"
|
||||||
#include "usenetsearch/Filter.h"
|
#include "usenetsearch/Filter.h"
|
||||||
#include "usenetsearch/UsenetClient.h"
|
#include "usenetsearch/UsenetClient.h"
|
||||||
#include "usenetsearch/ThreadPool.h"
|
#include "usenetsearch/ThreadPool.h"
|
||||||
|
@ -61,14 +61,13 @@ typedef std::vector<SearchResult> SearchResults;
|
||||||
|
|
||||||
class Indexer
|
class Indexer
|
||||||
{
|
{
|
||||||
Application& m_app;
|
|
||||||
UsenetClient& m_client;
|
UsenetClient& m_client;
|
||||||
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> m_conv;
|
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> m_conv;
|
||||||
ThreadPool m_threads;
|
ThreadPool m_threads;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
Indexer(Application& app, UsenetClient& client);
|
Indexer(UsenetClient& client);
|
||||||
|
|
||||||
void Connect();
|
void Connect();
|
||||||
void Index(const std::vector<NntpListEntry>& newsgroups);
|
void Index(const std::vector<NntpListEntry>& newsgroups);
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <mutex>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
namespace usenetsearch {
|
namespace usenetsearch {
|
||||||
|
@ -32,6 +33,8 @@ class Logger
|
||||||
|
|
||||||
/// Holds a pointer to the singleton instance.
|
/// Holds a pointer to the singleton instance.
|
||||||
static std::unique_ptr<Logger> m_instance;
|
static std::unique_ptr<Logger> m_instance;
|
||||||
|
std::mutex m_stdOutLock;
|
||||||
|
std::mutex m_stdErrLock;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
|
@ -47,28 +50,28 @@ public:
|
||||||
/**
|
/**
|
||||||
* Gets an instance to the logger.
|
* Gets an instance to the logger.
|
||||||
*/
|
*/
|
||||||
static Logger& get();
|
static Logger& Get();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Logs a debug message.
|
* Logs a debug message.
|
||||||
*
|
*
|
||||||
* @param[in] msg Message to be logged.
|
* @param[in] msg Message to be logged.
|
||||||
*/
|
*/
|
||||||
void debug(const std::string& msg) const;
|
void Debug(const std::string& func, const std::string& msg);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Logs an info message.
|
* Logs an info message.
|
||||||
*
|
*
|
||||||
* @param[in] msg Message to be logged.
|
* @param[in] msg Message to be logged.
|
||||||
*/
|
*/
|
||||||
void info(const std::string& msg) const;
|
void Info(const std::string& func, const std::string& msg);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Logs an error message.
|
* Logs an error message.
|
||||||
*
|
*
|
||||||
* @param[in] msg Message to be logged.
|
* @param[in] msg Message to be logged.
|
||||||
*/
|
*/
|
||||||
void error(const std::string& msg) const;
|
void Error(const std::string& func, const std::string& msg);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Logs an error message and throws.
|
* Logs an error message and throws.
|
||||||
|
@ -77,14 +80,18 @@ public:
|
||||||
* @param[in] msg Error message to be logged.
|
* @param[in] msg Error message to be logged.
|
||||||
*/
|
*/
|
||||||
template <typename EXCEPTION>
|
template <typename EXCEPTION>
|
||||||
inline void fatal(const std::string& msg) const
|
inline void Fatal(const std::string& func, const std::string& msg)
|
||||||
{
|
{
|
||||||
static_assert(
|
static_assert(
|
||||||
std::is_base_of<std::exception, EXCEPTION>::value,
|
std::is_base_of<std::exception, EXCEPTION>::value,
|
||||||
"EXCEPTION must be an std::exception or derrived child.");
|
"EXCEPTION must be an std::exception or derrived child.");
|
||||||
error(msg);
|
Error(func, msg);
|
||||||
throw EXCEPTION(msg);
|
throw EXCEPTION(msg);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
std::string LogName(const std::string& module, const std::string& func);
|
||||||
|
|
||||||
|
#define LOGID(module) LogName(module, __FUNCTION__)
|
||||||
|
|
||||||
} // namespace usenetsearch
|
} // namespace usenetsearch
|
||||||
|
|
|
@ -28,8 +28,8 @@ namespace usenetsearch {
|
||||||
|
|
||||||
struct SSLException: public UsenetSearchException
|
struct SSLException: public UsenetSearchException
|
||||||
{
|
{
|
||||||
SSLException(int errorCode, const std::string& message):
|
SSLException(const std::string& message):
|
||||||
UsenetSearchException(errorCode, message){}
|
UsenetSearchException(message){}
|
||||||
|
|
||||||
virtual ~SSLException() = default;
|
virtual ~SSLException() = default;
|
||||||
};
|
};
|
||||||
|
|
|
@ -33,16 +33,16 @@ struct NntpListEntry;
|
||||||
|
|
||||||
struct FileIOException: public UsenetSearchException
|
struct FileIOException: public UsenetSearchException
|
||||||
{
|
{
|
||||||
FileIOException(int errorCode, const std::string& message):
|
FileIOException(const std::string& message):
|
||||||
UsenetSearchException(errorCode, message){}
|
UsenetSearchException(message){}
|
||||||
|
|
||||||
virtual ~FileIOException() = default;
|
virtual ~FileIOException() = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SerializeException: public UsenetSearchException
|
struct SerializeException: public UsenetSearchException
|
||||||
{
|
{
|
||||||
SerializeException(int errorCode, const std::string& message):
|
SerializeException(const std::string& message):
|
||||||
UsenetSearchException(errorCode, message){}
|
UsenetSearchException(message){}
|
||||||
|
|
||||||
virtual ~SerializeException() = default;
|
virtual ~SerializeException() = default;
|
||||||
};
|
};
|
||||||
|
@ -125,5 +125,4 @@ SerializableFile& operator>>(SerializableFile& in, NntpHeader& obj);
|
||||||
SerializableFile& operator<<(SerializableFile& out, const NntpListEntry& obj);
|
SerializableFile& operator<<(SerializableFile& out, const NntpListEntry& obj);
|
||||||
SerializableFile& operator>>(SerializableFile& in, NntpListEntry& obj);
|
SerializableFile& operator>>(SerializableFile& in, NntpListEntry& obj);
|
||||||
|
|
||||||
|
|
||||||
} // namespace usenetsearch
|
} // namespace usenetsearch
|
||||||
|
|
|
@ -29,8 +29,8 @@ namespace usenetsearch {
|
||||||
|
|
||||||
struct StringException: public UsenetSearchException
|
struct StringException: public UsenetSearchException
|
||||||
{
|
{
|
||||||
StringException(int errorCode, const std::string& message):
|
StringException(const std::string& message):
|
||||||
UsenetSearchException(errorCode, message){}
|
UsenetSearchException(message){}
|
||||||
|
|
||||||
virtual ~StringException() = default;
|
virtual ~StringException() = default;
|
||||||
};
|
};
|
||||||
|
|
|
@ -27,8 +27,8 @@ namespace usenetsearch {
|
||||||
|
|
||||||
struct SocketException: public UsenetSearchException
|
struct SocketException: public UsenetSearchException
|
||||||
{
|
{
|
||||||
SocketException(int errorCode, const std::string& message):
|
SocketException(const std::string& message):
|
||||||
UsenetSearchException(errorCode, message){}
|
UsenetSearchException(message){}
|
||||||
|
|
||||||
virtual ~SocketException() = default;
|
virtual ~SocketException() = default;
|
||||||
};
|
};
|
||||||
|
|
|
@ -36,8 +36,8 @@ class Application;
|
||||||
|
|
||||||
struct UsenetClientException: public UsenetSearchException
|
struct UsenetClientException: public UsenetSearchException
|
||||||
{
|
{
|
||||||
UsenetClientException(int errorCode, const std::string& message):
|
UsenetClientException(const std::string& message):
|
||||||
UsenetSearchException(errorCode, message){}
|
UsenetSearchException(message){}
|
||||||
|
|
||||||
virtual ~UsenetClientException() = default;
|
virtual ~UsenetClientException() = default;
|
||||||
};
|
};
|
||||||
|
@ -77,7 +77,6 @@ struct NntpListEntry
|
||||||
class UsenetClient
|
class UsenetClient
|
||||||
{
|
{
|
||||||
|
|
||||||
Application& m_app;
|
|
||||||
std::unique_ptr<SSLConnection> m_ssl;
|
std::unique_ptr<SSLConnection> m_ssl;
|
||||||
std::unique_ptr<TcpConnection> m_tcp;
|
std::unique_ptr<TcpConnection> m_tcp;
|
||||||
bool m_useSSL{false};
|
bool m_useSSL{false};
|
||||||
|
@ -89,7 +88,7 @@ class UsenetClient
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
UsenetClient(Application& app);
|
UsenetClient() = default;
|
||||||
|
|
||||||
void Authenticate(const std::wstring& user, const std::wstring& password);
|
void Authenticate(const std::wstring& user, const std::wstring& password);
|
||||||
|
|
||||||
|
|
|
@ -17,8 +17,10 @@
|
||||||
|
|
||||||
#include "usenetsearch/Application.h"
|
#include "usenetsearch/Application.h"
|
||||||
|
|
||||||
|
#include "usenetsearch/Logger.h"
|
||||||
#include "usenetsearch/StringUtils.h"
|
#include "usenetsearch/StringUtils.h"
|
||||||
|
|
||||||
|
#include <csignal>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
@ -26,6 +28,15 @@
|
||||||
|
|
||||||
namespace usenetsearch {
|
namespace usenetsearch {
|
||||||
|
|
||||||
|
sig_atomic_t _signalled_to_stop{0};
|
||||||
|
|
||||||
|
void sigHandler(int)
|
||||||
|
{
|
||||||
|
_signalled_to_stop = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<Application> Application::m_instance;
|
||||||
|
|
||||||
void Application::AddBooleanOption(
|
void Application::AddBooleanOption(
|
||||||
char option,
|
char option,
|
||||||
const std::string& help,
|
const std::string& help,
|
||||||
|
@ -88,6 +99,25 @@ void Application::AddStringOption(
|
||||||
m_commandLineArguments.emplace_back(std::move(val));
|
m_commandLineArguments.emplace_back(std::move(val));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Application& Application::Get()
|
||||||
|
{
|
||||||
|
if (m_instance == nullptr)
|
||||||
|
{
|
||||||
|
m_instance = std::make_unique<Application>();
|
||||||
|
}
|
||||||
|
return *m_instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Application::ShouldStop() const
|
||||||
|
{
|
||||||
|
const bool result = _signalled_to_stop == 1;
|
||||||
|
if (result)
|
||||||
|
{
|
||||||
|
Logger::Get().Info(LOGID("Application"), "Been signalled to stop.");
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
void Application::Usage(const std::string& programName)
|
void Application::Usage(const std::string& programName)
|
||||||
{
|
{
|
||||||
std::cout << "UsenetSearch - usenet search indexer" << std::endl;
|
std::cout << "UsenetSearch - usenet search indexer" << std::endl;
|
||||||
|
@ -147,7 +177,7 @@ void Application::Usage(const std::string& programName)
|
||||||
std::cout << std::endl;
|
std::cout << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
Application::Application() : m_db(*this), m_filter(m_config)
|
Application::Application() : m_filter(m_config)
|
||||||
{
|
{
|
||||||
std::cout.setf(std::ios::unitbuf);
|
std::cout.setf(std::ios::unitbuf);
|
||||||
}
|
}
|
||||||
|
@ -249,6 +279,11 @@ bool Application::Init(int argc, char* argv[])
|
||||||
{
|
{
|
||||||
ParseArgs(argc, argv);
|
ParseArgs(argc, argv);
|
||||||
if (!m_canRun) return false;
|
if (!m_canRun) return false;
|
||||||
|
// Install signal handlers.
|
||||||
|
void (*prev_handler)(int);
|
||||||
|
prev_handler = signal(SIGINT, sigHandler);
|
||||||
|
prev_handler = signal(SIGTERM, sigHandler);
|
||||||
|
prev_handler = signal(SIGTSTP, sigHandler);
|
||||||
// Read config, setup db
|
// Read config, setup db
|
||||||
m_config.Open(m_configFile);
|
m_config.Open(m_configFile);
|
||||||
m_db.MaxTreeDepth(m_config.MaxTreeDepth());
|
m_db.MaxTreeDepth(m_config.MaxTreeDepth());
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
|
|
||||||
#include "usenetsearch/Configuration.h"
|
#include "usenetsearch/Configuration.h"
|
||||||
|
|
||||||
|
#include "usenetsearch/Logger.h"
|
||||||
#include "usenetsearch/ScopeExit.h"
|
#include "usenetsearch/ScopeExit.h"
|
||||||
#include "usenetsearch/StringUtils.h"
|
#include "usenetsearch/StringUtils.h"
|
||||||
|
|
||||||
|
@ -103,7 +104,8 @@ void Configuration::Open(const std::string& filename)
|
||||||
std::ifstream fin(filename.c_str());
|
std::ifstream fin(filename.c_str());
|
||||||
if (!fin.is_open())
|
if (!fin.is_open())
|
||||||
{
|
{
|
||||||
throw ConfigurationException(EINVAL,
|
Logger::Get().Fatal<ConfigurationException>(
|
||||||
|
LOGID("Configuration"),
|
||||||
"Could not open configuration file: " + filename
|
"Could not open configuration file: " + filename
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -121,7 +123,8 @@ void Configuration::Open(const std::string& filename)
|
||||||
const auto kvp = StringSplit(line, std::string{":"}, 2);
|
const auto kvp = StringSplit(line, std::string{":"}, 2);
|
||||||
if (kvp.size() != 2)
|
if (kvp.size() != 2)
|
||||||
{
|
{
|
||||||
throw ConfigurationException(EINVAL,
|
Logger::Get().Fatal<ConfigurationException>(
|
||||||
|
LOGID("Configuration"),
|
||||||
std::string("Invalid configuration in ")
|
std::string("Invalid configuration in ")
|
||||||
+ filename + std::string(" line ")
|
+ filename + std::string(" line ")
|
||||||
+ std::to_string(line_nr)
|
+ std::to_string(line_nr)
|
||||||
|
@ -158,7 +161,8 @@ void Configuration::Open(const std::string& filename)
|
||||||
}
|
}
|
||||||
catch (const std::regex_error& e)
|
catch (const std::regex_error& e)
|
||||||
{
|
{
|
||||||
throw ConfigurationException(EINVAL,
|
Logger::Get().Fatal<ConfigurationException>(
|
||||||
|
LOGID("Configuration"),
|
||||||
std::string("Invalid configuration in ")
|
std::string("Invalid configuration in ")
|
||||||
+ filename + std::string(" line ")
|
+ filename + std::string(" line ")
|
||||||
+ std::to_string(line_nr) + " : Regular expression \""
|
+ std::to_string(line_nr) + " : Regular expression \""
|
||||||
|
@ -175,7 +179,8 @@ void Configuration::Open(const std::string& filename)
|
||||||
}
|
}
|
||||||
catch (const std::regex_error& e)
|
catch (const std::regex_error& e)
|
||||||
{
|
{
|
||||||
throw ConfigurationException(EINVAL,
|
Logger::Get().Fatal<ConfigurationException>(
|
||||||
|
LOGID("Configuration"),
|
||||||
std::string("Invalid configuration in ")
|
std::string("Invalid configuration in ")
|
||||||
+ filename + std::string(" line ")
|
+ filename + std::string(" line ")
|
||||||
+ std::to_string(line_nr) + " : Regular expression \""
|
+ std::to_string(line_nr) + " : Regular expression \""
|
||||||
|
@ -228,7 +233,8 @@ void Configuration::Open(const std::string& filename)
|
||||||
catch (const StringException& e)
|
catch (const StringException& e)
|
||||||
{
|
{
|
||||||
fin.close();
|
fin.close();
|
||||||
throw ConfigurationException(EINVAL,
|
Logger::Get().Fatal<ConfigurationException>(
|
||||||
|
LOGID("Configuration"),
|
||||||
std::string("Invalid configuration in ")
|
std::string("Invalid configuration in ")
|
||||||
+ filename + std::string(" line ")
|
+ filename + std::string(" line ")
|
||||||
+ std::to_string(line_nr) + " - " + e.what()
|
+ std::to_string(line_nr) + " - " + e.what()
|
||||||
|
@ -241,7 +247,8 @@ void Configuration::Open(const std::string& filename)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw ConfigurationException(EINVAL,
|
Logger::Get().Fatal<ConfigurationException>(
|
||||||
|
LOGID("Configuration"),
|
||||||
std::string("Invalid configuration in ")
|
std::string("Invalid configuration in ")
|
||||||
+ filename + std::string(" line ")
|
+ filename + std::string(" line ")
|
||||||
+ std::to_string(line_nr)
|
+ std::to_string(line_nr)
|
||||||
|
|
|
@ -18,13 +18,13 @@
|
||||||
#include "usenetsearch/Database.h"
|
#include "usenetsearch/Database.h"
|
||||||
|
|
||||||
#include "usenetsearch/Application.h"
|
#include "usenetsearch/Application.h"
|
||||||
|
#include "usenetsearch/Logger.h"
|
||||||
#include "usenetsearch/StringUtils.h"
|
#include "usenetsearch/StringUtils.h"
|
||||||
#include "usenetsearch/UsenetClient.h"
|
|
||||||
#include "usenetsearch/ScopeExit.h"
|
#include "usenetsearch/ScopeExit.h"
|
||||||
#include "usenetsearch/Serialize.h"
|
#include "usenetsearch/Serialize.h"
|
||||||
|
#include "usenetsearch/UsenetClient.h"
|
||||||
|
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
#include <iostream>
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
@ -36,27 +36,42 @@ namespace usenetsearch {
|
||||||
|
|
||||||
// Database class --------------------------------------------------------------
|
// Database class --------------------------------------------------------------
|
||||||
|
|
||||||
Database::Database(Application& app): m_app(app)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
Database::~Database()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void Database::CheckDbVersion(const SerializableFile& f) const
|
void Database::CheckDbVersion(const SerializableFile& f) const
|
||||||
{
|
{
|
||||||
f.Seek(0);
|
f.Seek(0);
|
||||||
const std::uint64_t ver = f.ReadInt64();
|
const std::uint64_t ver = f.ReadInt64();
|
||||||
if (ver != m_databaseVersion)
|
if (ver != m_databaseVersion)
|
||||||
{
|
{
|
||||||
throw DatabaseException(EINVAL,
|
Logger::Get().Fatal<DatabaseException>(
|
||||||
|
LOGID("Database"),
|
||||||
"Wrong database version - Got: " + std::to_string(ver) + " want: "
|
"Wrong database version - Got: " + std::to_string(ver) + " want: "
|
||||||
+ std::to_string(m_databaseVersion)
|
+ std::to_string(m_databaseVersion)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<NntpListEntry> Database::FindNntpEntry(std::uint64_t id)
|
||||||
|
{
|
||||||
|
const auto path = GetNewsGroupFilePath();
|
||||||
|
if (!std::filesystem::exists(path)) return nullptr;
|
||||||
|
SerializableFile io;
|
||||||
|
io.Open(path);
|
||||||
|
CheckDbVersion(io);
|
||||||
|
const std::uint64_t numGroups = io.ReadInt64();
|
||||||
|
std::unique_ptr<NntpListEntry> result = nullptr;
|
||||||
|
for (std::uint64_t n = 0; n != numGroups; ++n)
|
||||||
|
{
|
||||||
|
NntpListEntry entry;
|
||||||
|
io >> entry;
|
||||||
|
if (entry.id == id)
|
||||||
|
{
|
||||||
|
result = std::make_unique<NntpListEntry>(entry);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
std::unique_ptr<NntpListEntry> Database::FindNntpEntry(
|
std::unique_ptr<NntpListEntry> Database::FindNntpEntry(
|
||||||
const std::string& subject)
|
const std::string& subject)
|
||||||
{
|
{
|
||||||
|
@ -85,8 +100,11 @@ std::uint32_t Database::GetLastIndexedArticle(std::uint64_t newsgroupID)
|
||||||
const auto path = GetNewsGroupFilePath();
|
const auto path = GetNewsGroupFilePath();
|
||||||
if (!std::filesystem::exists(path))
|
if (!std::filesystem::exists(path))
|
||||||
{
|
{
|
||||||
throw DatabaseException(ENOTFOUND, "No indexed articles for newsgroup: "
|
Logger::Get().Fatal<DatabaseException>(
|
||||||
+ std::to_string(newsgroupID));
|
LOGID("Database"),
|
||||||
|
"No indexed articles for newsgroup: "
|
||||||
|
+ std::to_string(newsgroupID)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
SerializableFile io;
|
SerializableFile io;
|
||||||
io.Open(path);
|
io.Open(path);
|
||||||
|
@ -101,8 +119,11 @@ std::uint32_t Database::GetLastIndexedArticle(std::uint64_t newsgroupID)
|
||||||
return entry.lastIndexedArticle;
|
return entry.lastIndexedArticle;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw DatabaseException(ENOTFOUND, "No indexed articles for newsgroup: "
|
Logger::Get().Fatal<DatabaseException>(
|
||||||
+ std::to_string(newsgroupID));
|
LOGID("Database"),
|
||||||
|
"No indexed articles for newsgroup: " + std::to_string(newsgroupID)
|
||||||
|
);
|
||||||
|
return NntpListEntry::NOT_INDEXED;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::filesystem::path Database::GetTokenFilePath(
|
std::filesystem::path Database::GetTokenFilePath(
|
||||||
|
@ -185,8 +206,8 @@ void Database::ParseTokenFile(
|
||||||
{
|
{
|
||||||
if (!std::filesystem::exists(dbFile))
|
if (!std::filesystem::exists(dbFile))
|
||||||
{
|
{
|
||||||
throw DatabaseException(
|
Logger::Get().Fatal<DatabaseException>(
|
||||||
ENOTFOUND,
|
LOGID("Database"),
|
||||||
"File does not exist: " + dbFile.string()
|
"File does not exist: " + dbFile.string()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -195,6 +216,7 @@ void Database::ParseTokenFile(
|
||||||
const std::uint64_t tokenCount = io.ReadInt64();
|
const std::uint64_t tokenCount = io.ReadInt64();
|
||||||
for (std::uint64_t i = 0; i != tokenCount; ++i)
|
for (std::uint64_t i = 0; i != tokenCount; ++i)
|
||||||
{
|
{
|
||||||
|
if (Application::Get().ShouldStop()) return;
|
||||||
ArticleEntry token;
|
ArticleEntry token;
|
||||||
io >> token;
|
io >> token;
|
||||||
onParse(token);
|
onParse(token);
|
||||||
|
@ -221,9 +243,11 @@ void Database::SetLastIndexedArticle(
|
||||||
}
|
}
|
||||||
if (!found)
|
if (!found)
|
||||||
{
|
{
|
||||||
throw DatabaseException(EINVAL,
|
Logger::Get().Fatal<DatabaseException>(
|
||||||
|
LOGID("Database"),
|
||||||
"Attempt to update newsgroup not found in database - id: "
|
"Attempt to update newsgroup not found in database - id: "
|
||||||
+ std::to_string(newsgroupID));
|
+ std::to_string(newsgroupID)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
UpdateNewsgroupList(*outItems);
|
UpdateNewsgroupList(*outItems);
|
||||||
}
|
}
|
||||||
|
@ -239,7 +263,7 @@ void Database::SaveSearchTokens(
|
||||||
" ",
|
" ",
|
||||||
m_maxTreeDepth,
|
m_maxTreeDepth,
|
||||||
[&](const std::string& subToken, const std::string& str){
|
[&](const std::string& subToken, const std::string& str){
|
||||||
const std::string tok = m_app.GetFilter().ProcessToken(
|
const std::string tok = Application::Get().GetFilter().ProcessToken(
|
||||||
subToken,
|
subToken,
|
||||||
str
|
str
|
||||||
);
|
);
|
||||||
|
@ -322,11 +346,6 @@ void Database::SaveToken(
|
||||||
io << std::uint64_t{1};
|
io << std::uint64_t{1};
|
||||||
}
|
}
|
||||||
// write out token.
|
// write out token.
|
||||||
#if 0
|
|
||||||
std::cout << "Token: " << subtoken << std::endl;
|
|
||||||
std::cout << "Saving into file: " << path << std::endl;
|
|
||||||
std::cout << "Token hash: " << HashBytesToString(token.hash) << std::endl << std::endl;
|
|
||||||
#endif
|
|
||||||
io << token;
|
io << token;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -352,15 +371,6 @@ std::unique_ptr<std::vector<ArticleEntry>> Database::Search(
|
||||||
const auto foundTokens = LoadTokens(path, searchToken);
|
const auto foundTokens = LoadTokens(path, searchToken);
|
||||||
if (foundTokens->empty()) continue;
|
if (foundTokens->empty()) continue;
|
||||||
result->insert(result->end(), foundTokens->begin(), foundTokens->end());
|
result->insert(result->end(), foundTokens->begin(), foundTokens->end());
|
||||||
std::cout << std::left << std::setw(searchString.length() + 7)
|
|
||||||
<< "token: " + searchToken
|
|
||||||
<< std::setw(3) << " | "
|
|
||||||
<< std::setw(10)
|
|
||||||
<< "db file: " << path.string()
|
|
||||||
<< std::setw(3) << " | "
|
|
||||||
<< std::setw(9)
|
|
||||||
<< "#results: " + std::to_string(foundTokens->size())
|
|
||||||
<< std::endl;
|
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,8 @@
|
||||||
|
|
||||||
#include "usenetsearch/Dns.h"
|
#include "usenetsearch/Dns.h"
|
||||||
|
|
||||||
|
#include "usenetsearch/Logger.h"
|
||||||
|
|
||||||
#include <cerrno>
|
#include <cerrno>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
|
@ -64,7 +66,8 @@ std::vector<struct addrinfo> DnsResolve(
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw DnsResolveException(getAddrInfoResult,
|
Logger::Get().Fatal<DnsResolveException>(
|
||||||
|
LOGID("Dns"),
|
||||||
"Could not resolve host " + host + ": - Error ("
|
"Could not resolve host " + host + ": - Error ("
|
||||||
+ std::to_string(getAddrInfoResult) + ") - "
|
+ std::to_string(getAddrInfoResult) + ") - "
|
||||||
+ gai_strerror(getAddrInfoResult)
|
+ gai_strerror(getAddrInfoResult)
|
||||||
|
@ -73,7 +76,8 @@ std::vector<struct addrinfo> DnsResolve(
|
||||||
}
|
}
|
||||||
if (result == nullptr)
|
if (result == nullptr)
|
||||||
{
|
{
|
||||||
throw DnsResolveException(ETIMEDOUT,
|
Logger::Get().Fatal<DnsResolveException>(
|
||||||
|
LOGID("Dns"),
|
||||||
"Timed out trying to resolve host: " + host
|
"Timed out trying to resolve host: " + host
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,17 +19,11 @@
|
||||||
|
|
||||||
namespace usenetsearch {
|
namespace usenetsearch {
|
||||||
|
|
||||||
UsenetSearchException::UsenetSearchException(int errorCode, const std::string& message)
|
UsenetSearchException::UsenetSearchException(const std::string& message)
|
||||||
{
|
{
|
||||||
m_errorCode = errorCode;
|
|
||||||
m_message = message;
|
m_message = message;
|
||||||
}
|
}
|
||||||
|
|
||||||
int UsenetSearchException::Code() const
|
|
||||||
{
|
|
||||||
return m_errorCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* UsenetSearchException::what() const noexcept
|
const char* UsenetSearchException::what() const noexcept
|
||||||
{
|
{
|
||||||
return m_message.c_str();
|
return m_message.c_str();
|
||||||
|
|
|
@ -36,7 +36,7 @@ void Filter::Init()
|
||||||
// Pre-compile regexes for all the subtokens that should be erased.
|
// Pre-compile regexes for all the subtokens that should be erased.
|
||||||
std::for_each(eraseTokens.begin(), eraseTokens.end(),
|
std::for_each(eraseTokens.begin(), eraseTokens.end(),
|
||||||
[&](const std::string& tok){
|
[&](const std::string& tok){
|
||||||
const std::wstring wtok = WideStringFromString(tok);
|
const std::wstring wtok = StringToLower(WideStringFromString(tok));
|
||||||
m_eraseTokenRegexes.emplace(
|
m_eraseTokenRegexes.emplace(
|
||||||
std::make_unique<std::wregex>(L"^" + wtok + L"\\s+"),
|
std::make_unique<std::wregex>(L"^" + wtok + L"\\s+"),
|
||||||
std::wstring{}
|
std::wstring{}
|
||||||
|
@ -80,17 +80,15 @@ bool Filter::ProcessNewsgroup(const std::string& newsgroup) const
|
||||||
|
|
||||||
std::string Filter::ProcessSearchString(const std::string& searchString) const
|
std::string Filter::ProcessSearchString(const std::string& searchString) const
|
||||||
{
|
{
|
||||||
std::wstring str = WideStringFromString(searchString);
|
std::wstring str = StringToLower(WideStringFromString(searchString));
|
||||||
std::remove_if(str.begin(), str.end(), [](wchar_t c){
|
std::remove_if(str.begin(), str.end(), [](wchar_t c){
|
||||||
// Remove control characters.
|
// Remove control characters.
|
||||||
if (c < 0x20) return true; // ascii control chars
|
if (c < 0x20) return true; // ascii control chars
|
||||||
if ((c > 0x7e) && (c < 0xa0)) return true; // utf8 control chars
|
if ((c > 0x7e) && (c < 0xa0)) return true; // utf8 control chars
|
||||||
return false; // don't delete anything else
|
return false; // don't delete anything else
|
||||||
});
|
});
|
||||||
// Remove Re: for obvious reasons
|
|
||||||
str = StringRemove(StringToLower(str), std::wstring{L"re:"});
|
|
||||||
// Remove punctuation and stuff by converting to whitespace
|
// Remove punctuation and stuff by converting to whitespace
|
||||||
static std::wregex rxPunctuation(L"[\\.!?#$%^&~*\\(\\)\\+\\[\\]\"\\-<>]+");
|
static std::wregex rxPunctuation(L"[\\.!?#$%^&~*\\(\\)\\[\\]\"\\-<>]+");
|
||||||
str = std::regex_replace(str, rxPunctuation, L" ");
|
str = std::regex_replace(str, rxPunctuation, L" ");
|
||||||
// Process erase subtoken list.
|
// Process erase subtoken list.
|
||||||
std::for_each(m_eraseTokenRegexes.begin(), m_eraseTokenRegexes.end(),
|
std::for_each(m_eraseTokenRegexes.begin(), m_eraseTokenRegexes.end(),
|
||||||
|
|
|
@ -17,11 +17,10 @@
|
||||||
|
|
||||||
#include "usenetsearch/Indexer.h"
|
#include "usenetsearch/Indexer.h"
|
||||||
|
|
||||||
|
#include "usenetsearch/Application.h"
|
||||||
#include "usenetsearch/Logger.h"
|
#include "usenetsearch/Logger.h"
|
||||||
#include "usenetsearch/StringUtils.h"
|
#include "usenetsearch/StringUtils.h"
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
namespace usenetsearch {
|
namespace usenetsearch {
|
||||||
|
|
||||||
// SearchResult class ----------------------------------------------------------
|
// SearchResult class ----------------------------------------------------------
|
||||||
|
@ -110,48 +109,46 @@ bool SearchResult::operator<=(const SearchResult& other) const
|
||||||
|
|
||||||
// Indexer class ---------------------------------------------------------------
|
// Indexer class ---------------------------------------------------------------
|
||||||
|
|
||||||
Indexer::Indexer(Application& app, UsenetClient& client)
|
Indexer::Indexer(UsenetClient& client)
|
||||||
: m_app(app), m_client(client)
|
: m_client(client)
|
||||||
{
|
{
|
||||||
m_threads.MaxThreads(m_app.GetConfig().MaxThreads());
|
m_threads.MaxThreads(Application::Get().GetConfig().MaxThreads());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Indexer::Connect()
|
void Indexer::Connect()
|
||||||
{
|
{
|
||||||
m_client.Connect(
|
m_client.Connect(
|
||||||
m_app.GetConfig().NNTPServerHost(),
|
Application::Get().GetConfig().NNTPServerHost(),
|
||||||
m_app.GetConfig().NNTPServerPort(),
|
Application::Get().GetConfig().NNTPServerPort(),
|
||||||
m_app.GetConfig().NNTPServerSSL()
|
Application::Get().GetConfig().NNTPServerSSL()
|
||||||
);
|
);
|
||||||
m_client.Authenticate(
|
m_client.Authenticate(
|
||||||
m_conv.from_bytes(m_app.GetConfig().NNTPServerUser()),
|
m_conv.from_bytes(Application::Get().GetConfig().NNTPServerUser()),
|
||||||
m_conv.from_bytes(m_app.GetConfig().NNTPServerPassword())
|
m_conv.from_bytes(Application::Get().GetConfig().NNTPServerPassword())
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Indexer::Index(const std::vector<NntpListEntry>& newsgroups)
|
void Indexer::Index(const std::vector<NntpListEntry>& newsgroups)
|
||||||
{
|
{
|
||||||
/**
|
const size_t batchSize = Application::Get().GetConfig().BatchSize();
|
||||||
* @todo Replace all stdout stuff with Logger class.
|
|
||||||
*/
|
|
||||||
const size_t batchSize = m_app.GetConfig().BatchSize();
|
|
||||||
for (const auto& group: newsgroups)
|
for (const auto& group: newsgroups)
|
||||||
{
|
{
|
||||||
const std::wstring newsgroup = m_conv.from_bytes(group.name);
|
const std::wstring newsgroup = m_conv.from_bytes(group.name);
|
||||||
std::cout << "Setting group to " << group.name << "...";
|
Logger::Get().Debug(LOGID("Indexer"), "Setting group to " + group.name);
|
||||||
std::cout.flush();
|
|
||||||
m_client.Group(newsgroup);
|
m_client.Group(newsgroup);
|
||||||
std::cout << "DONE." << std::endl;
|
Logger::Get().Debug(
|
||||||
std::cout << "Reading headers in " << group.name << " "
|
LOGID("Indexer"),
|
||||||
<< "(.=" << batchSize << " headers)." << std::endl;
|
"Reading headers in " + group.name + " "
|
||||||
std::cout.flush();
|
+ "(batch size = " + std::to_string(batchSize) + " headers)."
|
||||||
|
);
|
||||||
std::atomic<std::uint64_t> headerCount{0};
|
std::atomic<std::uint64_t> headerCount{0};
|
||||||
const std::atomic<std::uint64_t> groupID = group.id;
|
const std::atomic<std::uint64_t> groupID = group.id;
|
||||||
std::reference_wrapper<Database> dbref = std::ref(m_app.GetDb());
|
|
||||||
std::uint32_t startMessage = 0;
|
std::uint32_t startMessage = 0;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
startMessage = dbref.get().GetLastIndexedArticle(groupID);
|
startMessage = Application::Get().GetDb().GetLastIndexedArticle(
|
||||||
|
groupID
|
||||||
|
);
|
||||||
if (startMessage == NntpListEntry::NOT_INDEXED)
|
if (startMessage == NntpListEntry::NOT_INDEXED)
|
||||||
{
|
{
|
||||||
startMessage = 0;
|
startMessage = 0;
|
||||||
|
@ -165,22 +162,27 @@ void Indexer::Index(const std::vector<NntpListEntry>& newsgroups)
|
||||||
{
|
{
|
||||||
startMessage = 0;
|
startMessage = 0;
|
||||||
}
|
}
|
||||||
|
Logger::Get().Debug(
|
||||||
std::cout << "Indexing starting at message: "
|
LOGID("Indexer"),
|
||||||
<< std::to_string(startMessage) << std::endl;
|
"Indexing starting at message: "
|
||||||
|
+ std::to_string(startMessage)
|
||||||
|
);
|
||||||
|
if (Application::Get().ShouldStop()) return;
|
||||||
m_client.ProcessHeaders(startMessage,
|
m_client.ProcessHeaders(startMessage,
|
||||||
[this, &startMessage, &headerCount, &dbref, &groupID](std::shared_ptr<NntpHeaders> headers){
|
[this, &startMessage, &headerCount, &groupID](std::shared_ptr<NntpHeaders> headers){
|
||||||
m_threads.Queue([this, headers, &startMessage, &headerCount, &dbref, &groupID](){
|
if (Application::Get().ShouldStop()) return;
|
||||||
|
m_threads.Queue([this, headers, &startMessage, &headerCount, &groupID](){
|
||||||
std::uint64_t lastArticle{0};
|
std::uint64_t lastArticle{0};
|
||||||
for (const auto& header: *headers)
|
for (const auto& header: *headers)
|
||||||
{
|
{
|
||||||
|
if (Application::Get().ShouldStop()) return;
|
||||||
const std::uint64_t articleID{header.articleID};
|
const std::uint64_t articleID{header.articleID};
|
||||||
std::string subject = header.subject;
|
std::string subject = header.subject;
|
||||||
subject = m_app.GetFilter().ProcessSearchString(
|
subject = Application::Get().GetFilter().ProcessSearchString(
|
||||||
subject
|
subject
|
||||||
);
|
);
|
||||||
if (subject == "") continue;
|
if (subject == "") continue;
|
||||||
dbref.get().SaveSearchTokens(
|
Application::Get().GetDb().SaveSearchTokens(
|
||||||
groupID,
|
groupID,
|
||||||
articleID,
|
articleID,
|
||||||
subject
|
subject
|
||||||
|
@ -195,20 +197,20 @@ void Indexer::Index(const std::vector<NntpListEntry>& newsgroups)
|
||||||
// Update last-indexed id for the newsgroup.
|
// Update last-indexed id for the newsgroup.
|
||||||
if (startMessage < lastArticle)
|
if (startMessage < lastArticle)
|
||||||
{
|
{
|
||||||
dbref.get().SetLastIndexedArticle(
|
Application::Get().GetDb().SetLastIndexedArticle(
|
||||||
groupID, lastArticle
|
groupID, lastArticle
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
std::cout << ".";
|
Logger::Get().Debug("Indexer::Index", "Finished batch.");
|
||||||
std::cout.flush();
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
batchSize
|
batchSize
|
||||||
);
|
);
|
||||||
m_threads.JoinThreads();
|
m_threads.JoinThreads();
|
||||||
std::cout << "DONE." << std::endl;
|
Logger::Get().Debug(
|
||||||
std::cout << "Saved " << headerCount << " headers." << std::endl;
|
LOGID("Indexer"),
|
||||||
std::cout.flush();
|
"Saved " + std::to_string(headerCount) + " headers."
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -216,13 +218,21 @@ std::unique_ptr<SearchResults> Indexer::Search(
|
||||||
const std::string& searchString)
|
const std::string& searchString)
|
||||||
{
|
{
|
||||||
auto result = std::make_unique<SearchResults>();
|
auto result = std::make_unique<SearchResults>();
|
||||||
const std::string sstr = m_app.GetFilter().ProcessSearchString(
|
const std::string sstr = Application::Get().GetFilter().ProcessSearchString(
|
||||||
searchString
|
searchString
|
||||||
);
|
);
|
||||||
auto searchResults = m_app.GetDb().Search(sstr);
|
const auto searchHash = StringHashBytes(sstr);
|
||||||
|
auto searchResults = Application::Get().GetDb().Search(sstr);
|
||||||
if (!searchResults) return result;
|
if (!searchResults) return result;
|
||||||
for(const ArticleEntry& entry: *searchResults)
|
for(const ArticleEntry& entry: *searchResults)
|
||||||
{
|
{
|
||||||
|
if (Application::Get().ShouldStop())
|
||||||
|
{
|
||||||
|
Logger::Get().Fatal<UsenetSearchException>(
|
||||||
|
"Indexer",
|
||||||
|
"Interrupted."
|
||||||
|
);
|
||||||
|
}
|
||||||
SearchResult sr(entry);
|
SearchResult sr(entry);
|
||||||
// Check if a matching entry already exists in the result set, if so,
|
// Check if a matching entry already exists in the result set, if so,
|
||||||
// increment count. Otherwise, append a new entry.
|
// increment count. Otherwise, append a new entry.
|
||||||
|
@ -230,6 +240,9 @@ std::unique_ptr<SearchResults> Indexer::Search(
|
||||||
if (it != result->end())
|
if (it != result->end())
|
||||||
{
|
{
|
||||||
(*it).Inc();
|
(*it).Inc();
|
||||||
|
// An exact match gets double points to ensure it's above other
|
||||||
|
// partial matches.
|
||||||
|
if (entry.hash == searchHash) (*it).Inc();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
namespace usenetsearch {
|
namespace usenetsearch {
|
||||||
|
|
||||||
|
@ -27,7 +28,7 @@ std::unique_ptr<Logger> Logger::m_instance;
|
||||||
|
|
||||||
Logger::Logger() {}
|
Logger::Logger() {}
|
||||||
|
|
||||||
Logger& Logger::get()
|
Logger& Logger::Get()
|
||||||
{
|
{
|
||||||
if (m_instance == nullptr)
|
if (m_instance == nullptr)
|
||||||
{
|
{
|
||||||
|
@ -36,19 +37,27 @@ Logger& Logger::get()
|
||||||
return *m_instance;
|
return *m_instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Logger::debug(const std::string& msg) const
|
void Logger::Debug(const std::string& func, const std::string& msg)
|
||||||
{
|
{
|
||||||
std::cout << "[debug] " << msg << std::endl << std::flush;
|
std::lock_guard<std::mutex> lock(m_stdOutLock);
|
||||||
|
std::cout << "[debug] [" + func + "] " << msg << std::endl << std::flush;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Logger::info(const std::string& msg) const
|
void Logger::Info(const std::string& func, const std::string& msg)
|
||||||
{
|
{
|
||||||
std::cout << "[info] " << msg << std::endl << std::flush;
|
std::lock_guard<std::mutex> lock(m_stdOutLock);
|
||||||
|
std::cout << "[info] [" + func + "] " << msg << std::endl << std::flush;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Logger::error(const std::string& msg) const
|
void Logger::Error(const std::string& func, const std::string& msg)
|
||||||
{
|
{
|
||||||
std::cerr << "[error] " << msg << std::endl << std::flush;
|
std::lock_guard<std::mutex> lock(m_stdErrLock);
|
||||||
|
std::cerr << "[error] [" + func + "] " << msg << std::endl << std::flush;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string LogName(const std::string& module, const std::string& func)
|
||||||
|
{
|
||||||
|
return module + std::string{"::"} + func;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace usenetsearch
|
} // namespace usenetsearch
|
||||||
|
|
|
@ -17,6 +17,8 @@
|
||||||
|
|
||||||
#include "usenetsearch/SSLConnection.h"
|
#include "usenetsearch/SSLConnection.h"
|
||||||
|
|
||||||
|
#include "usenetsearch/Logger.h"
|
||||||
|
|
||||||
#include <openssl/ssl.h>
|
#include <openssl/ssl.h>
|
||||||
#include <openssl/err.h>
|
#include <openssl/err.h>
|
||||||
|
|
||||||
|
@ -38,8 +40,10 @@ SSLConnection::SSLReturnState SSLConnection::CheckSSLReturn(int ret)
|
||||||
if (result == SSL_ERROR_SYSCALL)
|
if (result == SSL_ERROR_SYSCALL)
|
||||||
{
|
{
|
||||||
if (errno == 0) return SSLReturnState::SUCCESS;
|
if (errno == 0) return SSLReturnState::SUCCESS;
|
||||||
throw SSLException(errno, std::string{"SSL error: "}
|
Logger::Get().Fatal<SSLException>(
|
||||||
+ std::strerror(errno));
|
LOGID("SSLConnection"),
|
||||||
|
std::string{"SSL error: "} + std::strerror(errno)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (result == SSL_ERROR_SSL)
|
if (result == SSL_ERROR_SSL)
|
||||||
{
|
{
|
||||||
|
@ -48,8 +52,10 @@ SSLConnection::SSLReturnState SSLConnection::CheckSSLReturn(int ret)
|
||||||
errorCode,
|
errorCode,
|
||||||
nullptr
|
nullptr
|
||||||
);
|
);
|
||||||
throw SSLException(errorCode,
|
Logger::Get().Fatal<SSLException>(
|
||||||
"SSL error: " + errorString);
|
LOGID("SSLConnection"),
|
||||||
|
"SSL error: " + errorString
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return SSLReturnState::RETRY;
|
return SSLReturnState::RETRY;
|
||||||
}
|
}
|
||||||
|
@ -58,14 +64,16 @@ void SSLConnection::Connect()
|
||||||
{
|
{
|
||||||
if (m_tcpConnection == nullptr)
|
if (m_tcpConnection == nullptr)
|
||||||
{
|
{
|
||||||
throw SSLException(EBADF,
|
Logger::Get().Fatal<SSLException>(
|
||||||
|
LOGID("SSLConnection"),
|
||||||
"Null tcp connection when attempting ssl connect."
|
"Null tcp connection when attempting ssl connect."
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
int fd = m_tcpConnection->FileDescriptor();
|
int fd = m_tcpConnection->FileDescriptor();
|
||||||
if (!fd)
|
if (!fd)
|
||||||
{
|
{
|
||||||
throw SSLException(EBADF,
|
Logger::Get().Fatal<SSLException>(
|
||||||
|
LOGID("SSLConnection"),
|
||||||
"Bad file descriptor (" + std::to_string(fd)
|
"Bad file descriptor (" + std::to_string(fd)
|
||||||
+ ") when attempting to ssl connect."
|
+ ") when attempting to ssl connect."
|
||||||
);
|
);
|
||||||
|
@ -93,8 +101,8 @@ void SSLConnection::Connect()
|
||||||
);
|
);
|
||||||
if (timeDelta > m_connectionTimeout)
|
if (timeDelta > m_connectionTimeout)
|
||||||
{
|
{
|
||||||
throw SSLException(
|
Logger::Get().Fatal<SSLException>(
|
||||||
ETIMEDOUT,
|
LOGID("SSLConnection"),
|
||||||
"Timed out while trying to establish SSL connection."
|
"Timed out while trying to establish SSL connection."
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -115,7 +123,8 @@ std::string SSLConnection::Read(size_t amount)
|
||||||
{
|
{
|
||||||
if (m_sslContext == nullptr)
|
if (m_sslContext == nullptr)
|
||||||
{
|
{
|
||||||
throw SSLException(EBADF,
|
Logger::Get().Fatal<SSLException>(
|
||||||
|
LOGID("SSLConnection"),
|
||||||
"Attempted to write over SSL socket without SSL context."
|
"Attempted to write over SSL socket without SSL context."
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -155,7 +164,8 @@ void SSLConnection::Write(const std::string& data)
|
||||||
{
|
{
|
||||||
if (m_sslContext == nullptr)
|
if (m_sslContext == nullptr)
|
||||||
{
|
{
|
||||||
throw SSLException(EBADF,
|
Logger::Get().Fatal<SSLException>(
|
||||||
|
LOGID("SSLConnection"),
|
||||||
"Attempted to write over SSL socket without SSL context."
|
"Attempted to write over SSL socket without SSL context."
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -184,8 +194,8 @@ void SSLConnection::Write(const std::string& data)
|
||||||
(timeDelta > m_ioTimeout)
|
(timeDelta > m_ioTimeout)
|
||||||
|| (m_ioTimeout == std::chrono::milliseconds{0}) )
|
|| (m_ioTimeout == std::chrono::milliseconds{0}) )
|
||||||
{
|
{
|
||||||
throw SSLException(
|
Logger::Get().Fatal<SSLException>(
|
||||||
ETIMEDOUT,
|
LOGID("SSLConnection"),
|
||||||
"Timed out while trying to write to SSL connection."
|
"Timed out while trying to write to SSL connection."
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,9 @@
|
||||||
|
|
||||||
#include "usenetsearch/Serialize.h"
|
#include "usenetsearch/Serialize.h"
|
||||||
|
|
||||||
|
#include "usenetsearch/Application.h"
|
||||||
#include "usenetsearch/Database.h"
|
#include "usenetsearch/Database.h"
|
||||||
|
#include "usenetsearch/Logger.h"
|
||||||
#include "usenetsearch/ScopeExit.h"
|
#include "usenetsearch/ScopeExit.h"
|
||||||
#include "usenetsearch/UsenetClient.h"
|
#include "usenetsearch/UsenetClient.h"
|
||||||
|
|
||||||
|
@ -26,11 +28,13 @@
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include <cerrno>
|
#include <cerrno>
|
||||||
|
#include <chrono>
|
||||||
#include <codecvt>
|
#include <codecvt>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <locale>
|
#include <locale>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
#include <thread>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace usenetsearch {
|
namespace usenetsearch {
|
||||||
|
@ -50,17 +54,34 @@ void SerializableFile::FileLock()
|
||||||
{
|
{
|
||||||
if (!m_fd)
|
if (!m_fd)
|
||||||
{
|
{
|
||||||
throw FileIOException(EBADFD,
|
Logger::Get().Fatal<FileIOException>(
|
||||||
|
LOGID("SerializableFile"),
|
||||||
"Attempt to write to a file (" + m_fileName.string()
|
"Attempt to write to a file (" + m_fileName.string()
|
||||||
+ " ) that isn't open."
|
+ " ) that isn't open."
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
int ret{EWOULDBLOCK};
|
int ret{EWOULDBLOCK};
|
||||||
while ((ret == EWOULDBLOCK) || (ret == EINTR)) ret = flock(m_fd, LOCK_EX);
|
while ((ret == EWOULDBLOCK) || (ret == EINTR))
|
||||||
|
{
|
||||||
|
if (Application::Get().ShouldStop())
|
||||||
|
{
|
||||||
|
Logger::Get().Fatal<FileIOException>(
|
||||||
|
LOGID("SerializableFile"),
|
||||||
|
"Interrupt while trying to establish a lock on file ("
|
||||||
|
+ m_fileName.string() + ")."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
ret = flock(m_fd, LOCK_EX);
|
||||||
|
if ((ret == EWOULDBLOCK) || (ret == EINTR))
|
||||||
|
{
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||||
|
}
|
||||||
|
}
|
||||||
if (ret != 0)
|
if (ret != 0)
|
||||||
{
|
{
|
||||||
throw FileIOException(
|
Logger::Get().Fatal<FileIOException>(
|
||||||
errno, "Error trying to lock file: " + m_fileName.string()
|
LOGID("SerializableFile"),
|
||||||
|
"Error trying to lock file: " + m_fileName.string()
|
||||||
+ " : " + std::strerror(errno)
|
+ " : " + std::strerror(errno)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -71,7 +92,8 @@ void SerializableFile::FileUnlock()
|
||||||
{
|
{
|
||||||
if (!m_fd)
|
if (!m_fd)
|
||||||
{
|
{
|
||||||
throw FileIOException(EBADFD,
|
Logger::Get().Fatal<FileIOException>(
|
||||||
|
LOGID("SerializableFile"),
|
||||||
"Attempt to write to a file (" + m_fileName.string()
|
"Attempt to write to a file (" + m_fileName.string()
|
||||||
+ " ) that isn't open."
|
+ " ) that isn't open."
|
||||||
);
|
);
|
||||||
|
@ -79,8 +101,9 @@ void SerializableFile::FileUnlock()
|
||||||
const int ret = flock(m_fd, LOCK_UN);
|
const int ret = flock(m_fd, LOCK_UN);
|
||||||
if (ret != 0)
|
if (ret != 0)
|
||||||
{
|
{
|
||||||
throw FileIOException(
|
Logger::Get().Fatal<FileIOException>(
|
||||||
errno, "Error trying to unlock file: " + m_fileName.string()
|
LOGID("SerializableFile"),
|
||||||
|
"Error trying to unlock file: " + m_fileName.string()
|
||||||
+ " : " + std::strerror(errno)
|
+ " : " + std::strerror(errno)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -108,8 +131,11 @@ void SerializableFile::Open(const std::string& fileName)
|
||||||
int ret = open(fileName.c_str(), flags, 0644);
|
int ret = open(fileName.c_str(), flags, 0644);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
{
|
{
|
||||||
throw FileIOException(errno, "Could not open file: " + fileName
|
Logger::Get().Fatal<FileIOException>(
|
||||||
+ " : " + std::string{std::strerror(errno)});
|
LOGID("SerializableFile"),
|
||||||
|
"Could not open file: " + fileName
|
||||||
|
+ " : " + std::string{std::strerror(errno)}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
m_fd = ret;
|
m_fd = ret;
|
||||||
if (m_lockOnOpen) FileLock();
|
if (m_lockOnOpen) FileLock();
|
||||||
|
@ -119,7 +145,8 @@ void SerializableFile::RangeLock(size_t offset, size_t size) const
|
||||||
{
|
{
|
||||||
if (!m_fd)
|
if (!m_fd)
|
||||||
{
|
{
|
||||||
throw FileIOException(EBADFD,
|
Logger::Get().Fatal<FileIOException>(
|
||||||
|
LOGID("SerializableFile"),
|
||||||
"Attempt to write to a file (" + m_fileName.string()
|
"Attempt to write to a file (" + m_fileName.string()
|
||||||
+ " ) that isn't open."
|
+ " ) that isn't open."
|
||||||
);
|
);
|
||||||
|
@ -128,9 +155,11 @@ void SerializableFile::RangeLock(size_t offset, size_t size) const
|
||||||
Seek(offset, std::ios_base::beg);
|
Seek(offset, std::ios_base::beg);
|
||||||
if (lockf(m_fd, F_LOCK, size) == -1)
|
if (lockf(m_fd, F_LOCK, size) == -1)
|
||||||
{
|
{
|
||||||
throw FileIOException(errno,
|
Logger::Get().Fatal<FileIOException>(
|
||||||
|
LOGID("SerializableFile"),
|
||||||
"Could not acquire a write lock on file: " + m_fileName.string()
|
"Could not acquire a write lock on file: " + m_fileName.string()
|
||||||
+ " : " + std::strerror(errno));
|
+ " : " + std::strerror(errno)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
Seek(pos, std::ios_base::beg);
|
Seek(pos, std::ios_base::beg);
|
||||||
}
|
}
|
||||||
|
@ -139,7 +168,8 @@ void SerializableFile::RangeUnlock(size_t offset, size_t size) const
|
||||||
{
|
{
|
||||||
if (!m_fd)
|
if (!m_fd)
|
||||||
{
|
{
|
||||||
throw FileIOException(EBADFD,
|
Logger::Get().Fatal<FileIOException>(
|
||||||
|
LOGID("SerializableFile"),
|
||||||
"Attempt to write to a file (" + m_fileName.string()
|
"Attempt to write to a file (" + m_fileName.string()
|
||||||
+ " ) that isn't open."
|
+ " ) that isn't open."
|
||||||
);
|
);
|
||||||
|
@ -148,9 +178,11 @@ void SerializableFile::RangeUnlock(size_t offset, size_t size) const
|
||||||
Seek(offset, std::ios_base::beg);
|
Seek(offset, std::ios_base::beg);
|
||||||
if (lockf(m_fd, F_ULOCK, size) == -1)
|
if (lockf(m_fd, F_ULOCK, size) == -1)
|
||||||
{
|
{
|
||||||
throw FileIOException(errno,
|
Logger::Get().Fatal<FileIOException>(
|
||||||
|
LOGID("SerializableFile"),
|
||||||
"Could not release a write lock on file: " + m_fileName.string()
|
"Could not release a write lock on file: " + m_fileName.string()
|
||||||
+ " : " + std::strerror(errno));
|
+ " : " + std::strerror(errno)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
Seek(pos, std::ios_base::beg);
|
Seek(pos, std::ios_base::beg);
|
||||||
}
|
}
|
||||||
|
@ -159,7 +191,8 @@ std::string SerializableFile::ReadStr(size_t size) const
|
||||||
{
|
{
|
||||||
if (!m_fd)
|
if (!m_fd)
|
||||||
{
|
{
|
||||||
throw FileIOException(EBADFD,
|
Logger::Get().Fatal<FileIOException>(
|
||||||
|
LOGID("SerializableFile"),
|
||||||
"Attempt to write to a file (" + m_fileName.string()
|
"Attempt to write to a file (" + m_fileName.string()
|
||||||
+ " ) that isn't open."
|
+ " ) that isn't open."
|
||||||
);
|
);
|
||||||
|
@ -176,7 +209,8 @@ std::string SerializableFile::ReadStr(size_t size) const
|
||||||
const auto readNow = read(m_fd, &result[0], size);
|
const auto readNow = read(m_fd, &result[0], size);
|
||||||
if (readNow == -1)
|
if (readNow == -1)
|
||||||
{
|
{
|
||||||
throw FileIOException(errno,
|
Logger::Get().Fatal<FileIOException>(
|
||||||
|
LOGID("SerializableFile"),
|
||||||
"Error while reading from file: " + m_fileName.string()
|
"Error while reading from file: " + m_fileName.string()
|
||||||
+ " : " + std::strerror(errno)
|
+ " : " + std::strerror(errno)
|
||||||
);
|
);
|
||||||
|
@ -219,7 +253,8 @@ void SerializableFile::Seek(
|
||||||
{
|
{
|
||||||
if (!m_fd)
|
if (!m_fd)
|
||||||
{
|
{
|
||||||
throw FileIOException(EBADFD,
|
Logger::Get().Fatal<FileIOException>(
|
||||||
|
LOGID("SerializableFile"),
|
||||||
"Attempt to write to a file (" + m_fileName.string()
|
"Attempt to write to a file (" + m_fileName.string()
|
||||||
+ " ) that isn't open."
|
+ " ) that isn't open."
|
||||||
);
|
);
|
||||||
|
@ -241,7 +276,8 @@ void SerializableFile::Seek(
|
||||||
const auto newOffset = lseek(m_fd, offset, whence);
|
const auto newOffset = lseek(m_fd, offset, whence);
|
||||||
if (newOffset == -1)
|
if (newOffset == -1)
|
||||||
{
|
{
|
||||||
throw FileIOException(errno,
|
Logger::Get().Fatal<FileIOException>(
|
||||||
|
LOGID("SerializableFile"),
|
||||||
"Could not set cursor position in file: " + m_fileName.string()
|
"Could not set cursor position in file: " + m_fileName.string()
|
||||||
+ " : " + std::strerror(errno)
|
+ " : " + std::strerror(errno)
|
||||||
);
|
);
|
||||||
|
@ -261,7 +297,8 @@ size_t SerializableFile::Tell() const
|
||||||
{
|
{
|
||||||
if (!m_fd)
|
if (!m_fd)
|
||||||
{
|
{
|
||||||
throw FileIOException(EBADFD,
|
Logger::Get().Fatal<FileIOException>(
|
||||||
|
LOGID("SerializableFile"),
|
||||||
"Attempt to write to a file (" + m_fileName.string()
|
"Attempt to write to a file (" + m_fileName.string()
|
||||||
+ " ) that isn't open."
|
+ " ) that isn't open."
|
||||||
);
|
);
|
||||||
|
@ -269,7 +306,8 @@ size_t SerializableFile::Tell() const
|
||||||
const auto result = lseek(m_fd, 0, SEEK_CUR);
|
const auto result = lseek(m_fd, 0, SEEK_CUR);
|
||||||
if (result == -1)
|
if (result == -1)
|
||||||
{
|
{
|
||||||
throw FileIOException(errno,
|
Logger::Get().Fatal<FileIOException>(
|
||||||
|
LOGID("SerializableFile"),
|
||||||
"Could not get cursor position in file: " + m_fileName.string()
|
"Could not get cursor position in file: " + m_fileName.string()
|
||||||
+ " : " + std::strerror(errno)
|
+ " : " + std::strerror(errno)
|
||||||
);
|
);
|
||||||
|
@ -301,7 +339,8 @@ void SerializableFile::Write(const char* bytes, size_t size) const
|
||||||
{
|
{
|
||||||
if (!m_fd)
|
if (!m_fd)
|
||||||
{
|
{
|
||||||
throw FileIOException(EBADFD,
|
Logger::Get().Fatal<FileIOException>(
|
||||||
|
LOGID("SerializableFile"),
|
||||||
"Attempt to write to a file (" + m_fileName.string()
|
"Attempt to write to a file (" + m_fileName.string()
|
||||||
+ " ) that isn't open."
|
+ " ) that isn't open."
|
||||||
);
|
);
|
||||||
|
@ -323,7 +362,8 @@ void SerializableFile::Write(const char* bytes, size_t size) const
|
||||||
{
|
{
|
||||||
if (errno == 0) continue;
|
if (errno == 0) continue;
|
||||||
}
|
}
|
||||||
throw FileIOException(errno,
|
Logger::Get().Fatal<FileIOException>(
|
||||||
|
LOGID("SerializableFile"),
|
||||||
"Failure while writing to file: " + m_fileName.string()
|
"Failure while writing to file: " + m_fileName.string()
|
||||||
+ " : " + std::strerror(errno)
|
+ " : " + std::strerror(errno)
|
||||||
);
|
);
|
||||||
|
@ -452,7 +492,10 @@ SerializableFile& operator>>(SerializableFile& in, ArticleEntry& obj)
|
||||||
in >> STX;
|
in >> STX;
|
||||||
if ((SOH != 1) || (STX != 2))
|
if ((SOH != 1) || (STX != 2))
|
||||||
{
|
{
|
||||||
throw SerializeException(EBADMSG, "Bad magic number in entry header.");
|
Logger::Get().Fatal<SerializeException>(
|
||||||
|
LOGID("SerializableFile"),
|
||||||
|
"Bad magic number in entry header."
|
||||||
|
);
|
||||||
}
|
}
|
||||||
for (std::uint8_t i = 0; i != 16; ++i)
|
for (std::uint8_t i = 0; i != 16; ++i)
|
||||||
{
|
{
|
||||||
|
@ -464,7 +507,10 @@ SerializableFile& operator>>(SerializableFile& in, ArticleEntry& obj)
|
||||||
in >> EOT;
|
in >> EOT;
|
||||||
if ((ETX != 3) || (EOT != 4))
|
if ((ETX != 3) || (EOT != 4))
|
||||||
{
|
{
|
||||||
throw SerializeException(EBADMSG, "Bad magic number in entry footer.");
|
Logger::Get().Fatal<SerializeException>(
|
||||||
|
LOGID("SerializableFile"),
|
||||||
|
"Bad magic number in entry footer."
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return in;
|
return in;
|
||||||
}
|
}
|
||||||
|
@ -509,7 +555,8 @@ SerializableFile& operator>>(SerializableFile& in, NntpListEntry& obj)
|
||||||
in >> STX;
|
in >> STX;
|
||||||
if ((SOH != 1) || (STX != 2))
|
if ((SOH != 1) || (STX != 2))
|
||||||
{
|
{
|
||||||
throw SerializeException(EBADMSG,
|
Logger::Get().Fatal<SerializeException>(
|
||||||
|
LOGID("SerializableFile"),
|
||||||
"Bad magic number in NNTP entry header."
|
"Bad magic number in NNTP entry header."
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -524,7 +571,8 @@ SerializableFile& operator>>(SerializableFile& in, NntpListEntry& obj)
|
||||||
in >> EOT;
|
in >> EOT;
|
||||||
if ((ETX != 3) || (EOT != 4))
|
if ((ETX != 3) || (EOT != 4))
|
||||||
{
|
{
|
||||||
throw SerializeException(EBADMSG,
|
Logger::Get().Fatal<SerializeException>(
|
||||||
|
LOGID("SerializableFile"),
|
||||||
"Bad magic number in NNTP entry footer."
|
"Bad magic number in NNTP entry footer."
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,8 @@
|
||||||
|
|
||||||
#include "usenetsearch/StringUtils.h"
|
#include "usenetsearch/StringUtils.h"
|
||||||
|
|
||||||
|
#include "usenetsearch/Logger.h"
|
||||||
|
|
||||||
#include <openssl/md5.h>
|
#include <openssl/md5.h>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
@ -111,9 +113,11 @@ bool StringToBoolean(const std::string& str)
|
||||||
const std::string lstr = StringTrim(StringToLower(str));
|
const std::string lstr = StringTrim(StringToLower(str));
|
||||||
if ((lstr == "true") || (lstr == "yes") || (lstr == "1")) return true;
|
if ((lstr == "true") || (lstr == "yes") || (lstr == "1")) return true;
|
||||||
if ((lstr == "false") || (lstr == "no") || (lstr == "0")) return false;
|
if ((lstr == "false") || (lstr == "no") || (lstr == "0")) return false;
|
||||||
throw StringException(EINVAL,
|
Logger::Get().Fatal<StringException>(
|
||||||
|
LOGID("StringUtils"),
|
||||||
"The string \"" + str + "\" is not a valid boolean value."
|
"The string \"" + str + "\" is not a valid boolean value."
|
||||||
);
|
);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool StringToBoolean(const std::wstring& str)
|
bool StringToBoolean(const std::wstring& str)
|
||||||
|
@ -122,10 +126,12 @@ bool StringToBoolean(const std::wstring& str)
|
||||||
if ((lstr == L"true") || (lstr == L"yes") || (lstr == L"1")) return true;
|
if ((lstr == L"true") || (lstr == L"yes") || (lstr == L"1")) return true;
|
||||||
if ((lstr == L"false") || (lstr == L"no") || (lstr == L"0")) return false;
|
if ((lstr == L"false") || (lstr == L"no") || (lstr == L"0")) return false;
|
||||||
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> conv;
|
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> conv;
|
||||||
throw StringException(EINVAL,
|
Logger::Get().Fatal<StringException>(
|
||||||
|
LOGID("StringUtils"),
|
||||||
"The string \"" + conv.to_bytes(str)
|
"The string \"" + conv.to_bytes(str)
|
||||||
+ "\" is not a valid boolean value."
|
+ "\" is not a valid boolean value."
|
||||||
);
|
);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void StringTreeOperation(
|
void StringTreeOperation(
|
||||||
|
@ -135,6 +141,7 @@ void StringTreeOperation(
|
||||||
std::function<void(const std::string& subToken, const std::string& str)> Fn)
|
std::function<void(const std::string& subToken, const std::string& str)> Fn)
|
||||||
{
|
{
|
||||||
const auto tokens = StringSplit(searchString, splitBy);
|
const auto tokens = StringSplit(searchString, splitBy);
|
||||||
|
std::vector<std::string> tokenList;
|
||||||
for (auto outerIt = tokens.begin(); outerIt != tokens.end(); outerIt++)
|
for (auto outerIt = tokens.begin(); outerIt != tokens.end(); outerIt++)
|
||||||
{
|
{
|
||||||
for (size_t depth = 1; depth != maxDepth + 1; ++depth)
|
for (size_t depth = 1; depth != maxDepth + 1; ++depth)
|
||||||
|
@ -142,7 +149,16 @@ void StringTreeOperation(
|
||||||
const auto endIt = outerIt + depth;
|
const auto endIt = outerIt + depth;
|
||||||
const auto subset = std::vector<std::string>(outerIt, endIt);
|
const auto subset = std::vector<std::string>(outerIt, endIt);
|
||||||
const auto subToken = StringJoin(subset, splitBy);
|
const auto subToken = StringJoin(subset, splitBy);
|
||||||
Fn(subToken, searchString);
|
// Check if we already have this token.
|
||||||
|
//
|
||||||
|
// For phrases like "we went here and then we went there" this would
|
||||||
|
// avoid indexing the tokens 'we' and 'went' and 'we went' twice.
|
||||||
|
if (std::find(tokenList.begin(), tokenList.end(), subToken)
|
||||||
|
== tokenList.end())
|
||||||
|
{
|
||||||
|
Fn(subToken, searchString);
|
||||||
|
tokenList.emplace_back(subToken);
|
||||||
|
}
|
||||||
if (endIt == tokens.end()) break;
|
if (endIt == tokens.end()) break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
along with UsenetSearch. If not, see <https://www.gnu.org/licenses/>.
|
along with UsenetSearch. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "usenetsearch/Logger.h"
|
||||||
#include "usenetsearch/TcpConnection.h"
|
#include "usenetsearch/TcpConnection.h"
|
||||||
|
|
||||||
#include "usenetsearch/Dns.h"
|
#include "usenetsearch/Dns.h"
|
||||||
|
@ -45,7 +46,8 @@ void TcpConnection::Connect(const std::string& host, std::uint16_t port)
|
||||||
const std::vector<struct addrinfo> addresses = DnsResolve(host, port);
|
const std::vector<struct addrinfo> addresses = DnsResolve(host, port);
|
||||||
if (addresses.empty())
|
if (addresses.empty())
|
||||||
{
|
{
|
||||||
throw DnsResolveException(EDESTADDRREQ,
|
Logger::Get().Fatal<DnsResolveException>(
|
||||||
|
LOGID("TcpConnection"),
|
||||||
"The provided host (" + host + ") did not resolve to an address."
|
"The provided host (" + host + ") did not resolve to an address."
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -66,8 +68,8 @@ void TcpConnection::Connect(const std::string& host, std::uint16_t port)
|
||||||
);
|
);
|
||||||
if (timeDelta > m_connectionTimeout)
|
if (timeDelta > m_connectionTimeout)
|
||||||
{
|
{
|
||||||
throw SocketException(
|
Logger::Get().Fatal<SocketException>(
|
||||||
ETIMEDOUT,
|
LOGID("TcpConnection"),
|
||||||
"Timed out while trying to connect to " + host + ":"
|
"Timed out while trying to connect to " + host + ":"
|
||||||
+ std::to_string(port) + "."
|
+ std::to_string(port) + "."
|
||||||
);
|
);
|
||||||
|
@ -75,7 +77,8 @@ void TcpConnection::Connect(const std::string& host, std::uint16_t port)
|
||||||
fd = socket(addr.ai_family, SOCK_STREAM, 0);
|
fd = socket(addr.ai_family, SOCK_STREAM, 0);
|
||||||
if (fd < 0)
|
if (fd < 0)
|
||||||
{
|
{
|
||||||
throw SocketException(errno,
|
Logger::Get().Fatal<SocketException>(
|
||||||
|
LOGID("TcpConnection"),
|
||||||
"Failed to create socket - Error (" + std::to_string(errno)
|
"Failed to create socket - Error (" + std::to_string(errno)
|
||||||
+ ") - " + std::strerror(errno)
|
+ ") - " + std::strerror(errno)
|
||||||
);
|
);
|
||||||
|
@ -101,7 +104,8 @@ void TcpConnection::Connect(const std::string& host, std::uint16_t port)
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
close(fd);
|
close(fd);
|
||||||
throw SocketException(errno,
|
Logger::Get().Fatal<SocketException>(
|
||||||
|
LOGID("TcpConnection"),
|
||||||
"Failed to connect to " + host + ":"
|
"Failed to connect to " + host + ":"
|
||||||
+ std::to_string(port) + " - Error ("
|
+ std::to_string(port) + " - Error ("
|
||||||
+ std::to_string(errno) + ") - " + strerror(errno)
|
+ std::to_string(errno) + ") - " + strerror(errno)
|
||||||
|
@ -152,7 +156,8 @@ std::string TcpConnection::Read(size_t amount)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw SocketException(errno,
|
Logger::Get().Fatal<SocketException>(
|
||||||
|
LOGID("TcpConnection"),
|
||||||
"Error while reading from TCP socket (" + std::to_string(errno)
|
"Error while reading from TCP socket (" + std::to_string(errno)
|
||||||
+ ") - " + std::strerror(errno)
|
+ ") - " + std::strerror(errno)
|
||||||
);
|
);
|
||||||
|
@ -178,7 +183,8 @@ void TcpConnection::Write(const std::string& data)
|
||||||
if ((timeDelta > m_ioTimeout)
|
if ((timeDelta > m_ioTimeout)
|
||||||
|| m_ioTimeout == std::chrono::milliseconds{0})
|
|| m_ioTimeout == std::chrono::milliseconds{0})
|
||||||
{
|
{
|
||||||
throw SocketException(ETIMEDOUT,
|
Logger::Get().Fatal<SocketException>(
|
||||||
|
LOGID("TcpConnection"),
|
||||||
"Timed out writing to TCP socket."
|
"Timed out writing to TCP socket."
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -191,7 +197,8 @@ void TcpConnection::Write(const std::string& data)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw SocketException(errno,
|
Logger::Get().Fatal<SocketException>(
|
||||||
|
LOGID("TcpConnection"),
|
||||||
"Error writing to tcp socket (" + std::to_string(errno)
|
"Error writing to tcp socket (" + std::to_string(errno)
|
||||||
+ ") - " + std::strerror(errno)
|
+ ") - " + std::strerror(errno)
|
||||||
);
|
);
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
|
|
||||||
#include "usenetsearch/Application.h"
|
#include "usenetsearch/Application.h"
|
||||||
#include "usenetsearch/Except.h"
|
#include "usenetsearch/Except.h"
|
||||||
|
#include "usenetsearch/Logger.h"
|
||||||
#include "usenetsearch/StringUtils.h"
|
#include "usenetsearch/StringUtils.h"
|
||||||
|
|
||||||
#include <codecvt>
|
#include <codecvt>
|
||||||
|
@ -32,10 +33,6 @@ namespace usenetsearch {
|
||||||
|
|
||||||
// UsenetClient class ----------------------------------------------------------
|
// UsenetClient class ----------------------------------------------------------
|
||||||
|
|
||||||
UsenetClient::UsenetClient(Application& app): m_app(app)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void UsenetClient::Authenticate(
|
void UsenetClient::Authenticate(
|
||||||
const std::wstring& user,
|
const std::wstring& user,
|
||||||
const std::wstring& password)
|
const std::wstring& password)
|
||||||
|
@ -45,8 +42,8 @@ void UsenetClient::Authenticate(
|
||||||
auto response = ReadLine();
|
auto response = ReadLine();
|
||||||
if (IsError(response))
|
if (IsError(response))
|
||||||
{
|
{
|
||||||
throw UsenetClientException(
|
Logger::Get().Fatal<UsenetClientException>(
|
||||||
response.code,
|
LOGID("UsenetClient"),
|
||||||
"Error authenticating with NNTP server: "
|
"Error authenticating with NNTP server: "
|
||||||
+ response.message
|
+ response.message
|
||||||
);
|
);
|
||||||
|
@ -56,8 +53,8 @@ void UsenetClient::Authenticate(
|
||||||
response = ReadLine();
|
response = ReadLine();
|
||||||
if (IsError(response))
|
if (IsError(response))
|
||||||
{
|
{
|
||||||
throw UsenetClientException(
|
Logger::Get().Fatal<UsenetClientException>(
|
||||||
response.code,
|
LOGID("UsenetClient"),
|
||||||
"Error authenticating with NNTP server: "
|
"Error authenticating with NNTP server: "
|
||||||
+ response.message
|
+ response.message
|
||||||
);
|
);
|
||||||
|
@ -83,8 +80,8 @@ void UsenetClient::Connect(
|
||||||
}
|
}
|
||||||
catch (const UsenetSearchException& e)
|
catch (const UsenetSearchException& e)
|
||||||
{
|
{
|
||||||
throw UsenetClientException(
|
Logger::Get().Fatal<UsenetClientException>(
|
||||||
e.Code(),
|
LOGID("UsenetClient"),
|
||||||
"Error while trying to connect to host: " + host + ":"
|
"Error while trying to connect to host: " + host + ":"
|
||||||
+ std::to_string(port) + " - " + e.what()
|
+ std::to_string(port) + " - " + e.what()
|
||||||
);
|
);
|
||||||
|
@ -93,8 +90,8 @@ void UsenetClient::Connect(
|
||||||
const auto serverHello = ReadLine();
|
const auto serverHello = ReadLine();
|
||||||
if (IsError(serverHello))
|
if (IsError(serverHello))
|
||||||
{
|
{
|
||||||
throw UsenetClientException(
|
Logger::Get().Fatal<UsenetClientException>(
|
||||||
serverHello.code,
|
LOGID("UsenetClient"),
|
||||||
"Error received from NNTP server: "
|
"Error received from NNTP server: "
|
||||||
+ serverHello.message
|
+ serverHello.message
|
||||||
);
|
);
|
||||||
|
@ -108,8 +105,8 @@ void UsenetClient::Group(const std::wstring& newsgroup)
|
||||||
auto response = ReadLine();
|
auto response = ReadLine();
|
||||||
if (IsError(response))
|
if (IsError(response))
|
||||||
{
|
{
|
||||||
throw UsenetClientException(
|
Logger::Get().Fatal<UsenetClientException>(
|
||||||
response.code,
|
LOGID("UsenetClient"),
|
||||||
"Error changing group to " + StringFromWideString(newsgroup) + " : "
|
"Error changing group to " + StringFromWideString(newsgroup) + " : "
|
||||||
+ response.message
|
+ response.message
|
||||||
);
|
);
|
||||||
|
@ -124,8 +121,8 @@ NntpHeader UsenetClient::Head(std::uint64_t articleID)
|
||||||
auto response = ReadLine();
|
auto response = ReadLine();
|
||||||
if (IsError(response))
|
if (IsError(response))
|
||||||
{
|
{
|
||||||
throw UsenetClientException(
|
Logger::Get().Fatal<UsenetClientException>(
|
||||||
response.code,
|
LOGID("UsenetClient"),
|
||||||
"Error getting headers for article id "
|
"Error getting headers for article id "
|
||||||
+ std::to_string(articleID)
|
+ std::to_string(articleID)
|
||||||
+ " : " + response.message
|
+ " : " + response.message
|
||||||
|
@ -164,8 +161,8 @@ std::unique_ptr<std::vector<NntpListEntry>> UsenetClient::List()
|
||||||
const auto response = ReadLine();
|
const auto response = ReadLine();
|
||||||
if (IsError(response))
|
if (IsError(response))
|
||||||
{
|
{
|
||||||
throw UsenetClientException(
|
Logger::Get().Fatal<UsenetClientException>(
|
||||||
response.code,
|
LOGID("UsenetClient"),
|
||||||
"Failed to fetch newsgroup list from server, "
|
"Failed to fetch newsgroup list from server, "
|
||||||
+ std::string{"server responded with: "}
|
+ std::string{"server responded with: "}
|
||||||
+ response.message
|
+ response.message
|
||||||
|
@ -189,7 +186,7 @@ std::unique_ptr<std::vector<NntpListEntry>> UsenetClient::List()
|
||||||
entry.status = fields[4];
|
entry.status = fields[4];
|
||||||
entry.id = 0; // incremented by db when saving.
|
entry.id = 0; // incremented by db when saving.
|
||||||
entry.lastIndexedArticle = NntpListEntry::NOT_INDEXED;
|
entry.lastIndexedArticle = NntpListEntry::NOT_INDEXED;
|
||||||
if (m_app.GetFilter().ProcessNewsgroup(entry.name))
|
if (Application::Get().GetFilter().ProcessNewsgroup(entry.name))
|
||||||
{
|
{
|
||||||
result->emplace_back(entry);
|
result->emplace_back(entry);
|
||||||
}
|
}
|
||||||
|
@ -202,7 +199,9 @@ std::unique_ptr<std::vector<std::uint64_t>> UsenetClient::ListGroup(
|
||||||
const std::wstring& newsGroup)
|
const std::wstring& newsGroup)
|
||||||
{
|
{
|
||||||
auto result = std::make_unique<std::vector<std::uint64_t>>();
|
auto result = std::make_unique<std::vector<std::uint64_t>>();
|
||||||
if (!m_app.GetFilter().ProcessNewsgroup(StringFromWideString(newsGroup)))
|
if (!Application::Get().GetFilter().ProcessNewsgroup(
|
||||||
|
StringFromWideString(newsGroup))
|
||||||
|
)
|
||||||
{
|
{
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -212,8 +211,8 @@ std::unique_ptr<std::vector<std::uint64_t>> UsenetClient::ListGroup(
|
||||||
const auto response = ReadLine();
|
const auto response = ReadLine();
|
||||||
if (IsError(response))
|
if (IsError(response))
|
||||||
{
|
{
|
||||||
throw UsenetClientException(
|
Logger::Get().Fatal<UsenetClientException>(
|
||||||
response.code,
|
LOGID("UsenetClient"),
|
||||||
"Failed to fetch newsgroup list from server, "
|
"Failed to fetch newsgroup list from server, "
|
||||||
+ std::string{"server responded with: "}
|
+ std::string{"server responded with: "}
|
||||||
+ response.message
|
+ response.message
|
||||||
|
@ -241,8 +240,8 @@ void UsenetClient::ProcessHeaders(
|
||||||
const auto response = ReadLine();
|
const auto response = ReadLine();
|
||||||
if (IsError(response))
|
if (IsError(response))
|
||||||
{
|
{
|
||||||
throw UsenetClientException(
|
Logger::Get().Fatal<UsenetClientException>(
|
||||||
response.code,
|
LOGID("UsenetClient"),
|
||||||
"Error getting headers: "
|
"Error getting headers: "
|
||||||
+ response.message
|
+ response.message
|
||||||
);
|
);
|
||||||
|
@ -293,9 +292,11 @@ NntpMessage UsenetClient::ReadLine()
|
||||||
line = ReadUntil("\r\n");
|
line = ReadUntil("\r\n");
|
||||||
if (line.length() < 2)
|
if (line.length() < 2)
|
||||||
{
|
{
|
||||||
throw UsenetSearchException(EPROTONOSUPPORT,
|
Logger::Get().Fatal<UsenetClientException>(
|
||||||
|
LOGID("UsenetClient"),
|
||||||
"NNTP protocol error - invalid response from server: "
|
"NNTP protocol error - invalid response from server: "
|
||||||
+ line);
|
+ line
|
||||||
|
);
|
||||||
}
|
}
|
||||||
std::string codeStr = line.substr(0, 3);
|
std::string codeStr = line.substr(0, 3);
|
||||||
result.code = std::stoi(codeStr);
|
result.code = std::stoi(codeStr);
|
||||||
|
|
|
@ -25,10 +25,9 @@ using namespace usenetsearch;
|
||||||
|
|
||||||
int main(int argc, char* argv[])
|
int main(int argc, char* argv[])
|
||||||
{
|
{
|
||||||
Application app;
|
|
||||||
std::string tokenFile{""};
|
std::string tokenFile{""};
|
||||||
std::string newsgroupFile{""};
|
std::string newsgroupFile{""};
|
||||||
app.AddFileOption(
|
Application::Get().AddFileOption(
|
||||||
't',
|
't',
|
||||||
"token db file to dump.",
|
"token db file to dump.",
|
||||||
[&tokenFile](const std::string& val)
|
[&tokenFile](const std::string& val)
|
||||||
|
@ -36,7 +35,7 @@ int main(int argc, char* argv[])
|
||||||
tokenFile = val;
|
tokenFile = val;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
app.AddFileOption(
|
Application::Get().AddFileOption(
|
||||||
'n',
|
'n',
|
||||||
"newsgroup file to dump.",
|
"newsgroup file to dump.",
|
||||||
[&newsgroupFile](const std::string& val)
|
[&newsgroupFile](const std::string& val)
|
||||||
|
@ -44,20 +43,24 @@ int main(int argc, char* argv[])
|
||||||
newsgroupFile = val;
|
newsgroupFile = val;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
if (!app.Init(argc, argv)) return 1;
|
if (!Application::Get().Init(argc, argv)) return 1;
|
||||||
if (!tokenFile.empty())
|
if (!tokenFile.empty())
|
||||||
{
|
{
|
||||||
app.GetDb().ParseTokenFile(tokenFile, [](const ArticleEntry& token){
|
Application::Get().GetDb().ParseTokenFile(
|
||||||
std::cout << "Hash: " << HashBytesToString(token.hash) << " | "
|
tokenFile,
|
||||||
<< "NewsgroupID: " << token.newsgroupID << " | "
|
[](const ArticleEntry& token){
|
||||||
<< "ArticleID: " << token.articleID << std::endl;
|
std::cout << "Hash: " << HashBytesToString(token.hash) << " | "
|
||||||
});
|
<< "NewsgroupID: " << token.newsgroupID << " | "
|
||||||
|
<< "ArticleID: " << token.articleID << std::endl;
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (!newsgroupFile.empty())
|
if (!newsgroupFile.empty())
|
||||||
{
|
{
|
||||||
const auto groups = app.GetDb().LoadNewsgroupList();
|
const auto groups = Application::Get().GetDb().LoadNewsgroupList();
|
||||||
for(const auto& group: *groups)
|
for(const auto& group: *groups)
|
||||||
{
|
{
|
||||||
|
if (Application::Get().ShouldStop()) return 1;
|
||||||
std::cout << std::left
|
std::cout << std::left
|
||||||
<< std::setw(9) << "Id: " + std::to_string(group.id)
|
<< std::setw(9) << "Id: " + std::to_string(group.id)
|
||||||
<< std::setw(3) << " | "
|
<< std::setw(3) << " | "
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
|
|
||||||
#include "usenetsearch/Application.h"
|
#include "usenetsearch/Application.h"
|
||||||
#include "usenetsearch/UsenetClient.h"
|
|
||||||
#include "usenetsearch/Indexer.h"
|
#include "usenetsearch/Indexer.h"
|
||||||
|
#include "usenetsearch/StringUtils.h"
|
||||||
|
#include "usenetsearch/UsenetClient.h"
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
|
@ -9,28 +10,27 @@ using namespace usenetsearch;
|
||||||
|
|
||||||
int main(int argc, char* argv[])
|
int main(int argc, char* argv[])
|
||||||
{
|
{
|
||||||
Application app;
|
|
||||||
std::string searchString;
|
std::string searchString;
|
||||||
app.AddStringOption('s', "Search string",
|
Application::Get().AddStringOption('s', "Search string",
|
||||||
[&searchString](const std::string& s){
|
[&searchString](const std::string& s){
|
||||||
searchString = s;
|
searchString = s;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
int maxResults{0};
|
int maxResults{0};
|
||||||
app.AddIntegerOption('n', "Maximum results",
|
Application::Get().AddIntegerOption('n', "Maximum results",
|
||||||
[&maxResults](int n){
|
[&maxResults](int n){
|
||||||
maxResults = n;
|
maxResults = n;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
if (!app.Init(argc, argv)) return 1;
|
if (!Application::Get().Init(argc, argv)) return 1;
|
||||||
if (searchString.empty())
|
if (searchString.empty())
|
||||||
{
|
{
|
||||||
std::cerr << "Missing search string." << std::endl;
|
std::cerr << "Missing search string." << std::endl;
|
||||||
app.Usage(argv[0]);
|
Application::Get().Usage(argv[0]);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
UsenetClient client(app);
|
UsenetClient client;
|
||||||
Indexer idx(app, client);
|
Indexer idx(client);
|
||||||
std::unique_ptr<SearchResults> results = idx.Search(
|
std::unique_ptr<SearchResults> results = idx.Search(
|
||||||
searchString
|
searchString
|
||||||
);
|
);
|
||||||
|
@ -39,15 +39,24 @@ int main(int argc, char* argv[])
|
||||||
std::cout << "Nothing found." << std::endl;
|
std::cout << "Nothing found." << std::endl;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
idx.Connect();
|
||||||
size_t resultCounter{0};
|
size_t resultCounter{0};
|
||||||
for (const auto& sr: *results)
|
for (const auto& sr: *results)
|
||||||
{
|
{
|
||||||
std::cout << std::left
|
if (Application::Get().ShouldStop()) return 1;
|
||||||
<< std::setw(18) << "Newsgroup id: " + std::to_string(sr.NewsgroupId())
|
const std::unique_ptr<NntpListEntry> newsGroup
|
||||||
<< std::setw(4) << " | "
|
= Application::Get().GetDb().FindNntpEntry(sr.NewsgroupId());
|
||||||
<< std::setw(17) << "Article id: " + std::to_string(sr.ArticleId())
|
if (!newsGroup) continue;
|
||||||
<< std::setw(4) << " | "
|
client.Group(WideStringFromString(newsGroup->name));
|
||||||
<< std::setw(10) << "Hits: " + std::to_string(sr.Hits())
|
const NntpHeader headers = client.Head(sr.ArticleId());
|
||||||
|
std::cout
|
||||||
|
<< "Score: " + std::to_string(sr.Hits())
|
||||||
|
<< " | "
|
||||||
|
<< "Newsgroup: " + newsGroup->name
|
||||||
|
<< " | "
|
||||||
|
<< "Article id: " + std::to_string(sr.ArticleId())
|
||||||
|
<< " | "
|
||||||
|
<< "Subject: " + headers.subject
|
||||||
<< std::endl;
|
<< std::endl;
|
||||||
resultCounter++;
|
resultCounter++;
|
||||||
if ((maxResults > 0) && (resultCounter >= maxResults)) break;
|
if ((maxResults > 0) && (resultCounter >= maxResults)) break;
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
#include "usenetsearch/Application.h"
|
#include "usenetsearch/Application.h"
|
||||||
#include "usenetsearch/Except.h"
|
#include "usenetsearch/Except.h"
|
||||||
#include "usenetsearch/Indexer.h"
|
#include "usenetsearch/Indexer.h"
|
||||||
|
#include "usenetsearch/Logger.h"
|
||||||
#include "usenetsearch/UsenetClient.h"
|
#include "usenetsearch/UsenetClient.h"
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
@ -26,30 +27,30 @@ using namespace usenetsearch;
|
||||||
|
|
||||||
int main(int argc, char* argv[])
|
int main(int argc, char* argv[])
|
||||||
{
|
{
|
||||||
Application app;
|
if (!Application::Get().Init(argc, argv)) return 1;
|
||||||
if (!app.Init(argc, argv)) return 1;
|
|
||||||
|
|
||||||
UsenetClient client(app);
|
UsenetClient client;
|
||||||
Indexer indexer(app, client);
|
Indexer indexer(client);
|
||||||
std::cout << "Connecting to newsgroup server...";
|
Logger::Get().Debug(
|
||||||
|
LOGID("usenetindexd"),
|
||||||
|
"Connecting to newsgroup server..."
|
||||||
|
);
|
||||||
indexer.Connect();
|
indexer.Connect();
|
||||||
std::cout << "<OK>" << std::endl;
|
Logger::Get().Debug(LOGID("usenetindexd"), "Connected.");
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
std::cout << "Getting newsgroup list...";
|
Logger::Get().Debug(LOGID("usenetindexd"), "Getting newsgroup list...");
|
||||||
std::cout.flush();
|
|
||||||
auto list = client.List();
|
auto list = client.List();
|
||||||
app.GetDb().UpdateNewsgroupList(*list);
|
Application::Get().GetDb().UpdateNewsgroupList(*list);
|
||||||
std::cout << "<DONE>" << std::endl;
|
Logger::Get().Debug(
|
||||||
std::cout.flush();
|
LOGID("usenetindexd"),
|
||||||
std::cout << "Found " << list->size() << " newsgroups." << std::endl;
|
"Found " + std::to_string(list->size()) + " newsgroups."
|
||||||
std::cout.flush();
|
);
|
||||||
indexer.Index(*list);
|
indexer.Index(*list);
|
||||||
}
|
}
|
||||||
catch (const UsenetSearchException& e)
|
catch (const UsenetSearchException& e)
|
||||||
{
|
{
|
||||||
std::cerr << e.what() << std::endl;;
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
|
Loading…
Reference in New Issue