BastionGuard/src/pac/PacManager.cpp

362 lines
11 KiB
C++

/*
* BastionGuard™
* Copyright (C) 2025–2026 Calogero Scarnà
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* BastionGuard™ is a trademark of Calogero Scarnà.
* The BastionGuard™ name and branding are not licensed under the GPL.
*/
#include "PacManager.hpp"
#include "../SafeProcess.hpp"
#include <cstdlib>
#include <unistd.h>
#include <cstdio>
#include <array>
#include <filesystem>
#include <fstream>
#include <sstream>
#include <glib/gi18n.h>
#include <glibmm/miscutils.h>
#include <glibmm/shell.h>
namespace fs = std::filesystem;
bool PacManager::cmd_exists(const std::string& cmd) {
if (cmd.empty()) return false;
if (cmd.find('/') != std::string::npos) return access(cmd.c_str(), X_OK) == 0;
const char* configured_path = std::getenv("PATH");
std::istringstream dirs(configured_path ? configured_path : "/usr/local/bin:/usr/bin:/bin");
std::string dir;
while (std::getline(dirs, dir, ':')) {
if (dir.empty()) dir = ".";
if (access((fs::path(dir) / cmd).c_str(), X_OK) == 0) return true;
}
return false;
}
static bool run_user_servicectl(const std::vector<std::string>& args) {
std::vector<std::string> command = {
"/usr/libexec/bastionguard/bastionguard-service", "--user"};
command.insert(command.end(), args.begin(), args.end());
return bg_process::run(command).ok();
}
PacManager::Result PacManager::start_pacd_user() {
Result r; r.backend = "user-service-manager";
if (!cmd_exists("/usr/libexec/bastionguard/bastionguard-service")) {
r.message = _("gestore servizi BastionGuard non disponibile");
return r;
}
if (run_user_servicectl({"daemon-reload"}) &&
run_user_servicectl({"enable", "--now", "BastionGuard-pacd.service"})) {
r.ok = true;
r.message = _("bastionguard-pacd avviato (servizio utente)");
return r;
}
r.message = _("Impossibile avviare bastionguard-pacd (servizio utente)");
return r;
}
PacManager::Result PacManager::stop_pacd_user() {
Result r; r.backend = "user-service-manager";
if (!cmd_exists("/usr/libexec/bastionguard/bastionguard-service")) {
r.message = _("gestore servizi BastionGuard non disponibile");
return r;
}
// stop best-effort
run_user_servicectl({"disable", "--now", "BastionGuard-pacd.service"});
r.ok = true;
r.message = _("bastionguard-pacd fermato (servizio utente)");
return r;
}
PacManager::Result PacManager::enable_pac(const std::string& pac_url) {
auto r = enable_gnome(pac_url);
if (r.ok) return r;
r = enable_kde(pac_url);
if (r.ok) return r;
r = enable_xfce(pac_url);
if (r.ok) return r;
// headless fallback (NON PAC)
r = enable_env_fallback();
return r;
}
PacManager::Result PacManager::disable_pac() {
auto r = disable_gnome();
if (r.ok) return r;
r = disable_kde();
if (r.ok) return r;
r = disable_xfce();
if (r.ok) return r;
r = disable_env_fallback();
return r;
}
PacManager::Result PacManager::enable_gnome(const std::string& pac_url) {
Result r; r.backend = "gnome";
if (!cmd_exists("gsettings")) {
r.message = _("gsettings non disponibile");
return r;
}
// Imposta PAC (AUTO)
int rc1 = bg_process::run({"gsettings", "set", "org.gnome.system.proxy", "mode", "auto"}).exit_code;
int rc2 = bg_process::run({"gsettings", "set", "org.gnome.system.proxy", "autoconfig-url", pac_url}).exit_code;
if (rc1 == 0 && rc2 == 0) {
r.ok = true;
r.message = _("Proxy GNOME impostato su AUTO (PAC)");
return r;
}
r.message = _("Impossibile impostare proxy GNOME via gsettings");
return r;
}
PacManager::Result PacManager::disable_gnome() {
Result r; r.backend = "gnome";
if (!cmd_exists("gsettings")) {
r.message = _("gsettings non disponibile");
return r;
}
int rc = bg_process::run({"gsettings", "set", "org.gnome.system.proxy", "mode", "none"}).exit_code;
if (rc == 0) {
r.ok = true;
r.message = _("Proxy GNOME ripristinato a NONE");
return r;
}
r.message = _("Impossibile disabilitare proxy GNOME");
return r;
}
static fs::path kde_kioslaverc() {
return fs::path(Glib::get_home_dir()) / ".config" / "kioslaverc";
}
PacManager::Result PacManager::enable_kde(const std::string& pac_url) {
Result r; r.backend = "kde";
fs::path p = kde_kioslaverc();
try {
fs::create_directories(p.parent_path());
} catch (...) {}
if (cmd_exists("kwriteconfig6")) {
auto first = bg_process::run({"kwriteconfig6", "--file", p.string(),
"--group", "Proxy Settings", "--key", "ProxyType", "2"});
if (first.ok() && bg_process::run({"kwriteconfig6", "--file", p.string(),
"--group", "Proxy Settings", "--key", "AutoConfigScript", pac_url}).ok()) {
r.ok = true;
r.message = _("Proxy KDE impostato su PAC (kioslaverc)");
return r;
}
}
try {
std::string content;
if (fs::exists(p)) {
std::ifstream in(p);
std::stringstream ss;
ss << in.rdbuf();
content = ss.str();
}
auto pos = content.find("[Proxy Settings]");
if (pos != std::string::npos) {
auto next = content.find("\n[", pos + 1);
if (next == std::string::npos) content.erase(pos);
else content.erase(pos, next - pos);
}
std::ofstream out(p, std::ios::app);
out << "\n[Proxy Settings]\n";
out << "ProxyType=2\n";
out << "AutoConfigScript=" << pac_url << "\n";
out.close();
r.ok = true;
r.message = _("Proxy KDE impostato su PAC (fallback write kioslaverc)");
return r;
} catch (...) {
r.message = _("Errore scrittura kioslaverc");
return r;
}
}
PacManager::Result PacManager::disable_kde() {
Result r; r.backend = "kde";
fs::path p = kde_kioslaverc();
if (!fs::exists(p)) {
r.message = _("kioslaverc non esiste");
return r;
}
if (cmd_exists("kwriteconfig6")) {
if (bg_process::run({"kwriteconfig6", "--file", p.string(),
"--group", "Proxy Settings", "--key", "ProxyType", "0"}).ok()) {
r.ok = true;
r.message = _("Proxy KDE ripristinato a NONE");
return r;
}
}
try {
std::string content;
{
std::ifstream in(p);
std::stringstream ss;
ss << in.rdbuf();
content = ss.str();
}
auto tpos = content.find("ProxyType=");
if (tpos != std::string::npos) {
auto eol = content.find('\n', tpos);
content.replace(tpos, (eol == std::string::npos ? content.size()-tpos : eol-tpos),
"ProxyType=0");
} else {
content += "\n[Proxy Settings]\nProxyType=0\n";
}
std::ofstream out(p, std::ios::trunc);
out << content;
out.close();
r.ok = true;
r.message = _("Proxy KDE ripristinato a NONE (fallback)");
return r;
} catch (...) {
r.message = _("Errore scrittura kioslaverc");
return r;
}
}
PacManager::Result PacManager::enable_xfce(const std::string& pac_url) {
Result r; r.backend = "xfce";
if (!cmd_exists("xfconf-query")) {
r.message = "xfconf-query non disponibile";
return r;
}
const int rc1 = bg_process::run({"xfconf-query", "-c", "xfce4-settings-manager",
"-p", "/proxy/mode", "-s", "2"}).exit_code;
const int rc2 = bg_process::run({"xfconf-query", "-c", "xfce4-settings-manager",
"-p", "/proxy/autoconfig-url", "-s", pac_url}).exit_code;
if (rc1 == 0 && rc2 == 0) {
r.ok = true;
r.message = _("Proxy XFCE impostato su AUTO (PAC)");
return r;
}
r.message = _("Impossibile impostare proxy XFCE (canale/proprietà potrebbero differire)");
return r;
}
PacManager::Result PacManager::disable_xfce() {
Result r; r.backend = "xfce";
if (!cmd_exists("xfconf-query")) {
r.message = "xfconf-query non disponibile";
return r;
}
int rc = bg_process::run({"xfconf-query", "-c", "xfce4-settings-manager", "-p", "/proxy/mode", "-s", "0"}).exit_code;
if (rc == 0) {
r.ok = true;
r.message = _("Proxy XFCE ripristinato a NONE");
return r;
}
r.message = _("Impossibile disabilitare proxy XFCE");
return r;
}
static fs::path envd_file() {
return fs::path(Glib::get_home_dir()) / ".config" / "environment.d" / "90-bastionguard-proxy.conf";
}
PacManager::Result PacManager::enable_env_fallback() {
Result r; r.backend = "env";
try {
fs::path f = envd_file();
fs::create_directories(f.parent_path());
// Proxy locale (adattalo alla tua porta reale)
std::ofstream out(f, std::ios::trunc);
out << "http_proxy=http://127.0.0.1:3129\n";
out << "https_proxy=http://127.0.0.1:3129\n";
out << "HTTP_PROXY=http://127.0.0.1:3129\n";
out << "HTTPS_PROXY=http://127.0.0.1:3129\n";
out.close();
r.ok = true;
r.message = _("Fallback headless: environment.d impostato (richiede relogin/riavvio sessione)");
return r;
} catch (...) {
r.message = _("Impossibile scrivere environment.d fallback");
return r;
}
}
PacManager::Result PacManager::disable_env_fallback() {
Result r; r.backend = "env";
try {
fs::path f = envd_file();
if (fs::exists(f)) fs::remove(f);
r.ok = true;
r.message = _("Fallback headless: environment.d rimosso");
return r;
} catch (...) {
r.message = _("Impossibile rimuovere environment.d fallback");
return r;
}
}
PacManager::Result PacManager::enable_pac_with_pacd(const std::string& pac_url) {
auto s = start_pacd_user();
if (!s.ok) return s;
return enable_pac(pac_url);
}
PacManager::Result PacManager::disable_pac_with_pacd() {
auto d = disable_pac();
stop_pacd_user();
return d;
}