#include #include #include #include #include #include #include #include #include namespace nntpchan { 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) { } void StaticFileFrontend::ProcessNewMessage(const fs::path & fpath) { std::clog << "process message " << fpath << std::endl; auto file = OpenFile(fpath, eRead); if(file) { // read header RawHeader header; if(!ReadHeader(file, header)) { std::clog << "failed to read mime header" << std::endl; return; } // read body auto findMsgidFunc = [](const std::pair & item) -> bool { auto lower = ToLower(item.first); return (lower == "message-id") || (lower == "messageid"); }; 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; } std::string rootmsgid; auto findReferences = [](const std::pair & item) -> bool { auto lower = ToLower(item.first); return lower == "references"; }; 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)) { /** split off first element */ auto idx = std::find_if(s.begin(), s.end(), checkfunc); rootmsgid = s.substr(0, s.find(*idx)); } else { rootmsgid = references_itr->second; } } std::string rootmsgid_hash = sha1_hex(rootmsgid); 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)) { std::clog << "failed to write " << threadFilePath << std::endl; return; } } std::set newsgroups_list; auto findNewsgroupsFunc = [](const std::pair & 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); } nntpchan::model::BoardPage page; for(const auto & name : newsgroups_list) { uint32_t pageno = 0; while(pageno < m_Pages) { page.clear(); if(!m_MessageDB->LoadBoardPage(page, name, 10, m_Pages)) { std::clog << "cannot load board page "<< pageno << " for " << name << std::endl; break; } 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) { 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; } ++pageno; } } } } bool StaticFileFrontend::AcceptsNewsgroup(const std::string & newsgroup) { return IsValidNewsgroup(newsgroup); } bool StaticFileFrontend::AcceptsMessage(const std::string & msgid) { return IsValidMessageID(msgid); } }