Skip to content
API Documentation
BastionGuard™
bankgui-main.cpp
Go to the documentation of this file.
1/*
2 * BastionGuard™
3 * Copyright (C) 2025–2026 Calogero Scarnà
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, version 3.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <https://www.gnu.org/licenses/>.
16 *
17 * BastionGuard™ is a trademark of Calogero Scarnà.
18 * The BastionGuard™ name and branding are not licensed under the GPL.
19 */
20
21#include <gtkmm.h>
22#include <giomm.h>
23#include <glib/gi18n.h>
24#include <csignal>
25#include <iostream>
26#include <cstdio>
27#include <memory>
28#include <array>
29#include <fstream>
30#include <unistd.h>
31#include <clocale>
32#include <glibmm/miscutils.h>
33#include "StyleProvider.hpp"
34#include "Resource.hpp"
35
36#ifndef LOCALEDIR
37#error "LOCALEDIR non definita per questo target"
38#endif
39
40class BankGuiWindow : public Gtk::Window {
41public:
43 set_title("");
44 set_resizable(false);
45 set_default_size(640, 300);
47
48
49 auto main_box = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL, 0);
50 set_child(*main_box);
51
52
53 auto sidebar = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::VERTICAL, 10);
54 sidebar->set_size_request(240, -1);
55 sidebar->get_style_context()->add_class("sidebar");
56
57
58 {
59 auto header = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL, 8);
60 header->get_style_context()->add_class("sidebar-header");
61
62 try {
63 auto pixbuf = Gdk::Pixbuf::create_from_file(resource("logo-sidebar.png"), 96, 96, true);
64 auto logo = Gtk::make_managed<Gtk::Image>(pixbuf);
65 header->append(*logo);
66 } catch (...) {
67 std::cerr << _("⚠ Impossibile caricare data/logo-sidebar.png") << "\n";
68 }
69
70 auto lbl = Gtk::make_managed<Gtk::Label>("BastionGuard");
71 lbl->set_halign(Gtk::Align::START);
72 lbl->get_style_context()->add_class("sidebar-title");
73 header->append(*lbl);
74
75 sidebar->append(*header);
76 }
77
78
79 auto spacer = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::VERTICAL);
80 spacer->set_vexpand(true);
81 sidebar->append(*spacer);
82
83 main_box->append(*sidebar);
84
85
86 auto content_box = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::VERTICAL, 10);
87 content_box->set_margin(24);
88 content_box->set_hexpand(true);
89 content_box->get_style_context()->add_class("content");
90 main_box->append(*content_box);
91
92 auto label = Gtk::make_managed<Gtk::Label>(
93 _("Inserisci l'URL della banca o un identificatore:")
94 );
95 label->set_wrap(true);
96 label->set_halign(Gtk::Align::START);
97 content_box->append(*label);
98
99 entry_.set_placeholder_text("https://www.miabanca.it");
100 entry_.set_activates_default(true);
101 content_box->append(entry_);
102
103 auto buttons = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL, 8);
104 buttons->set_halign(Gtk::Align::END);
105
106 btn_cancel_.set_label(_("Annulla"));
107 btn_cancel_.signal_clicked().connect(sigc::mem_fun(*this, &BankGuiWindow::on_cancel_clicked));
108 buttons->append(btn_cancel_);
109
110 btn_open_.set_label(_("Apri in Secure Browser"));
111 btn_open_.signal_clicked().connect(sigc::mem_fun(*this, &BankGuiWindow::on_open_clicked));
112 buttons->append(btn_open_);
113
114 content_box->append(*buttons);
115}
116
117
118protected:
119 bool on_close_request() override {
121 return false;
122 }
123
124private:
125 Gtk::Entry entry_;
126 Gtk::Button btn_open_;
127 Gtk::Button btn_cancel_;
128 Glib::RefPtr<Gio::Subprocess> sandbox_proc_;
129 std::string sandbox_cmd_;
130
131 // ===== HeaderBar come in MainWindow =====
133 auto header = Gtk::make_managed<Gtk::HeaderBar>();
134 header->set_show_title_buttons(false);
135 header->set_decoration_layout("");
136 header->get_style_context()->add_class("custom-headerbar");
137
138 // Titolo centrato
139 auto title_label = Gtk::make_managed<Gtk::Label>("BastionGuard Secure Browser");
140 title_label->get_style_context()->add_class("header-title");
141 title_label->set_halign(Gtk::Align::CENTER);
142 title_label->set_valign(Gtk::Align::CENTER);
143 header->set_title_widget(*title_label);
144
145 // ---- Destra (pulsanti finestra) ----
146 auto header_right = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL);
147 header_right->get_style_context()->add_class("header-right");
148
149 try {
150 auto pix_min = Gdk::Pixbuf::create_from_file(resource("icon-minimize.png"), 32, 32, true);
151 auto btn_min = Gtk::make_managed<Gtk::Button>();
152 btn_min->set_child(*Gtk::make_managed<Gtk::Image>(pix_min));
153 btn_min->get_style_context()->add_class("header-button");
154 header_right->append(*btn_min);
155 btn_min->signal_clicked().connect([this]() { minimize(); });
156 } catch (...) {
157 std::cerr << _("⚠ Errore caricamento icona minimize") << "\n";
158 }
159
160 try {
161 auto pix_close = Gdk::Pixbuf::create_from_file(resource("icon-close.png"), 32, 32, true);
162 auto btn_close = Gtk::make_managed<Gtk::Button>();
163 btn_close->set_child(*Gtk::make_managed<Gtk::Image>(pix_close));
164 btn_close->get_style_context()->add_class("header-button");
165 header_right->append(*btn_close);
166 btn_close->signal_clicked().connect([this]() {
168 this->hide();
169 });
170 } catch (...) {
171 std::cerr << _("⚠ Errore caricamento icona close") << "\n";
172 }
173
174 header->pack_end(*header_right);
175 set_titlebar(*header);
176 }
177
178
179 void minimize() {
180#if GTKMM_CHECK_VERSION(4,10,0)
181 this->hide();
182#endif
183 }
184
187 hide();
188 }
189
191 auto url = entry_.get_text();
192 if (url.empty()) {
193 show_warning(_("Inserisci un URL valido."), _("Il campo non può essere vuoto."));
194 return;
195 }
196
197 set_sensitive(false);
198 try {
199 sandbox_cmd_ = "BastionGuard-bankopener \"" + url + "\"";
200 sandbox_proc_ = Gio::Subprocess::create(
201 {"BastionGuard-bankopener", url},
202 Gio::Subprocess::Flags::NONE
203 );
204 sandbox_proc_->wait_check_async(
205 sigc::mem_fun(*this, &BankGuiWindow::on_sandbox_finished)
206 );
207 } catch (const Glib::Error& ex) {
208 show_warning(_("Errore avvio subprocess."), ex.what());
209 set_sensitive(true);
210 }
211 }
212
213 void show_warning(const Glib::ustring& msg, const Glib::ustring& secondary = {}) {
214 auto* dialog = new Gtk::MessageDialog(
215 *this, msg, false, Gtk::MessageType::WARNING, Gtk::ButtonsType::OK, true);
216 if (!secondary.empty()) dialog->set_secondary_text(secondary);
217 dialog->set_modal(true);
218 dialog->signal_response().connect([dialog](int){ dialog->hide(); delete dialog; });
219 dialog->present();
220 }
221
222 void on_sandbox_finished(const Glib::RefPtr<Gio::AsyncResult>& /*res*/) {
223 hide();
224 }
225
227 if (!sandbox_proc_) return;
228 // Only terminate the subprocess started by this window. Never search
229 // the entire process table with an untrusted URL in a shell command.
230 sandbox_proc_->force_exit();
231 sandbox_proc_.reset();
232 }
233};
234
235static bool load_lang_conf()
236{
237 const std::string lang_conf = Glib::get_home_dir() + "/.config/BastionGuard/lang.conf";
238 std::ifstream f(lang_conf);
239 if (!f.is_open())
240 return false;
241
242 std::string line;
243 bool any = false;
244
245 while (std::getline(f, line)) {
246 if (line.empty() || line[0] == '#')
247 continue;
248
249 auto pos = line.find('=');
250 if (pos == std::string::npos)
251 continue;
252
253 std::string key = line.substr(0, pos);
254 std::string val = line.substr(pos + 1);
255
256 auto ltrim = [](std::string& s){
257 s.erase(0, s.find_first_not_of(" \t\r\n"));
258 };
259 auto rtrim = [](std::string& s){
260 s.erase(s.find_last_not_of(" \t\r\n") + 1);
261 };
262 ltrim(key); rtrim(key);
263 ltrim(val); rtrim(val);
264
265 if (!key.empty() && !val.empty()) {
266 setenv(key.c_str(), val.c_str(), 1);
267 any = true;
268 }
269 }
270
271 return any;
272}
273
274int main(int argc, char* argv[])
275{
276 bool loaded = load_lang_conf();
277
278 std::setlocale(LC_ALL, "");
279
280
281 bindtextdomain("BastionGuard", LOCALEDIR);
282 bind_textdomain_codeset("BastionGuard", "UTF-8");
283 textdomain("BastionGuard");
284
285 if (loaded) {
286 std::cerr << _("[Lang] Config caricata da ~/.config/BastionGuard/lang.conf") << "\n";
287 } else {
288 std::cerr << _("[Lang] Nessuna config trovata, uso locale di sistema") << "\n";
289 }
290
291 auto app = Gtk::Application::create("org.BastionGuard.bankgui");
292 load_style();
293 return app->make_window_and_run<BankGuiWindow>(argc, argv);
294}
295
#define _(s)
int main(int argc, char *argv[])
static bool load_lang_conf()
#define LOCALEDIR
Gtk::Button btn_cancel_
void terminate_sandbox()
Glib::RefPtr< Gio::Subprocess > sandbox_proc_
void on_cancel_clicked()
bool on_close_request() override
std::string sandbox_cmd_
void on_sandbox_finished(const Glib::RefPtr< Gio::AsyncResult > &)
Gtk::Entry entry_
void show_warning(const Glib::ustring &msg, const Glib::ustring &secondary={})
Gtk::Button btn_open_
std::string resource(const std::string &rel)
Definition Resource.hpp:31
void load_style()