/* This file is part of FediBlock-backend. * Copyright © 2020, 2021 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 "cgi.hpp" #include "files.hpp" #include "fs-compat.hpp" #include "git.hpp" #include "time.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace FediBlock::cgi { using fmt::format; using std::getline; using std::ios; using std::map; using std::ofstream; using std::runtime_error; using std::string; using std::string_view; using std::stringstream; using std::transform; using std::vector; using std::chrono::system_clock; entry_type parse_formdata() { entry_type entry; cgicc::Cgicc cgi; // Catch non-targeted spam. if (!cgi("url").empty()) { throw SpamException{}; } if (!captcha_valid(static_cast(std::stoul(cgi("captcha_id"))), cgi("captcha_answer"))) { // throw CaptchaException{}; std::cout << "DEBUG: Captcha invalid. You can ignore this line.\r\n"; } entry.instance = cgi("instance"); entry.tags = string_to_vector(cgi("tags")); transform(entry.tags.begin(), entry.tags.end(), entry.tags.begin(), [](const auto &tag) { return tolower(tag); }); entry.receipts = string_to_vector(cgi("receipts")); entry.description = cgi("description"); entry.report_time = time::to_string(system_clock::now()); std::uint8_t screenshot_counter{1}; for (const auto &screenshot : cgi.getFiles()) { constexpr size_t size_limit{1024 * 512}; if (screenshot.getDataLength() > size_limit) { throw runtime_error{format("The screenshot “{0:s}” is too big. " "The limit is {1:.1f} kilobyte (KiB).", screenshot.getFilename(), size_limit / 1024.0)}; } const string filepath{ files::get_tmpdir() / format("{:s}-{:d}{:s}", git::get_branch_name(), screenshot_counter, fs::path(screenshot.getFilename()).extension().string())}; ofstream file{filepath, ios::binary}; if (!file.good()) { throw runtime_error{"Could not open temporary file: " + filepath}; } screenshot.writeToStream(file); entry.screenshot_filepaths.push_back(filepath); ++screenshot_counter; } return entry; } vector string_to_vector(const string_view str) { vector vec; stringstream input{str.data()}; string element; while (getline(input, element, ',')) { if (!element.empty()) { const size_t startpos{element.find_first_not_of(' ')}; if (element[startpos] == *element.end()) { continue; } const size_t length{element.find_last_not_of(' ') - startpos + 1}; vec.push_back(element.substr(startpos, length)); } } return vec; } vector get_tags() { cgicc::Cgicc cgi; vector tags_form; vector tags_string; cgi.getElement("tags[]", tags_form); for (const auto &element : tags_form) { const string tag{element.getValue()}; if (!tag.empty()) { const auto new_tags{string_to_vector(tolower(tag))}; tags_string.insert(tags_string.end(), std::make_move_iterator(new_tags.begin()), std::make_move_iterator(new_tags.end())); } } return tags_string; } string tolower(const string_view str) { string result; const auto unistr{icu::UnicodeString(str.data(), "UTF-8").toLower()}; unistr.toUTF8String(result); return result; } string text2html(string text) { static const map entities{{"<", "<"}, {">", ">"}}; static const map html{{"\r\n", "
"}}; for (const auto &replacementmap : {entities, html}) { for (const auto &repl : replacementmap) { size_t pos{0}; while ((pos = text.find(repl.first, pos)) != string::npos) { text.replace(pos, repl.first.size(), repl.second); pos += repl.second.size(); } } } return text; } bool captcha_valid(std::uint8_t id, const string_view answer) { std::array answers{"2", "6", "17", "12", "4", "1"}; if (answers.at(id) == answer) { return true; } return false; } } // namespace FediBlock::cgi const char *SpamException::what() const noexcept { return "Spam detected."; } const char *CaptchaException::what() const noexcept { return "The solution to the captcha is not correct."; }