2017-10-17 19:29:56 +05:00
|
|
|
#include <any>
|
|
|
|
#include <iostream>
|
2017-10-11 18:48:27 +05:00
|
|
|
#include <nntpchan/file_handle.hpp>
|
|
|
|
#include <nntpchan/mime.hpp>
|
2017-10-17 19:29:56 +05:00
|
|
|
#include <nntpchan/sanitize.hpp>
|
2017-10-11 18:48:27 +05:00
|
|
|
#include <nntpchan/sha1.hpp>
|
2017-10-17 19:29:56 +05:00
|
|
|
#include <nntpchan/staticfile_frontend.hpp>
|
2017-10-09 20:48:10 +05:00
|
|
|
#include <set>
|
|
|
|
#include <sstream>
|
|
|
|
|
|
|
|
namespace nntpchan
|
|
|
|
{
|
|
|
|
|
2017-10-17 19:29:56 +05:00
|
|
|
StaticFileFrontend::StaticFileFrontend(TemplateEngine *tmpl, const std::string &templateDir, const std::string &outDir,
|
|
|
|
uint32_t pages)
|
|
|
|
: m_TemplateEngine(tmpl), m_TemplateDir(templateDir), m_OutDir(outDir), m_Pages(pages)
|
|
|
|
{
|
|
|
|
}
|
2017-10-09 20:48:10 +05:00
|
|
|
|
2017-10-17 19:29:56 +05:00
|
|
|
void StaticFileFrontend::ProcessNewMessage(const fs::path &fpath)
|
|
|
|
{
|
|
|
|
std::clog << "process message " << fpath << std::endl;
|
|
|
|
auto file = OpenFile(fpath, eRead);
|
|
|
|
if (file)
|
2017-10-09 20:48:10 +05:00
|
|
|
{
|
2017-10-17 19:29:56 +05:00
|
|
|
// read header
|
|
|
|
RawHeader header;
|
|
|
|
if (!ReadHeader(file, header))
|
2017-10-09 20:48:10 +05:00
|
|
|
{
|
2017-10-17 19:29:56 +05:00
|
|
|
std::clog << "failed to read mime header" << std::endl;
|
|
|
|
return;
|
|
|
|
}
|
2017-10-09 20:48:10 +05:00
|
|
|
|
2017-10-17 19:29:56 +05:00
|
|
|
// read body
|
2017-10-09 20:48:10 +05:00
|
|
|
|
2017-10-17 19:29:56 +05:00
|
|
|
auto findMsgidFunc = [](const std::pair<std::string, std::string> &item) -> bool {
|
|
|
|
auto lower = ToLower(item.first);
|
|
|
|
return (lower == "message-id") || (lower == "messageid");
|
|
|
|
};
|
2017-10-13 16:58:41 +05:00
|
|
|
|
2017-10-17 19:29:56 +05:00
|
|
|
auto msgid_itr = std::find_if(header.begin(), header.end(), findMsgidFunc);
|
|
|
|
if (msgid_itr == std::end(header))
|
|
|
|
{
|
|
|
|
std::clog << "no message id for file " << fpath << std::endl;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string msgid = StripWhitespaces(msgid_itr->second);
|
|
|
|
|
|
|
|
if (!IsValidMessageID(msgid))
|
|
|
|
{
|
|
|
|
std::clog << "invalid message-id: " << msgid << std::endl;
|
|
|
|
return;
|
|
|
|
}
|
2017-10-13 16:58:41 +05:00
|
|
|
|
2017-10-17 19:29:56 +05:00
|
|
|
std::string rootmsgid;
|
2017-10-13 16:58:41 +05:00
|
|
|
|
2017-10-17 19:29:56 +05:00
|
|
|
auto findReferences = [](const std::pair<std::string, std::string> &item) -> bool {
|
|
|
|
auto lower = ToLower(item.first);
|
|
|
|
return lower == "references";
|
|
|
|
};
|
2017-10-13 16:58:41 +05:00
|
|
|
|
2017-10-17 19:29:56 +05:00
|
|
|
auto references_itr = std::find_if(header.begin(), header.end(), findReferences);
|
|
|
|
if (references_itr == std::end(header) || StripWhitespaces(references_itr->second).size() == 0)
|
|
|
|
{
|
|
|
|
rootmsgid = msgid;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
const auto &s = references_itr->second;
|
|
|
|
auto checkfunc = [](unsigned char ch) -> bool { return std::isspace(ch) || std::iscntrl(ch); };
|
|
|
|
if (std::count_if(s.begin(), s.end(), checkfunc))
|
2017-10-13 16:58:41 +05:00
|
|
|
{
|
2017-10-17 19:29:56 +05:00
|
|
|
/** split off first element */
|
|
|
|
auto idx = std::find_if(s.begin(), s.end(), checkfunc);
|
|
|
|
rootmsgid = s.substr(0, s.find(*idx));
|
2017-10-13 16:58:41 +05:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2017-10-17 19:29:56 +05:00
|
|
|
rootmsgid = references_itr->second;
|
2017-10-13 16:58:41 +05:00
|
|
|
}
|
2017-10-17 19:29:56 +05:00
|
|
|
}
|
2017-10-13 16:58:41 +05:00
|
|
|
|
2017-10-17 19:29:56 +05:00
|
|
|
std::string rootmsgid_hash = sha1_hex(rootmsgid);
|
2017-10-13 16:58:41 +05:00
|
|
|
|
2017-10-17 19:29:56 +05:00
|
|
|
std::set<std::string> newsgroups_list;
|
|
|
|
|
|
|
|
auto findNewsgroupsFunc = [](const std::pair<std::string, std::string> &item) -> bool {
|
|
|
|
return ToLower(item.first) == "newsgroups";
|
|
|
|
};
|
|
|
|
|
|
|
|
auto group = std::find_if(header.begin(), header.end(), findNewsgroupsFunc);
|
|
|
|
if (group == std::end(header))
|
|
|
|
{
|
|
|
|
std::clog << "no newsgroups header" << std::endl;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
std::istringstream input(group->second);
|
|
|
|
|
|
|
|
std::string newsgroup;
|
|
|
|
while (std::getline(input, newsgroup, ' '))
|
|
|
|
{
|
|
|
|
if (IsValidNewsgroup(newsgroup))
|
|
|
|
newsgroups_list.insert(newsgroup);
|
|
|
|
}
|
|
|
|
|
|
|
|
fs::path threadFilePath = m_OutDir / fs::path("thread-" + rootmsgid_hash + ".html");
|
|
|
|
nntpchan::model::Thread thread;
|
|
|
|
|
|
|
|
if (!m_MessageDB)
|
|
|
|
{
|
|
|
|
std::clog << "no message database" << std::endl;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!m_MessageDB->LoadThread(thread, rootmsgid))
|
|
|
|
{
|
|
|
|
std::clog << "cannot find thread with root " << rootmsgid << std::endl;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
TemplateEngine::Args_t thread_args;
|
|
|
|
thread_args["posts"] = thread;
|
|
|
|
if (m_TemplateEngine)
|
|
|
|
{
|
|
|
|
FileHandle_ptr out = OpenFile(threadFilePath, eWrite);
|
|
|
|
if (!out || !m_TemplateEngine->WriteTemplate("thread.mustache", thread_args, out))
|
2017-10-13 16:58:41 +05:00
|
|
|
{
|
2017-10-17 19:29:56 +05:00
|
|
|
std::clog << "failed to write " << threadFilePath << std::endl;
|
2017-10-13 16:58:41 +05:00
|
|
|
return;
|
|
|
|
}
|
2017-10-17 19:29:56 +05:00
|
|
|
}
|
|
|
|
nntpchan::model::BoardPage page;
|
|
|
|
for (const auto &name : newsgroups_list)
|
|
|
|
{
|
|
|
|
uint32_t pageno = 0;
|
|
|
|
while (pageno < m_Pages)
|
2017-10-11 18:48:27 +05:00
|
|
|
{
|
2017-10-17 19:29:56 +05:00
|
|
|
page.clear();
|
|
|
|
if (!m_MessageDB->LoadBoardPage(page, name, 10, m_Pages))
|
2017-10-09 20:48:10 +05:00
|
|
|
{
|
2017-10-17 19:29:56 +05:00
|
|
|
std::clog << "cannot load board page " << pageno << " for " << name << std::endl;
|
|
|
|
break;
|
2017-10-09 20:48:10 +05:00
|
|
|
}
|
2017-10-17 19:29:56 +05:00
|
|
|
TemplateEngine::Args_t page_args;
|
|
|
|
page_args["group"] = name;
|
|
|
|
page_args["threads"] = page;
|
|
|
|
page_args["pageno"] = std::to_string(pageno);
|
|
|
|
if (pageno)
|
|
|
|
page_args["prev_pageno"] = std::to_string(pageno - 1);
|
|
|
|
if (pageno + 1 < m_Pages)
|
|
|
|
page_args["next_pageno"] = std::to_string(pageno + 1);
|
|
|
|
fs::path boardPageFilename(name + "-" + std::to_string(pageno) + ".html");
|
|
|
|
if (m_TemplateEngine)
|
2017-10-09 20:48:10 +05:00
|
|
|
{
|
2017-10-17 19:29:56 +05:00
|
|
|
fs::path outfile = m_OutDir / boardPageFilename;
|
|
|
|
FileHandle_ptr out = OpenFile(outfile, eWrite);
|
|
|
|
if (out)
|
|
|
|
m_TemplateEngine->WriteTemplate("board.mustache", page_args, out);
|
|
|
|
else
|
|
|
|
std::clog << "failed to open board page " << outfile << std::endl;
|
2017-10-09 20:48:10 +05:00
|
|
|
}
|
2017-10-17 19:29:56 +05:00
|
|
|
|
|
|
|
++pageno;
|
2017-10-09 20:48:10 +05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-10-17 19:29:56 +05:00
|
|
|
}
|
2017-10-11 18:48:27 +05:00
|
|
|
|
2017-10-17 19:29:56 +05:00
|
|
|
bool StaticFileFrontend::AcceptsNewsgroup(const std::string &newsgroup) { return IsValidNewsgroup(newsgroup); }
|
2017-10-11 18:48:27 +05:00
|
|
|
|
2017-10-17 19:29:56 +05:00
|
|
|
bool StaticFileFrontend::AcceptsMessage(const std::string &msgid) { return IsValidMessageID(msgid); }
|
2017-10-09 20:48:10 +05:00
|
|
|
}
|