288 lines
7.0 KiB
C++
288 lines
7.0 KiB
C++
/* This file is part of FediBlock-backend.
|
|
* Copyright © 2020, 2021 tastytea <tastytea@tastytea.de>
|
|
*
|
|
* 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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
#include "files.hpp"
|
|
|
|
#include "config.hpp"
|
|
#include "fs-compat.hpp"
|
|
#include "json.hpp"
|
|
#include "types.hpp"
|
|
|
|
#include <fmt/format.h>
|
|
#include <fmt/ostream.h> // For compatibility with fmt 4.
|
|
#include <sys/types.h>
|
|
#include <unistd.h>
|
|
|
|
#include <algorithm>
|
|
#include <cstdint>
|
|
#include <cstdlib>
|
|
#include <fstream>
|
|
#include <sstream>
|
|
#include <stdexcept>
|
|
#include <string>
|
|
|
|
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<uintmax_t>(-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<entry_type> read_json_files(const bool cache)
|
|
{
|
|
using namespace FediBlock::config;
|
|
|
|
vector<entry_type> 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().string()};
|
|
// 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
|
|
}
|
|
}
|
|
}
|
|
entry.json_url = format("https://{:s}/{:s}/{:s}/raw/branch/{:s}/{:s}",
|
|
forge_domain, forge_org, forge_repo_data,
|
|
forge_repo_data_branch,
|
|
path.filename().string());
|
|
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
|