/* 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 "files.hpp" #include "fs-compat.hpp" #include "json.hpp" #include "types.hpp" #include #include // For compatibility with fmt 4. #include #include #include #include #include #include #include #include #include namespace FediBlock::files { using fmt::format; using std::getenv; using std::ifstream; using std::ofstream; using std::runtime_error; using std::sort; using std::string; using std::stringstream; using std::to_string; using std::uintmax_t; string _tmpdir; fs::path get_tmpdir() { if (_tmpdir.empty()) { _tmpdir = fs::temp_directory_path() / "fediblock-backend-XXXXXX"; if (mkdtemp(&_tmpdir[0]) == nullptr) // mkdtemp() modifies _tmpdir. { throw runtime_error{"Could not create temporary directory: " + _tmpdir}; } } return _tmpdir; } void remove_tmpdir() { if (fs::remove_all(get_tmpdir()) == static_cast(-1)) { throw runtime_error{"Couldn't remove temporary directory: " + get_tmpdir().string()}; } } fs::path get_xdg_dir(const string &identifier) { const string xdg_variable{format("XDG_{:s}_HOME", identifier)}; char *env{getenv(xdg_variable.c_str())}; if (env != nullptr) { auto path{fs::path(env) / "fediblock-backend"}; fs::create_directories(path); return path; } return {}; } fs::path get_datadir() { auto path{get_xdg_dir("DATA")}; if (!path.empty()) { return path; } char *env{getenv("HOME")}; if (env != nullptr) { path = fs::path(env); #if defined(__linux__) path /= ".local/share/fediblock-backend"; #else path /= ".fediblock-backend"; #endif if (!fs::exists(path)) { fs::create_directories(path); } return path; } throw runtime_error{"Could not find data dir"}; } fs::path get_cachedir() { auto path{get_xdg_dir("CACHE")}; if (!path.empty()) { return path; } char *env{getenv("HOME")}; if (env != nullptr) { path = fs::path(env); path /= ".cache/fediblock-backend"; if (!fs::exists(path)) { fs::create_directories(path); } return path; } throw runtime_error{"Could not find data dir"}; } string get_access_token() { const fs::path access_token_path{get_datadir() / "gitea_access_token"}; ifstream file(access_token_path); if (!file.good()) { throw runtime_error{format("Could not read access token: {:s}", access_token_path.string())}; } stringstream ss; ss << file.rdbuf(); return ss.str().substr(0, ss.str().find('\n')); // Remove trailing newline. } vector read_json_files(const bool cache) { vector entries; // clang-format off const auto dir_iter{[cache] { if (cache) { return fs::directory_iterator(files::get_cachedir() / "repo"); } return fs::directory_iterator(files::get_tmpdir() / "repo"); }()}; // clang-format on for (const fs::path &path : dir_iter) { if (path.filename().string()[0] == '.') { continue; } if (path.extension() != ".json") { continue; } ifstream file(path); if (!file.good()) { throw runtime_error{"Could not open file: " + path.string()}; } stringstream ss; ss << file.rdbuf(); auto entry{json::from_json(ss.str())}; if (!entry.screenshot_filepaths.empty()) { std::uint8_t counter{0}; for (auto &screenshot : entry.screenshot_filepaths) { ++counter; const fs::path sh_path{screenshot}; // clang-format off const auto compat_path{path.parent_path() /= path.stem() /= sh_path.extension()}; // clang-format on if (entry.screenshot_filepaths.size() == 1 && fs::exists(compat_path)) { // Compatibility for old entries. screenshot = path.stem().string() += sh_path.extension(); } else { // clang-format off screenshot = (path.stem().string() += '-') += to_string(counter) += sh_path.extension(); // clang-format on } } } entries.push_back(entry); } // clang-format off sort(entries.begin(), entries.end(), [](const auto &a, const auto &b) { return a.report_time > b.report_time; }); // Newest first. // clang-format on return entries; } fs::path get_lockfile() { #if defined(__linux__) fs::path path{format("/run/user/{:d}", getuid())}; // pam_systemd if (!fs::exists(path)) { path = "/run/lock"; // Linux } #else fs::path path{"/var/lock"}; // UNIX if (!fs::exists(path)) { path = "/var/run"; // UNIX } #endif if (!fs::exists(path)) { path = "/tmp"; // fallback } return path / "fediblock.lock"; } bool create_lockfile() { const string lockfile{get_lockfile()}; if (fs::exists(lockfile)) { return false; } ofstream file; file.exceptions(std::ofstream::failbit | std::ofstream::badbit); try { file.open(lockfile); file << getpid(); } catch (const std::ofstream::failure &e) { throw std::runtime_error{ format("Could not write lockfile: {:s}", e.what())}; } return true; } void remove_lockfile() { const string lockfile{get_lockfile()}; if (fs::exists(lockfile)) { fs::remove(lockfile); } } } // namespace FediBlock::files