Skip to content
API Documentation
BastionGuard™
VpnPage.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 "vpn/VpnPage.hpp"
22
23#include <glibmm/i18n.h>
24#include <glibmm/main.h>
25#include <thread>
26#include <utility>
27
28using namespace BastionGuard;
29
31 : Gtk::Box(Gtk::Orientation::VERTICAL, 14) {
32 backend_ = std::make_unique<WireguardBackend>("wg0");
33 build_ui();
34
35 append_log(_("Pagina VPN inizializzata."));
36
37 std::string missing;
38 if (!backend_->tooling_available(&missing)) {
39 append_log(_("⚠ Strumenti WireGuard mancanti: ") + missing +
40 _(". Installa il pacchetto 'wireguard-tools'."));
41 btn_connect_.set_sensitive(false);
42 btn_gen_keys_.set_sensitive(false);
43 btn_generate_.set_sensitive(false);
44 }
45
47
48
49 poll_conn_ = Glib::signal_timeout().connect(
50 sigc::mem_fun(*this, &VpnPage::on_poll_status), 4000);
51}
52
54 if (alive_) alive_->store(false);
55 if (poll_conn_.connected()) poll_conn_.disconnect();
56}
57
58// ---------------------------------------------------------------------------
59// Helper layout: riga "etichetta + campo"
60// ---------------------------------------------------------------------------
61Gtk::Box* VpnPage::labeled_row(const std::string& label, Gtk::Widget& field) {
62 auto row = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL, 8);
63 auto lbl = Gtk::make_managed<Gtk::Label>(label);
64 lbl->set_halign(Gtk::Align::START);
65 lbl->set_size_request(180, -1);
66 lbl->set_xalign(0.0f);
67 field.set_hexpand(true);
68 row->append(*lbl);
69 row->append(field);
70 return row;
71}
72
73// ---------------------------------------------------------------------------
74// Costruzione UI
75// ---------------------------------------------------------------------------
77 set_margin(20);
78 get_style_context()->add_class("content");
79
80 lbl_title_.set_text(_("VPN WireGuard"));
81 lbl_title_.set_halign(Gtk::Align::START);
82 lbl_title_.get_style_context()->add_class("page-title");
83 append(lbl_title_);
84
85 lbl_subtitle_.set_text(
86 _("Connettiti al tuo server WireGuard e maschera il tuo IP pubblico."));
87 lbl_subtitle_.set_halign(Gtk::Align::START);
88 lbl_subtitle_.get_style_context()->add_class("page-subtitle");
89 append(lbl_subtitle_);
90
91 // ===================================================================
92 // Corpo a due colonne: a sinistra le tab, a destra il log.
93 // ===================================================================
94 auto columns = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL, 16);
95 columns->set_vexpand(true);
96 columns->set_margin_top(10);
97
98 // --- Colonna sinistra: notebook con le schede ---
99 notebook_.set_vexpand(true);
100 notebook_.set_hexpand(true);
101 notebook_.append_page(*build_tab_connection(), _("Connessione"));
102 notebook_.append_page(*build_tab_config(), _("Configurazione"));
103 notebook_.append_page(*build_tab_killswitch(), _("Kill-switch"));
104 columns->append(notebook_);
105
106 // --- Colonna destra: pannello informativo ---
107 auto right_col = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::VERTICAL, 12);
108 right_col->set_size_request(320, -1); // larghezza fissa del pannello
109 right_col->set_vexpand(true);
110
111 // Log (riempie il resto della colonna destra)
112 auto log_lbl = Gtk::make_managed<Gtk::Label>(_("Log"));
113 log_lbl->set_halign(Gtk::Align::START);
114 log_lbl->get_style_context()->add_class("section-label");
115 right_col->append(*log_lbl);
116
117 txt_log_.set_editable(false);
118 txt_log_.set_monospace(true);
119 txt_log_.set_wrap_mode(Gtk::WrapMode::WORD_CHAR);
120 log_scroller_.set_child(txt_log_);
121 log_scroller_.set_vexpand(true);
122 log_scroller_.set_policy(Gtk::PolicyType::AUTOMATIC, Gtk::PolicyType::AUTOMATIC);
123 right_col->append(log_scroller_);
124
125 columns->append(*right_col);
126 append(*columns);
127}
128
130 tab_conn_.set_margin(14);
131
132 auto btn_row = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL, 8);
133 btn_connect_.set_label(_("Connetti"));
134 btn_connect_.get_style_context()->add_class("suggested-action");
135 btn_disconnect_.set_label(_("Disconnetti"));
136 btn_disconnect_.set_sensitive(false);
137 spinner_.set_visible(false);
138 btn_row->append(btn_connect_);
139 btn_row->append(btn_disconnect_);
140 btn_row->append(spinner_);
141 tab_conn_.append(*btn_row);
142
143 lbl_state_.set_text(_("Stato: disconnesso"));
144 lbl_state_.set_halign(Gtk::Align::START);
145 lbl_endpoint_.set_text(_("Endpoint: —"));
146 lbl_endpoint_.set_halign(Gtk::Align::START);
147 tab_conn_.append(lbl_state_);
148 tab_conn_.append(lbl_endpoint_);
149
150 auto sep = Gtk::make_managed<Gtk::Separator>(Gtk::Orientation::HORIZONTAL);
151 tab_conn_.append(*sep);
152
153 auto ip_lbl = Gtk::make_managed<Gtk::Label>(_("IP pubblico"));
154 ip_lbl->set_halign(Gtk::Align::START);
155 ip_lbl->get_style_context()->add_class("section-label");
156 tab_conn_.append(*ip_lbl);
157
158 lbl_ip_before_.set_text(_("Prima del tunnel: —"));
159 lbl_ip_before_.set_halign(Gtk::Align::START);
160 lbl_ip_after_.set_text(_("Con tunnel attivo: —"));
161 lbl_ip_after_.set_halign(Gtk::Align::START);
162 btn_refresh_ip_.set_label(_("Aggiorna IP"));
163 btn_refresh_ip_.set_halign(Gtk::Align::START);
165 tab_conn_.append(lbl_ip_after_);
167
168 btn_connect_.signal_clicked().connect(
169 sigc::mem_fun(*this, &VpnPage::on_connect_clicked));
170 btn_disconnect_.signal_clicked().connect(
171 sigc::mem_fun(*this, &VpnPage::on_disconnect_clicked));
172 btn_refresh_ip_.signal_clicked().connect(
173 sigc::mem_fun(*this, &VpnPage::on_refresh_ip_clicked));
174
175 return &tab_conn_;
176}
177
179 tab_config_.set_margin(14);
180
181 // --- Sezione chiavi client ---
182 auto keys_lbl = Gtk::make_managed<Gtk::Label>(_("Chiavi del client"));
183 keys_lbl->set_halign(Gtk::Align::START);
184 keys_lbl->get_style_context()->add_class("section-label");
185 tab_config_.append(*keys_lbl);
186
187 entry_client_priv_.set_editable(false);
188 entry_client_priv_.set_placeholder_text(_("(generata localmente)"));
189 entry_client_priv_.set_visibility(false); // nascondi la chiave privata
190 entry_client_pub_.set_editable(false);
191 entry_client_pub_.set_placeholder_text(_("(da comunicare al tuo server)"));
192 btn_gen_keys_.set_label(_("Genera chiavi"));
193 btn_gen_keys_.set_halign(Gtk::Align::START);
194
195 tab_config_.append(*labeled_row(_("Chiave privata:"), entry_client_priv_));
196 tab_config_.append(*labeled_row(_("Chiave pubblica:"), entry_client_pub_));
198
199 auto sep1 = Gtk::make_managed<Gtk::Separator>(Gtk::Orientation::HORIZONTAL);
200 tab_config_.append(*sep1);
201
202 // --- Sezione dati server ---
203 auto srv_lbl = Gtk::make_managed<Gtk::Label>(_("Dati del tuo server"));
204 srv_lbl->set_halign(Gtk::Align::START);
205 srv_lbl->get_style_context()->add_class("section-label");
206 tab_config_.append(*srv_lbl);
207
208 entry_server_pub_.set_placeholder_text(_("Chiave pubblica del server"));
209 entry_endpoint_.set_placeholder_text(_("es. vpn.miosito.it:51820"));
210 entry_address_.set_placeholder_text(_("es. 10.0.0.2/32"));
211 entry_dns_.set_placeholder_text(_("es. 10.0.0.1 (opzionale)"));
212 entry_allowed_.set_placeholder_text(_("0.0.0.0/0, ::/0"));
213 entry_allowed_.set_text("0.0.0.0/0, ::/0");
214
215 tab_config_.append(*labeled_row(_("Chiave pubblica server:"), entry_server_pub_));
216 tab_config_.append(*labeled_row(_("Endpoint (host:porta):"), entry_endpoint_));
217 tab_config_.append(*labeled_row(_("Indirizzo client:"), entry_address_));
218 tab_config_.append(*labeled_row(_("DNS:"), entry_dns_));
219 tab_config_.append(*labeled_row(_("AllowedIPs:"), entry_allowed_));
220
221 btn_generate_.set_label(_("Genera e carica"));
222 btn_generate_.get_style_context()->add_class("suggested-action");
223 btn_generate_.set_halign(Gtk::Align::START);
225
226 auto sep2 = Gtk::make_managed<Gtk::Separator>(Gtk::Orientation::HORIZONTAL);
227 tab_config_.append(*sep2);
228
229 // --- Sezione import alternativo ---
230 auto imp_lbl = Gtk::make_managed<Gtk::Label>(
231 _("In alternativa, importa un file .conf esistente"));
232 imp_lbl->set_halign(Gtk::Align::START);
233 imp_lbl->get_style_context()->add_class("section-label");
234 tab_config_.append(*imp_lbl);
235
236 auto imp_row = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL, 8);
237 entry_conf_path_.set_hexpand(true);
238 entry_conf_path_.set_placeholder_text(_("/percorso/al/tuo/wg0.conf"));
239 btn_browse_.set_label(_("Sfoglia…"));
240 btn_import_.set_label(_("Importa"));
241 imp_row->append(entry_conf_path_);
242 imp_row->append(btn_browse_);
243 imp_row->append(btn_import_);
244 tab_config_.append(*imp_row);
245
246 btn_gen_keys_.signal_clicked().connect(
247 sigc::mem_fun(*this, &VpnPage::on_generate_keys_clicked));
248 btn_generate_.signal_clicked().connect(
249 sigc::mem_fun(*this, &VpnPage::on_generate_config_clicked));
250 btn_browse_.signal_clicked().connect(
251 sigc::mem_fun(*this, &VpnPage::on_browse_clicked));
252 btn_import_.signal_clicked().connect(
253 sigc::mem_fun(*this, &VpnPage::on_import_clicked));
254
255 // La scheda config può crescere: avvolgila in uno scroller.
256 auto scroller = Gtk::make_managed<Gtk::ScrolledWindow>();
257 scroller->set_child(tab_config_);
258 scroller->set_policy(Gtk::PolicyType::NEVER, Gtk::PolicyType::AUTOMATIC);
259 return scroller;
260}
261
263 tab_ks_.set_margin(14);
264
265 auto row = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL, 8);
266 auto lbl = Gtk::make_managed<Gtk::Label>(
267 _("Blocca tutto il traffico se il tunnel cade"));
268 lbl->set_hexpand(true);
269 lbl->set_halign(Gtk::Align::START);
270 switch_ks_.set_valign(Gtk::Align::CENTER);
271 row->append(*lbl);
272 row->append(switch_ks_);
273 tab_ks_.append(*row);
274
275 auto note = Gtk::make_managed<Gtk::Label>(
276 _("Quando attivo, il traffico in uscita è consentito solo attraverso "
277 "il tunnel WireGuard. Se la VPN cade, la connessione viene bloccata."));
278 note->set_halign(Gtk::Align::START);
279 note->set_wrap(true);
280 note->get_style_context()->add_class("dim-label");
281 tab_ks_.append(*note);
282
283 switch_ks_.property_active().signal_changed().connect(
284 sigc::mem_fun(*this, &VpnPage::on_killswitch_toggled));
285
286 return &tab_ks_;
287}
288
289// ---------------------------------------------------------------------------
290// Helpers
291// ---------------------------------------------------------------------------
292void VpnPage::append_log(const std::string& line) {
293 auto buf = txt_log_.get_buffer();
294 buf->insert(buf->end(), line + "\n");
295 auto mark = buf->create_mark(buf->end());
296 txt_log_.scroll_to(mark);
297 buf->delete_mark(mark);
298}
299
300void VpnPage::set_busy(bool b) {
301 busy_ = b;
302 spinner_.set_visible(b);
303 if (b) spinner_.start();
304 else spinner_.stop();
305
306 const bool enable = !b;
307 btn_connect_.set_sensitive(enable);
308 btn_disconnect_.set_sensitive(enable);
309 btn_import_.set_sensitive(enable);
310 btn_generate_.set_sensitive(enable);
311 btn_gen_keys_.set_sensitive(enable);
312 btn_refresh_ip_.set_sensitive(enable);
313 btn_browse_.set_sensitive(enable);
314}
315
317 const bool enable = !busy_;
318 if (st.up) {
319 lbl_state_.set_text(_("Stato: connesso"));
320 lbl_state_.get_style_context()->remove_class("state-down");
321 lbl_state_.get_style_context()->add_class("state-up");
322 // Endpoint: preferisci quello riportato da `wg show`; se assente
323 // (perché richiede privilegi) usa quello della config caricata.
324 std::string ep = st.endpoint;
325 if (ep.empty()) ep = backend_->configured_endpoint();
326 lbl_endpoint_.set_text(
327 _("Endpoint: ") + (ep.empty() ? std::string("—") : ep));
328 btn_connect_.set_sensitive(false);
329 btn_disconnect_.set_sensitive(enable);
330 } else {
331 lbl_state_.set_text(_("Stato: disconnesso"));
332 lbl_state_.get_style_context()->remove_class("state-up");
333 lbl_state_.get_style_context()->add_class("state-down");
334 lbl_endpoint_.set_text(_("Endpoint: —"));
335 btn_connect_.set_sensitive(enable);
336 // "Disconnetti" resta attivo anche da disconnesso: serve per ripulire
337 // un'interfaccia wg0 rimasta appesa dopo un tentativo fallito.
338 btn_disconnect_.set_sensitive(enable);
339 }
340}
341
342// ---------------------------------------------------------------------------
343
344
345
346void VpnPage::run_async(std::function<OpResult()> work,
347 std::function<void(OpResult)> done) {
348 set_busy(true);
349 auto alive = alive_;
350 std::thread([this, alive, work = std::move(work), done = std::move(done)]() {
351 OpResult r = work();
352 Glib::signal_idle().connect_once([this, alive, r, done]() {
353 if (!*alive) return;
354 set_busy(false);
355 done(r);
356 });
357 }).detach();
358}
359
360// ---------------------------------------------------------------------------
361// Azioni: Configurazione
362// ---------------------------------------------------------------------------
364
365 append_log(_("Generazione coppia di chiavi…"));
366 run_async(
367 [this]() {
368 OpResult r;
369 auto kp = backend_->generate_keys();
370 if (kp) {
371 r.ok = true;
372 // impacchettiamo priv|pub nel campo output (separati da \n)
373 r.output = kp->private_key + "\n" + kp->public_key;
374 } else {
375 r.ok = false;
376 r.output = "generazione chiavi fallita";
377 }
378 return r;
379 },
380 [this](OpResult r) {
381 if (!r.ok) { append_log(_("✗ Generazione chiavi fallita.")); return; }
382 auto nl = r.output.find('\n');
383 std::string priv = r.output.substr(0, nl);
384 std::string pub = (nl == std::string::npos) ? "" : r.output.substr(nl + 1);
385 entry_client_priv_.set_text(priv);
386 entry_client_pub_.set_text(pub);
387 append_log(_("✔ Chiavi generate. Comunica la chiave pubblica al server."));
388 });
389}
390
392
393 if (entry_client_priv_.get_text().empty()) {
394 append_log(_("Genera prima le chiavi del client."));
395 return;
396 }
397
399 p.private_key = entry_client_priv_.get_text();
401 p.endpoint = entry_endpoint_.get_text();
402 p.address = entry_address_.get_text();
403 p.dns = entry_dns_.get_text();
404 p.allowed_ips = entry_allowed_.get_text();
405
406 std::string err;
407 if (backend_->load_from_params(p, &err)) {
408 append_log(_("✔ Configurazione caricata. Premi Connetti per attivare il tunnel."));
409 } else {
410 append_log(_("✗ Configurazione non valida: ") + err);
411 }
412}
413
415 auto parent = dynamic_cast<Gtk::Window*>(get_root());
416 auto dialog = Gtk::make_managed<Gtk::FileChooserDialog>(
417 *parent, _("Seleziona configurazione WireGuard"),
418 Gtk::FileChooser::Action::OPEN);
419
420 dialog->add_button(_("Annulla"), Gtk::ResponseType::CANCEL);
421 dialog->add_button(_("Apri"), Gtk::ResponseType::OK);
422
423 auto filter = Gtk::FileFilter::create();
424 filter->set_name(_("File WireGuard (*.conf)"));
425 filter->add_pattern("*.conf");
426 dialog->add_filter(filter);
427
428 auto alive = alive_;
429 dialog->signal_response().connect(
430 [this, alive, dialog](int response) {
431 if (*alive && response == Gtk::ResponseType::OK) {
432 auto file = dialog->get_file();
433 if (file) entry_conf_path_.set_text(file->get_path());
434 }
435 dialog->hide();
436 });
437 dialog->present();
438}
439
441
442 std::string path = entry_conf_path_.get_text();
443 if (path.empty()) { append_log(_("Nessun file selezionato.")); return; }
444
445 std::string err;
446 if (backend_->load_from_conf(path, &err)) {
447 append_log(_("✔ Configurazione importata. Premi Connetti per attivare il tunnel."));
448 } else {
449 append_log(_("✗ Import fallito: ") + err);
450 }
451}
452
453// ---------------------------------------------------------------------------
454// Azioni: Connessione
455// ---------------------------------------------------------------------------
457
458 if (!backend_->has_spec()) {
459 append_log(_("Nessuna configurazione caricata. Importa un file .conf "
460 "dalla scheda Configurazione, oppure compila i campi e premi "
461 "\"Genera e carica\"."));
462 notebook_.set_current_page(1);
463 return;
464 }
465
466 append_log(_("Connessione al server WireGuard…"));
467 run_async(
468 [this]() { return backend_->connect(); },
469 [this](OpResult r) {
470 if (r.ok) {
471 append_log(_("✔ Tunnel attivo."));
472 refresh_state_ui(backend_->status());
473 // Attendi ~1.5s che l'handshake e la route si stabilizzino,
474 // poi interroga esplicitamente l'IP "con tunnel attivo".
475 auto alive = alive_;
476 Glib::signal_timeout().connect_once(
477 [this, alive]() {
478 if (!*alive) return;
479 append_log(_("Verifica IP pubblico con tunnel…"));
481 },
482 1500);
483 } else {
484 append_log(_("✗ Connessione fallita: ") + r.output);
485 }
486 });
487}
488
490
491 append_log(_("Disconnessione…"));
492 run_async(
493 [this]() { return backend_->disconnect(); },
494 [this](OpResult r) {
495 if (r.ok) {
496 append_log(_("✔ Tunnel chiuso."));
497 lbl_ip_after_.set_text(_("Con tunnel attivo: —"));
498 } else {
499 append_log(_("✗ Disconnessione fallita: ") + r.output);
500 }
501 refresh_state_ui(backend_->status());
502 });
503}
504
508
510 // Risolvi "Auto" una sola volta, qui, in base allo stato corrente.
511 if (slot == IpSlot::Auto)
512 slot = backend_->status().up ? IpSlot::After : IpSlot::Before;
513
514 run_async(
515 [this]() {
516 OpResult r;
517 auto ip = backend_->public_ip();
518 r.ok = ip.has_value();
519 r.output = ip.value_or("");
520 return r;
521 },
522 [this, slot](OpResult r) {
523 if (!r.ok) {
524 append_log(_("Impossibile determinare l'IP pubblico."));
525 return;
526 }
527 if (slot == IpSlot::After) {
528 lbl_ip_after_.set_text(_("Con tunnel attivo: ") + r.output);
529 if (!ip_before_.empty()) {
530 if (ip_before_ != r.output)
531 append_log(_("✔ IP pubblico mascherato correttamente."));
532 else
533 append_log(_("⚠ L'IP pubblico non è cambiato: verifica "
534 "AllowedIPs = 0.0.0.0/0, ::/0 e il NAT sul server."));
535 }
536 } else {
537 ip_before_ = r.output;
538 lbl_ip_before_.set_text(_("Prima del tunnel: ") + r.output);
539 }
540 });
541}
542
543// ---------------------------------------------------------------------------
544// Azioni: Kill-switch
545// ---------------------------------------------------------------------------
547
548 bool enable = switch_ks_.get_active();
549 append_log(enable ? _("Attivazione kill-switch…")
550 : _("Disattivazione kill-switch…"));
551 run_async(
552 [this, enable]() {
553 return enable ? backend_->killswitch_enable()
554 : backend_->killswitch_disable();
555 },
556 [this, enable](OpResult r) {
557 if (r.ok) {
558 append_log(enable ? _("✔ Kill-switch attivo.")
559 : _("✔ Kill-switch disattivato."));
560 } else {
561 append_log(_("✗ Operazione kill-switch fallita: ") + r.output);
562 switch_ks_.set_active(backend_->killswitch_active());
563 }
564 });
565}
566
567// ---------------------------------------------------------------------------
568// Polling
569// ---------------------------------------------------------------------------
571 if (!*alive_) return false;
572 if (busy_) return true;
573 refresh_state_ui(backend_->status());
574 return true;
575}
#define _(s)
void refresh_public_ip(IpSlot slot)
Definition VpnPage.cpp:509
void on_killswitch_toggled()
Definition VpnPage.cpp:546
Gtk::Button btn_disconnect_
Definition VpnPage.hpp:58
Gtk::Widget * build_tab_config()
Definition VpnPage.cpp:178
Gtk::Label lbl_ip_after_
Definition VpnPage.hpp:63
std::string ip_before_
Definition VpnPage.hpp:92
void on_disconnect_clicked()
Definition VpnPage.cpp:489
sigc::connection poll_conn_
Definition VpnPage.hpp:93
void run_async(std::function< BastionGuard::OpResult()> work, std::function< void(BastionGuard::OpResult)> done)
Definition VpnPage.cpp:346
Gtk::Widget * build_tab_connection()
Definition VpnPage.cpp:129
Gtk::ScrolledWindow log_scroller_
Definition VpnPage.hpp:86
void on_refresh_ip_clicked()
Definition VpnPage.cpp:505
Gtk::Label lbl_subtitle_
Definition VpnPage.hpp:51
Gtk::Entry entry_allowed_
Definition VpnPage.hpp:75
Gtk::Entry entry_server_pub_
Definition VpnPage.hpp:71
void on_import_clicked()
Definition VpnPage.cpp:440
BastionGuard::AliveFlag alive_
Definition VpnPage.hpp:126
void append_log(const std::string &line)
Definition VpnPage.cpp:292
Gtk::Entry entry_client_pub_
Definition VpnPage.hpp:69
void on_connect_clicked()
Definition VpnPage.cpp:456
Gtk::Button btn_browse_
Definition VpnPage.hpp:78
Gtk::Switch switch_ks_
Definition VpnPage.hpp:83
Gtk::Entry entry_conf_path_
Definition VpnPage.hpp:77
void on_generate_keys_clicked()
Definition VpnPage.cpp:363
Gtk::Spinner spinner_
Definition VpnPage.hpp:61
Gtk::Label lbl_ip_before_
Definition VpnPage.hpp:62
void on_browse_clicked()
Definition VpnPage.cpp:414
Gtk::Button btn_import_
Definition VpnPage.hpp:79
Gtk::Label lbl_title_
Definition VpnPage.hpp:50
Gtk::Button btn_generate_
Definition VpnPage.hpp:76
void set_busy(bool b)
Definition VpnPage.cpp:300
Gtk::Entry entry_endpoint_
Definition VpnPage.hpp:72
Gtk::Button btn_refresh_ip_
Definition VpnPage.hpp:64
std::unique_ptr< BastionGuard::WireguardBackend > backend_
Definition VpnPage.hpp:90
Gtk::Entry entry_client_priv_
Definition VpnPage.hpp:68
Gtk::Box tab_conn_
Definition VpnPage.hpp:56
Gtk::Box tab_ks_
Definition VpnPage.hpp:82
bool on_poll_status()
Definition VpnPage.cpp:570
Gtk::Button btn_gen_keys_
Definition VpnPage.hpp:70
Gtk::Label lbl_state_
Definition VpnPage.hpp:59
void refresh_state_ui(const BastionGuard::WgStatus &st)
Definition VpnPage.cpp:316
Gtk::Widget * build_tab_killswitch()
Definition VpnPage.cpp:262
Gtk::Entry entry_dns_
Definition VpnPage.hpp:74
static Gtk::Box * labeled_row(const std::string &label, Gtk::Widget &field)
Definition VpnPage.cpp:61
VpnPage()
Definition VpnPage.cpp:30
~VpnPage() override
Definition VpnPage.cpp:53
Gtk::Notebook notebook_
Definition VpnPage.hpp:53
void on_generate_config_clicked()
Definition VpnPage.cpp:391
Gtk::Button btn_connect_
Definition VpnPage.hpp:57
Gtk::TextView txt_log_
Definition VpnPage.hpp:87
Gtk::Label lbl_endpoint_
Definition VpnPage.hpp:60
std::atomic< bool > busy_
Definition VpnPage.hpp:91
Gtk::Entry entry_address_
Definition VpnPage.hpp:73
void build_ui()
Definition VpnPage.cpp:76
Gtk::Box tab_config_
Definition VpnPage.hpp:67