/* This file is part of FediBlock-backend. * Copyright © 2020 tastytea * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include "rss.hpp" #include "cgi.hpp" #include "files.hpp" #include "git.hpp" #include "time.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include namespace FediBlock::rss { using fmt::format; using std::find; using std::getenv; using std::none_of; using std::ostream; using std::string; using std::string_view; using std::uint8_t; using std::chrono::system_clock; void write_rss(ostream &out, const vector &entries, const vector &tags) { string selfurl; char *env{getenv("HTTPS")}; if (env == nullptr || string(env) != "on") { selfurl = "http://"; } else { selfurl = "https://"; } env = getenv("SERVER_NAME"); if (env != nullptr) { selfurl += env; } const string baseurl{selfurl + "/blocklist/"}; env = getenv("REQUEST_URI"); if (env != nullptr) { selfurl += env; } constexpr string_view rss_time_format{"%a, %d %b %Y %T %z"}; pugi::xml_document doc; auto rss{doc.append_child("rss")}; rss.append_attribute("version") = "2.0"; rss.append_attribute("xmlns:atom") = "http://www.w3.org/2005/Atom"; auto channel{rss.append_child("channel")}; string tmp_title{"FediBlock: "}; string tmp_description{"The newest FediBlock entries. "}; if (tags.empty()) { tmp_title += "All tags"; tmp_description += "All tags"; } else { tmp_description += "Tags: "; } for (const auto &tag : tags) { if (tag != *tags.begin()) { tmp_title += ", "; tmp_description += ", "; } tmp_title += tag; tmp_description += tag; } tmp_description += "."; auto title{channel.append_child("title")}; title.text() = tmp_title.c_str(); auto link{channel.append_child("link")}; link.text() = baseurl.c_str(); auto description{channel.append_child("description")}; description.text() = tmp_description.c_str(); auto lastBuildDate{channel.append_child("lastBuildDate")}; lastBuildDate.text() = time::to_string(system_clock::now(), rss_time_format) .c_str(); for (const auto &entry : entries) { // If tags are specified and none of them match, go to the next entry. if (!tags.empty()) { // clang-format off if (none_of(entry.tags.begin(), entry.tags.end(), [&tags](const auto &tag) { return find(tags.begin(), tags.end(), tag) != tags.end(); })) { continue; } // clang-format on } auto item{channel.append_child("item")}; auto item_title{item.append_child("title")}; item_title.text() = entry.instance.c_str(); auto item_guid{item.append_child("guid")}; item_guid.append_attribute("isPermaLink") = "false"; item_guid.text() = format("FediBlock: {:s} {:s}", entry.report_time, entry.instance) .c_str(); auto item_pubDate{item.append_child("pubDate")}; item_pubDate.text() = time::to_string(entry.report_time, rss_time_format) .c_str(); auto item_link{item.append_child("link")}; item_link.text() = format("{:s}#{:s}", baseurl, entry.instance).c_str(); string tmp_item_description{format("

{:s}

" "

Tags: ", cgi::text2html(entry.description))}; for (const auto &tag : entry.tags) { if (tag != *entry.tags.begin()) { tmp_item_description += ", "; } tmp_item_description += tag; } tmp_item_description += "

Receipts:
    "; for (const auto &receipt : entry.receipts) { tmp_item_description += format( "
  • {0:s}
  • ", receipt); } tmp_item_description += "
"; if (!entry.screenshot_filepath.empty()) { tmp_item_description += "

Screenshot:
"; tmp_item_description += format( "

\n", baseurl, entry.screenshot_filepath); } auto item_description{item.append_child("description")}; item_description.append_child(pugi::node_cdata) .set_value(tmp_item_description.c_str()); } doc.print(out); } } // namespace FediBlock::rss using std::cerr; using std::cout; using std::exception; using std::runtime_error; using std::vector; using namespace FediBlock; using namespace FediBlock::rss; int main(int argc, char *argv[]) { const vector args(argv, argv + argc); git::init(true); bool remove_lockfile{false}; try { try { git::update_cached_repo(); remove_lockfile = true; } catch (const runtime_error &) { // Ignore, use old version of repo. } const auto entries{files::read_json_files(true)}; cout << "Content-Type: application/rss+xml\r\n\r\n"; write_rss(cout, entries, cgi::get_tags()); } catch (const exception &e) { cerr << "Error: " << e.what() << '\n'; } git::cleanup(remove_lockfile); return 0; }