👷 add dns servers page

This commit is contained in:
Vladislav Nepogodin 2023-09-18 01:14:21 +04:00
parent 7601d14d08
commit 61b7c65da2
No known key found for this signature in database
GPG Key ID: B62C3D10C54D5DA9
5 changed files with 338 additions and 2 deletions

64
Cargo.lock generated
View File

@ -175,6 +175,7 @@ dependencies = [
"i18n-embed-fl",
"once_cell",
"pacmanconf",
"phf",
"reqwest",
"rust-embed",
"serde",
@ -1273,6 +1274,48 @@ version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94"
[[package]]
name = "phf"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc"
dependencies = [
"phf_macros",
"phf_shared",
]
[[package]]
name = "phf_generator"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0"
dependencies = [
"phf_shared",
"rand",
]
[[package]]
name = "phf_macros"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b"
dependencies = [
"phf_generator",
"phf_shared",
"proc-macro2",
"quote",
"syn 2.0.36",
]
[[package]]
name = "phf_shared"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b"
dependencies = [
"siphasher",
]
[[package]]
name = "pin-project-lite"
version = "0.2.13"
@ -1343,6 +1386,21 @@ dependencies = [
"proc-macro2",
]
[[package]]
name = "rand"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
[[package]]
name = "redox_syscall"
version = "0.3.5"
@ -1615,6 +1673,12 @@ dependencies = [
"digest",
]
[[package]]
name = "siphasher"
version = "0.3.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d"
[[package]]
name = "slab"
version = "0.4.9"

View File

@ -24,6 +24,7 @@ serde = { version = "1", default-features = false }
serde_json = "1"
reqwest = { version = "0.11", features = ["blocking"] }
unic-langid = "0.9"
phf = { version = "0.11", features = ["macros"], default-features = false }
[profile.release]
strip = "symbols"

View File

@ -11,6 +11,17 @@ lock-doesnt-exist = Pacman db lock does not exist!
orphans-not-found = No orphan packages found!
package-not-installed = Package '{$package_name}' has not been installed!
# Dns Connections page
dns-settings = DNS Settings
select-connections = Select Connections:
select-dns-server = Select DNS server:
apply = Apply
reset = Reset
dns-server-changed = DNS server was successfully changed!
dns-server-failed = Failed to set DNS server!
dns-server-reset = DNS server has been reset!
dns-server-reset-failed = Failed to reset DNS server!
# Tweaks page (tweaks)
tweak-enabled-title = {$tweak} enabled
@ -22,6 +33,7 @@ update-system-title = System update
remove-orphans-title = Remove orphans
clear-pkgcache-title = Clear package cache
rankmirrors-title = Rank mirrors
dnsserver-title = Change DNS server
# Main Page (buttons)
button-about-tooltip = About

View File

@ -343,6 +343,7 @@ fn build_ui(application: &gtk::Application) {
}
pages::create_appbrowser_page(&builder);
pages::create_tweaks_page(&builder);
pages::create_dnsconnections_page(&builder);
// Show the UI
main_window.show();

View File

@ -5,6 +5,7 @@ use crate::{fl, utils};
use glib::translate::FromGlib;
use gtk::{glib, Builder};
use once_cell::sync::Lazy;
use phf::phf_ordered_map;
use std::fmt::Write as _;
use std::path::Path;
use std::sync::Mutex;
@ -17,6 +18,18 @@ use subprocess::{Exec, Redirection};
static G_LOCAL_UNITS: Lazy<Mutex<SystemdUnits>> = Lazy::new(|| Mutex::new(SystemdUnits::new()));
static G_GLOBAL_UNITS: Lazy<Mutex<SystemdUnits>> = Lazy::new(|| Mutex::new(SystemdUnits::new()));
static G_DNS_SERVERS: phf::OrderedMap<&'static str, &'static str> = phf_ordered_map! {
"Adguard" => "94.140.14.14",
"Adguard Family Protection" => "94.140.14.15",
"Cloudflare" => "1.1.1.1",
"Cloudflare Malware and adult content blocking" => "1.1.1.3",
"DNS.Watch" => "84.200.69.80",
"Cisco Umbrella(OpenDNS)" => "208.67.222.222,208.67.220.220",
"Quad9" => "9.9.9.9",
"Google" => "8.8.8.8,8.8.4.4",
"Yandex" => "77.88.8.8,77.88.8.1",
};
struct DialogMessage {
pub msg: String,
pub msg_type: gtk::MessageType,
@ -26,6 +39,7 @@ struct DialogMessage {
enum Action {
RemoveLock,
RemoveOrphans,
SetDnsServer,
}
fn update_translation_apps_section(section_box: &gtk::Box) {
@ -104,11 +118,24 @@ pub fn update_translations(builder: &Builder) {
}
}
fn create_fixes_section() -> gtk::Box {
fn get_nm_connections() -> Vec<String> {
let connections = Exec::cmd("/sbin/nmcli")
.args(&["-t", "-f", "NAME", "connection", "show"])
.stdout(Redirection::Pipe)
.capture()
.unwrap()
.stdout_str();
// get list of connections separated by newline
connections.split('\n').filter(|x| !x.is_empty()).map(String::from).collect::<Vec<_>>()
}
fn create_fixes_section(builder: &Builder) -> gtk::Box {
let topbox = gtk::Box::new(gtk::Orientation::Vertical, 2);
let button_box_f = gtk::Box::new(gtk::Orientation::Horizontal, 10);
let button_box_s = gtk::Box::new(gtk::Orientation::Horizontal, 10);
let button_box_t = gtk::Box::new(gtk::Orientation::Horizontal, 10);
let button_box_frth = gtk::Box::new(gtk::Orientation::Horizontal, 10);
let label = gtk::Label::new(None);
label.set_line_wrap(true);
label.set_justify(gtk::Justification::Center);
@ -121,6 +148,7 @@ fn create_fixes_section() -> gtk::Box {
let remove_orphans_btn = gtk::Button::with_label(&fl!("remove-orphans-title"));
let clear_pkgcache_btn = gtk::Button::with_label(&fl!("clear-pkgcache-title"));
let rankmirrors_btn = gtk::Button::with_label(&fl!("rankmirrors-title"));
let dnsserver_btn = gtk::Button::with_label(&fl!("dnsserver-title"));
{
removelock_btn.set_widget_name("remove-lock-title");
@ -130,6 +158,7 @@ fn create_fixes_section() -> gtk::Box {
remove_orphans_btn.set_widget_name("remove-orphans-title");
clear_pkgcache_btn.set_widget_name("clear-pkgcache-title");
rankmirrors_btn.set_widget_name("rankmirrors-title");
dnsserver_btn.set_widget_name("dnsserver-title");
}
// Create context channel.
@ -210,6 +239,11 @@ fn create_fixes_section() -> gtk::Box {
let _ = utils::run_cmd_terminal(String::from("cachyos-rate-mirrors"), true);
});
});
dnsserver_btn.connect_clicked(glib::clone!(@weak builder => move |_| {
let name = "dnsConnectionsBrowser";
let stack: gtk::Stack = builder.object("stack").unwrap();
stack.set_visible_child_name(&format!("{name}page"));
}));
// Setup receiver.
let removelock_btn_clone = removelock_btn.clone();
@ -218,6 +252,7 @@ fn create_fixes_section() -> gtk::Box {
let widget_obj = match msg.action {
Action::RemoveLock => &removelock_btn_clone,
Action::RemoveOrphans => &remove_orphans_btn_clone,
_ => panic!("Unexpected action!!"),
};
let widget_window =
utils::get_window_from_widget(widget_obj).expect("Failed to retrieve window");
@ -241,9 +276,12 @@ fn create_fixes_section() -> gtk::Box {
button_box_s.pack_start(&clear_pkgcache_btn, true, true, 2);
button_box_s.pack_end(&remove_orphans_btn, true, true, 2);
button_box_t.pack_end(&rankmirrors_btn, true, true, 2);
button_box_frth.pack_end(&dnsserver_btn, true, true, 2);
button_box_f.set_halign(gtk::Align::Fill);
button_box_s.set_halign(gtk::Align::Fill);
button_box_t.set_halign(gtk::Align::Fill);
button_box_frth.set_halign(gtk::Align::Fill);
topbox.pack_end(&button_box_frth, true, true, 5);
topbox.pack_end(&button_box_t, true, true, 5);
topbox.pack_end(&button_box_s, true, true, 5);
topbox.pack_end(&button_box_f, true, true, 5);
@ -369,6 +407,184 @@ fn create_apps_section() -> Option<gtk::Box> {
}
}
fn create_connections_section() -> gtk::Box {
let topbox = gtk::Box::new(gtk::Orientation::Vertical, 2);
let connection_box = gtk::Box::new(gtk::Orientation::Horizontal, 2);
let dnsservers_box = gtk::Box::new(gtk::Orientation::Horizontal, 2);
let button_box = gtk::Box::new(gtk::Orientation::Horizontal, 2);
let label = gtk::Label::new(None);
label.set_line_wrap(true);
label.set_justify(gtk::Justification::Center);
label.set_text(&fl!("dns-settings"));
let connections_label = gtk::Label::new(None);
connections_label.set_justify(gtk::Justification::Left);
connections_label.set_text(&fl!("select-connections"));
let servers_label = gtk::Label::new(None);
servers_label.set_justify(gtk::Justification::Left);
servers_label.set_text(&fl!("select-dns-server"));
let apply_btn = gtk::Button::with_label(&fl!("apply"));
let reset_btn = gtk::Button::with_label(&fl!("reset"));
let combo_conn = {
let store = gtk::ListStore::new(&[String::static_type()]);
let nm_connections = get_nm_connections();
for nm_connection in nm_connections.iter() {
store.set(&store.append(), &[(0, nm_connection)]);
}
utils::create_combo_with_model(&store)
};
let combo_servers = {
let store = gtk::ListStore::new(&[String::static_type()]);
for dns_server in G_DNS_SERVERS.keys().into_iter() {
store.set(&store.append(), &[(0, dns_server)]);
}
utils::create_combo_with_model(&store)
};
combo_servers.set_active(Some(2));
combo_conn.set_widget_name("connections_combo");
combo_servers.set_widget_name("servers_combo");
// Create context channel.
let (dialog_tx, dialog_rx) = glib::MainContext::channel(glib::Priority::default());
// Connect signals.
let dialog_tx_clone = dialog_tx.clone();
let combo_conn_clone = combo_conn.clone();
let combo_serv_clone = combo_servers.clone();
apply_btn.connect_clicked(move |_| {
let dialog_tx_clone = dialog_tx_clone.clone();
let conn_name = {
if let Some(tree_iter) = combo_conn_clone.active_iter() {
let model = combo_conn_clone.model().unwrap();
let group_gobj = model.value(&tree_iter, 0);
let group = group_gobj.get::<&str>().unwrap();
String::from(group)
} else {
"".into()
}
};
let server_name = {
if let Some(tree_iter) = combo_serv_clone.active_iter() {
let model = combo_serv_clone.model().unwrap();
let group_gobj = model.value(&tree_iter, 0);
let group = group_gobj.get::<&str>().unwrap();
String::from(group)
} else {
"".into()
}
};
let server_addr = G_DNS_SERVERS.get(&server_name).unwrap();
std::thread::spawn(move || {
println!("conn_name := {conn_name:?}; server_addr := {server_addr:?}");
let status_code = Exec::cmd("/sbin/pkexec")
.arg("bash")
.arg("-c")
.arg(format!(
"nmcli con mod '{conn_name}' ipv4.dns '{server_addr}' && systemctl restart \
NetworkManager"
))
.join()
.unwrap();
if status_code.success() {
dialog_tx_clone
.send(DialogMessage {
msg: fl!("dns-server-changed"),
msg_type: gtk::MessageType::Info,
action: Action::SetDnsServer,
})
.expect("Couldn't send data to channel");
} else {
dialog_tx_clone
.send(DialogMessage {
msg: fl!("dns-server-failed"),
msg_type: gtk::MessageType::Error,
action: Action::SetDnsServer,
})
.expect("Couldn't send data to channel");
}
});
});
let dialog_tx_clone = dialog_tx.clone();
let combo_conn_clone = combo_conn.clone();
reset_btn.connect_clicked(move |_| {
let dialog_tx_clone = dialog_tx_clone.clone();
let conn_name = {
if let Some(tree_iter) = combo_conn_clone.active_iter() {
let model = combo_conn_clone.model().unwrap();
let group_gobj = model.value(&tree_iter, 0);
let group = group_gobj.get::<&str>().unwrap();
String::from(group)
} else {
"".into()
}
};
std::thread::spawn(move || {
let status_code = Exec::cmd("/sbin/pkexec")
.arg("bash")
.arg("-c")
.arg(format!(
"nmcli con mod '{conn_name}' ipv4.dns '' && systemctl restart NetworkManager"
))
.join()
.unwrap();
if status_code.success() {
dialog_tx_clone
.send(DialogMessage {
msg: fl!("dns-server-reset"),
msg_type: gtk::MessageType::Info,
action: Action::SetDnsServer,
})
.expect("Couldn't send data to channel");
} else {
dialog_tx_clone
.send(DialogMessage {
msg: fl!("dns-server-reset-failed"),
msg_type: gtk::MessageType::Error,
action: Action::SetDnsServer,
})
.expect("Couldn't send data to channel");
}
});
});
// Setup receiver
let apply_btn_clone = apply_btn.clone();
dialog_rx.attach(None, move |msg| {
let widget_obj = &apply_btn_clone;
let widget_window =
utils::get_window_from_widget(widget_obj).expect("Failed to retrieve window");
let dialog = gtk::MessageDialog::builder()
.transient_for(&widget_window)
.message_type(msg.msg_type)
.text(msg.msg)
.title(msg.msg_type.to_string())
.modal(true)
.build();
dialog.show();
glib::ControlFlow::Continue
});
topbox.pack_start(&label, true, false, 1);
connection_box.pack_start(&connections_label, true, true, 2);
connection_box.pack_end(&combo_conn, true, true, 2);
dnsservers_box.pack_start(&servers_label, true, true, 2);
dnsservers_box.pack_end(&combo_servers, true, true, 2);
button_box.pack_start(&reset_btn, true, true, 2);
button_box.pack_end(&apply_btn, true, true, 2);
connection_box.set_halign(gtk::Align::Fill);
dnsservers_box.set_halign(gtk::Align::Fill);
button_box.set_halign(gtk::Align::Fill);
topbox.pack_start(&connection_box, true, true, 5);
topbox.pack_start(&dnsservers_box, true, true, 5);
topbox.pack_start(&button_box, true, true, 5);
topbox.set_hexpand(true);
topbox
}
fn load_enabled_units() {
G_LOCAL_UNITS.lock().unwrap().loaded_units.clear();
G_LOCAL_UNITS.lock().unwrap().enabled_units.clear();
@ -433,7 +649,7 @@ pub fn create_tweaks_page(builder: &Builder) {
}));
let options_section_box = create_options_section();
let fixes_section_box = create_fixes_section();
let fixes_section_box = create_fixes_section(builder);
let apps_section_box_opt = create_apps_section();
let child_name = "tweaksBrowserpage";
@ -472,6 +688,48 @@ pub fn create_tweaks_page(builder: &Builder) {
stack.add_named(&viewport, child_name);
}
pub fn create_dnsconnections_page(builder: &Builder) {
let viewport = gtk::Viewport::new(gtk::Adjustment::NONE, gtk::Adjustment::NONE);
let image = gtk::Image::from_icon_name(Some("go-previous"), gtk::IconSize::Button);
let back_btn = gtk::Button::new();
back_btn.set_image(Some(&image));
back_btn.set_widget_name("tweaksBrowser");
back_btn.connect_clicked(glib::clone!(@weak builder => move |button| {
let name = button.widget_name();
let stack: gtk::Stack = builder.object("stack").unwrap();
stack.set_visible_child_name(&format!("{name}page"));
}));
let connections_section_box = create_connections_section();
let child_name = "dnsConnectionsBrowserpage";
connections_section_box.set_widget_name(&format!("{child_name}_connections"));
let grid = gtk::Grid::new();
grid.set_hexpand(true);
grid.set_margin_start(10);
grid.set_margin_end(10);
grid.set_margin_top(5);
grid.set_margin_bottom(5);
grid.attach(&back_btn, 0, 1, 1, 1);
let box_collection_s = gtk::Box::new(gtk::Orientation::Vertical, 5);
let box_collection = gtk::Box::new(gtk::Orientation::Vertical, 5);
box_collection.set_widget_name(child_name);
box_collection.pack_start(&connections_section_box, false, false, 10);
box_collection.set_valign(gtk::Align::Center);
box_collection.set_halign(gtk::Align::Center);
box_collection_s.pack_start(&grid, false, false, 0);
box_collection_s.pack_start(&box_collection, false, false, 10);
viewport.add(&box_collection_s);
viewport.show_all();
let stack: gtk::Stack = builder.object("stack").unwrap();
stack.add_named(&viewport, child_name);
}
pub fn create_appbrowser_page(builder: &Builder) {
let install: gtk::Button = builder.object("appBrowser").unwrap();
install.set_visible(true);