This repository has been archived on 2021-03-22. You can view files and clone it, but cannot push or open issues or pull requests.
backend/src/files.cpp

282 lines
6.7 KiB
C++

/* This file is part of FediBlock-backend.
* Copyright © 2020 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 "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)
{
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()};
// 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