/* * 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 . * * 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 #include #include #include #include #include #include #include #include #include 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& args) { std::vector 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; }