mirror of
https://github.com/YGGverse/Yoda.git
synced 2025-09-13 07:22:16 +00:00
implement maxmind geo-location for page connection info
This commit is contained in:
parent
c35db8c1ff
commit
eb6da7a362
@ -36,6 +36,7 @@ ggemtext = "0.6.0"
|
|||||||
indexmap = "2.7.0"
|
indexmap = "2.7.0"
|
||||||
itertools = "0.14.0"
|
itertools = "0.14.0"
|
||||||
libspelling = "0.3.0"
|
libspelling = "0.3.0"
|
||||||
|
maxminddb = "0.25.0"
|
||||||
openssl = "0.10.70"
|
openssl = "0.10.70"
|
||||||
plurify = "0.2.0"
|
plurify = "0.2.0"
|
||||||
r2d2 = "0.8.10"
|
r2d2 = "0.8.10"
|
||||||
|
@ -105,7 +105,7 @@ impl Request {
|
|||||||
if is_focused(e) {
|
if is_focused(e) {
|
||||||
e.emit_activate()
|
e.emit_activate()
|
||||||
} else {
|
} else {
|
||||||
i.borrow().dialog(Some(e));
|
i.borrow().dialog(&p, Some(e));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
|
@ -2,6 +2,7 @@ mod dialog;
|
|||||||
mod event;
|
mod event;
|
||||||
mod socket;
|
mod socket;
|
||||||
|
|
||||||
|
use super::Profile;
|
||||||
use dialog::Dialog;
|
use dialog::Dialog;
|
||||||
use event::Event;
|
use event::Event;
|
||||||
use gtk::{gio::SocketAddress, prelude::IsA};
|
use gtk::{gio::SocketAddress, prelude::IsA};
|
||||||
@ -48,9 +49,9 @@ impl Info {
|
|||||||
|
|
||||||
// Actions
|
// Actions
|
||||||
|
|
||||||
pub fn dialog(&self, parent: Option<&impl IsA<gtk::Widget>>) {
|
pub fn dialog(&self, profile: &Profile, parent: Option<&impl IsA<gtk::Widget>>) {
|
||||||
use adw::{PreferencesDialog, prelude::AdwDialogExt};
|
use adw::{PreferencesDialog, prelude::AdwDialogExt};
|
||||||
PreferencesDialog::info(self).present(parent)
|
PreferencesDialog::info(self, profile).present(parent)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Actualize `Self`
|
/// Actualize `Self`
|
||||||
|
@ -1,17 +1,16 @@
|
|||||||
use super::Info;
|
use super::{Info, Profile};
|
||||||
use adw::{
|
use adw::{
|
||||||
ActionRow, PreferencesDialog, PreferencesGroup, PreferencesPage,
|
ActionRow, PreferencesDialog, PreferencesGroup, PreferencesPage,
|
||||||
prelude::{ActionRowExt, PreferencesDialogExt, PreferencesGroupExt, PreferencesPageExt},
|
prelude::{ActionRowExt, PreferencesDialogExt, PreferencesGroupExt, PreferencesPageExt},
|
||||||
};
|
};
|
||||||
use gtk::glib::gformat;
|
use gtk::glib::gformat;
|
||||||
use sourceview::prelude::{SocketAddressExt, SocketConnectableExt};
|
|
||||||
|
|
||||||
pub trait Dialog {
|
pub trait Dialog {
|
||||||
fn info(info: &Info) -> Self;
|
fn info(this: &Info, profile: &Profile) -> Self;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Dialog for PreferencesDialog {
|
impl Dialog for PreferencesDialog {
|
||||||
fn info(info: &Info) -> Self {
|
fn info(this: &Info, profile: &Profile) -> Self {
|
||||||
let d = PreferencesDialog::builder()
|
let d = PreferencesDialog::builder()
|
||||||
.search_enabled(true)
|
.search_enabled(true)
|
||||||
.title("Page info")
|
.title("Page info")
|
||||||
@ -21,10 +20,10 @@ impl Dialog for PreferencesDialog {
|
|||||||
.title("General")
|
.title("General")
|
||||||
.icon_name("help-about-symbolic")
|
.icon_name("help-about-symbolic")
|
||||||
.build();
|
.build();
|
||||||
if info.mime.is_some() {
|
if this.mime.is_some() {
|
||||||
p.add(&{
|
p.add(&{
|
||||||
let g = PreferencesGroup::builder().title("Meta").build();
|
let g = PreferencesGroup::builder().title("Meta").build();
|
||||||
if let Some(ref mime) = info.mime {
|
if let Some(ref mime) = this.mime {
|
||||||
g.add(
|
g.add(
|
||||||
&ActionRow::builder()
|
&ActionRow::builder()
|
||||||
.css_classes(["property"])
|
.css_classes(["property"])
|
||||||
@ -38,10 +37,10 @@ impl Dialog for PreferencesDialog {
|
|||||||
g
|
g
|
||||||
});
|
});
|
||||||
} // @TODO content language, header size, etc.
|
} // @TODO content language, header size, etc.
|
||||||
if info.size.is_some() {
|
if this.size.is_some() {
|
||||||
p.add(&{
|
p.add(&{
|
||||||
let g = PreferencesGroup::builder().title("Size").build();
|
let g = PreferencesGroup::builder().title("Size").build();
|
||||||
if let Some(ref size) = info.size {
|
if let Some(ref size) = this.size {
|
||||||
g.add(&{
|
g.add(&{
|
||||||
use crate::tool::Format;
|
use crate::tool::Format;
|
||||||
ActionRow::builder()
|
ActionRow::builder()
|
||||||
@ -63,8 +62,10 @@ impl Dialog for PreferencesDialog {
|
|||||||
.title("Connection")
|
.title("Connection")
|
||||||
.icon_name("network-transmit-receive")
|
.icon_name("network-transmit-receive")
|
||||||
.build();
|
.build();
|
||||||
if let Some(ref socket) = info.socket {
|
if let Some(ref socket) = this.socket {
|
||||||
use gtk::gio::SocketFamily;
|
use gtk::gio::{SocketAddress, SocketFamily};
|
||||||
|
use gtk::prelude::{SocketAddressExt, SocketConnectableExt};
|
||||||
|
/// Convert socket family to string
|
||||||
fn f2s(socket_family: &SocketFamily) -> &str {
|
fn f2s(socket_family: &SocketFamily) -> &str {
|
||||||
match socket_family {
|
match socket_family {
|
||||||
SocketFamily::Invalid => "Invalid",
|
SocketFamily::Invalid => "Invalid",
|
||||||
@ -74,6 +75,7 @@ impl Dialog for PreferencesDialog {
|
|||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/// Build common `ActionRow` widget
|
||||||
fn r(title: &str, subtitle: &str) -> ActionRow {
|
fn r(title: &str, subtitle: &str) -> ActionRow {
|
||||||
ActionRow::builder()
|
ActionRow::builder()
|
||||||
.css_classes(["property"])
|
.css_classes(["property"])
|
||||||
@ -83,24 +85,67 @@ impl Dialog for PreferencesDialog {
|
|||||||
.title(title)
|
.title(title)
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
/// Lookup [MaxMind](https://www.maxmind.com) database
|
||||||
|
fn l(profile: &Profile, socket_address: &SocketAddress) -> Option<String> {
|
||||||
|
use maxminddb::{
|
||||||
|
MaxMindDBError, Reader,
|
||||||
|
geoip2::{/*City,*/ Country},
|
||||||
|
};
|
||||||
|
if !matches!(
|
||||||
|
socket_address.family(),
|
||||||
|
SocketFamily::Ipv4 | SocketFamily::Ipv6,
|
||||||
|
) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let db = {
|
||||||
|
let mut c = profile.config_path.clone();
|
||||||
|
c.push("GeoLite2-Country.mmdb");
|
||||||
|
Reader::open_readfile(c)
|
||||||
|
}
|
||||||
|
.ok()?;
|
||||||
|
let lookup = {
|
||||||
|
let a: std::net::SocketAddr = socket_address.to_string().parse().unwrap();
|
||||||
|
let lookup: std::result::Result<Country, MaxMindDBError> =
|
||||||
|
db.lookup(a.ip());
|
||||||
|
lookup
|
||||||
|
}
|
||||||
|
.ok()?;
|
||||||
|
lookup.country.map(|c| {
|
||||||
|
let mut b = Vec::new();
|
||||||
|
if let Some(iso_code) = c.iso_code {
|
||||||
|
b.push(iso_code)
|
||||||
|
}
|
||||||
|
if let Some(n) = c.names {
|
||||||
|
if let Some(s) = n.get("en") {
|
||||||
|
b.push(s)
|
||||||
|
} // @TODO multi-lang
|
||||||
|
}
|
||||||
|
// @TODO city DB
|
||||||
|
b.join(", ")
|
||||||
|
})
|
||||||
|
}
|
||||||
p.add(&{
|
p.add(&{
|
||||||
let g = PreferencesGroup::builder().title("Remote").build();
|
let g = PreferencesGroup::builder().title("Remote").build();
|
||||||
g.add(&r("Address", &socket.remote_address.to_string()));
|
g.add(&r("Address", &socket.remote_address.to_string()));
|
||||||
g.add(&r("Family", f2s(&socket.remote_address.family())));
|
g.add(&r("Family", f2s(&socket.remote_address.family())));
|
||||||
g.add(&r("Location", "-")); // @TODO optional, MaxMind DB
|
if let Some(location) = l(profile, &socket.remote_address) {
|
||||||
|
g.add(&r("Location", &location));
|
||||||
|
}
|
||||||
g
|
g
|
||||||
});
|
});
|
||||||
p.add(&{
|
p.add(&{
|
||||||
let g = PreferencesGroup::builder().title("Local").build();
|
let g = PreferencesGroup::builder().title("Local").build();
|
||||||
g.add(&r("Address", &socket.local_address.to_string()));
|
g.add(&r("Address", &socket.local_address.to_string()));
|
||||||
g.add(&r("Family", f2s(&socket.local_address.family())));
|
g.add(&r("Family", f2s(&socket.local_address.family())));
|
||||||
g.add(&r("Location", "-")); // @TODO optional, MaxMind DB
|
if let Some(location) = l(profile, &socket.local_address) {
|
||||||
|
g.add(&r("Location", &location));
|
||||||
|
}
|
||||||
g
|
g
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
p
|
p
|
||||||
});
|
});
|
||||||
if info.redirect.is_some() {
|
if this.redirect.is_some() {
|
||||||
d.add(&{
|
d.add(&{
|
||||||
let g = PreferencesGroup::new();
|
let g = PreferencesGroup::new();
|
||||||
let p = PreferencesPage::builder()
|
let p = PreferencesPage::builder()
|
||||||
@ -119,7 +164,7 @@ impl Dialog for PreferencesDialog {
|
|||||||
chain(b, r)
|
chain(b, r)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
chain(&mut b, info);
|
chain(&mut b, this);
|
||||||
b.reverse();
|
b.reverse();
|
||||||
let l = b.len(); // calculate once
|
let l = b.len(); // calculate once
|
||||||
let t = b[0].event[0].time(); // first event time to count from
|
let t = b[0].event[0].time(); // first event time to count from
|
||||||
@ -165,7 +210,7 @@ impl Dialog for PreferencesDialog {
|
|||||||
p
|
p
|
||||||
}) // @TODO clickable navigation, test time values
|
}) // @TODO clickable navigation, test time values
|
||||||
}
|
}
|
||||||
if !info.event.is_empty() {
|
if !this.event.is_empty() {
|
||||||
d.add(&{
|
d.add(&{
|
||||||
let p = PreferencesPage::builder()
|
let p = PreferencesPage::builder()
|
||||||
.title("Events")
|
.title("Events")
|
||||||
@ -173,7 +218,7 @@ impl Dialog for PreferencesDialog {
|
|||||||
.build();
|
.build();
|
||||||
p.add(&{
|
p.add(&{
|
||||||
let g = PreferencesGroup::new();
|
let g = PreferencesGroup::new();
|
||||||
let e = &info.event[0];
|
let e = &this.event[0];
|
||||||
let t = e.time();
|
let t = e.time();
|
||||||
let n = e.name();
|
let n = e.name();
|
||||||
g.add(
|
g.add(
|
||||||
@ -184,7 +229,7 @@ impl Dialog for PreferencesDialog {
|
|||||||
.title(n)
|
.title(n)
|
||||||
.build(),
|
.build(),
|
||||||
);
|
);
|
||||||
for e in &info.event[1..] {
|
for e in &this.event[1..] {
|
||||||
g.add(
|
g.add(
|
||||||
&ActionRow::builder()
|
&ActionRow::builder()
|
||||||
.subtitle(gformat!(
|
.subtitle(gformat!(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user