diff options
| author | Namilskyy <alive6863@gmail.com> | 2025-12-04 23:37:33 +0300 |
|---|---|---|
| committer | Namilskyy <alive6863@gmail.com> | 2025-12-04 23:37:33 +0300 |
| commit | c9d526d6dc5d2c31abe689999497b00237a0bc12 (patch) | |
| tree | 96395cb62bdc904f1046c17240de24eeda48f1df /src | |
| parent | d5abad30ba847fa0e09aa711d90db29a473e9bef (diff) | |
Implemented full functional. Release v0.0.1
Diffstat (limited to 'src')
| -rw-r--r-- | src/image.png | bin | 0 -> 36839 bytes | |||
| -rw-r--r-- | src/main.rs | 96 | ||||
| -rw-r--r-- | src/redirecting_i2p.rs | 76 | ||||
| -rw-r--r-- | src/resources/itoope.png | bin | 0 -> 3938 bytes | |||
| -rw-r--r-- | src/resources/reload.png | bin | 0 -> 45649 bytes | |||
| -rw-r--r-- | src/resources/settings.png | bin | 0 -> 172525 bytes | |||
| -rw-r--r-- | src/settings_window.rs | 270 |
7 files changed, 386 insertions, 56 deletions
diff --git a/src/image.png b/src/image.png Binary files differnew file mode 100644 index 0000000..506eaf2 --- /dev/null +++ b/src/image.png diff --git a/src/main.rs b/src/main.rs index a74f0bd..287ec42 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,15 +1,27 @@ +mod redirecting_i2p; +mod settings_window; + use gtk4::prelude::*; use gtk4::{ - Application, ApplicationWindow, Box, Button, Entry, HeaderBar, Orientation, ScrolledWindow, + Application, ApplicationWindow, Box, Button, CheckButton, Entry, HeaderBar, Image, Orientation, + ScrolledWindow, }; - -use webkit6::WebView; use webkit6::prelude::*; - -mod settings_window; +use webkit6::{NetworkProxyMode, NetworkProxySettings, NetworkSession, WebView}; const APP_ID: &str = "com.namilsk.i2p-browser"; +fn configure_i2p_proxy(enabled: bool) { + if let Some(session) = NetworkSession::default() { + if enabled { + let settings = NetworkProxySettings::new(Some("http://127.0.0.1:4444"), &[]); + session.set_proxy_settings(NetworkProxyMode::Custom, Some(&settings)); + } else { + session.set_proxy_settings(NetworkProxyMode::Default, None); + } + } +} + fn main() -> glib::ExitCode { let app = Application::builder().application_id(APP_ID).build(); app.connect_activate(build_ui); @@ -23,7 +35,6 @@ fn build_ui(app: &Application) { .default_width(1024) .default_height(768) .build(); - let header_bar = HeaderBar::new(); window.set_titlebar(Some(&header_bar)); @@ -31,42 +42,75 @@ fn build_ui(app: &Application) { .placeholder_text("Enter I2P URL (e.g., http://example.i2p)") .build(); + let button_back = Button::with_label("<"); let button_forward = Button::with_label(">"); - let button_reload = Button::with_label("R"); - let button_home = Button::with_label("H"); + let button_reload = Button::with_label("Restart"); + let button_home = Button::with_label("Home"); + let settings_button = Button::with_label("Settings"); + let i2p_proxy = CheckButton::with_label("Enable i2p proxy"); + let i2p_panel = Button::with_label("Panel"); header_bar.pack_start(&button_back); header_bar.pack_start(&button_forward); header_bar.pack_start(&button_reload); header_bar.pack_start(&button_home); - header_bar.set_title_widget(Some(&url_entry)); + header_bar.pack_end(&settings_button); + header_bar.pack_end(&i2p_proxy); + header_bar.pack_end(&i2p_panel); let main_box = Box::new(Orientation::Vertical, 0); window.set_child(Some(&main_box)); - let web_view = WebView::new(); + if let Some(enabled) = settings_window::load_i2p_proxy_enabled() { + i2p_proxy.set_active(enabled); + configure_i2p_proxy(enabled); + } + + i2p_proxy.connect_toggled(|btn| { + let enabled = btn.is_active(); + settings_window::save_i2p_proxy_enabled(enabled); + configure_i2p_proxy(enabled); + }); + let web_view = WebView::new(); let scrolled_window = ScrolledWindow::builder().child(&web_view).build(); + scrolled_window.set_vexpand(true); + scrolled_window.set_hexpand(true); + web_view.set_vexpand(true); + web_view.set_hexpand(true); + + main_box.append(&url_entry); + + if let Some(enable_js) = settings_window::load_js_enabled() + && let Some(web_settings) = webkit6::prelude::WebViewExt::settings(&web_view) { + web_settings.set_enable_javascript(enable_js); + } + main_box.append(&scrolled_window); let web_view_for_entry = web_view.clone(); let url_entry_for_entry = url_entry.clone(); + url_entry.connect_activate(move |entry| { - let url = entry.text(); - let full_url = if url.starts_with("http://") || url.starts_with("https://") { - url.to_string() - } else { + let url = entry.text().to_string(); + + let url_to_use = if !url.starts_with("http://") && !url.starts_with("https://") { format!("http://{}", url) + } else { + url }; - if glib::Uri::parse(&full_url, glib::UriFlags::NONE).is_ok() { - web_view_for_entry.load_uri(&full_url); + let final_url = url_to_use; + + if glib::Uri::parse(&final_url, glib::UriFlags::NONE).is_ok() { + web_view_for_entry.load_uri(&final_url); + println!("Loading: {}", final_url); } else { - eprintln!("Invalid URI: {}", full_url); + eprintln!("Invalid URI: {}", final_url); } - url_entry_for_entry.set_text(&full_url); + url_entry_for_entry.set_text(&final_url); }); let web_view_for_back = web_view.clone(); @@ -96,6 +140,10 @@ fn build_ui(app: &Application) { url_entry_for_home.set_text(home_url); }); + settings_button.connect_clicked(move |_| { + settings_window::settings_window(); + }); + let url_entry_for_notify = url_entry.clone(); web_view.connect_notify_local(Some("uri"), move |web_view, _| { if let Some(uri) = web_view.uri() { @@ -103,7 +151,17 @@ fn build_ui(app: &Application) { } }); - web_view.load_uri("http://legwork.i2p/"); + let web_view_for_panel = web_view.clone(); + let _i2p_panel_for_open = i2p_panel.clone(); + i2p_panel.connect_clicked(move |_| { + let panel_port = redirecting_i2p::I2ProxySettings::from_i2pd_config(None) + .ok() + .and_then(|cfg| cfg.panel_port) + .unwrap_or(7070); + let i2p_panel_url = format!("http://127.0.0.1:{}/", panel_port); + web_view_for_panel.load_uri(&i2p_panel_url); + }); + web_view.load_uri("http://legwork.i2p/"); window.present(); } diff --git a/src/redirecting_i2p.rs b/src/redirecting_i2p.rs new file mode 100644 index 0000000..3feef83 --- /dev/null +++ b/src/redirecting_i2p.rs @@ -0,0 +1,76 @@ +use serde::Deserialize; +use std::fs; + +#[derive(Debug, Deserialize)] +pub struct I2ProxySettings { + pub i2p_port: Option<isize>, + pub panel_port: Option<isize>, +} + +#[derive(Deserialize, Debug)] +struct I2pdConfigFile { + httpproxy: Option<HttpProxySection>, + http: Option<HttpSection>, +} + +#[derive(Deserialize, Debug)] +struct HttpProxySection { + enabled: Option<bool>, + port: Option<isize>, + address: Option<String>, +} + +#[derive(Deserialize, Debug)] +struct HttpSection { + enabled: Option<bool>, + port: Option<isize>, + address: Option<String>, +} + +impl I2ProxySettings { + pub fn default() -> I2ProxySettings { + I2ProxySettings { + i2p_port: Some(4444), + panel_port: Some(7070), + } + } + + const DEFAULT_I2PD_CONFIG_PATH: &'static str = "/etc/i2pd/i2pd.conf"; + + pub fn from_i2pd_config( + config_path: Option<&str>, + ) -> Result<I2ProxySettings, Box<dyn std::error::Error>> { + let path = config_path.unwrap_or(Self::DEFAULT_I2PD_CONFIG_PATH); + let content = fs::read_to_string(path)?; + + let parsed_config: I2pdConfigFile = serde_ini::from_str(&content)?; + + let i2p_port = parsed_config.httpproxy.and_then(|proxy| proxy.port); + let panel_port = parsed_config.http.and_then(|http| http.port); + + Ok(I2ProxySettings { + i2p_port, + panel_port, + }) + } + + fn parse_url(url: &str) -> url::Url { + url::Url::parse(url).expect("Failed to parse URL") + } + + pub fn redirecting_i2p(&mut self, url: &str) -> String { + if self.i2p_port.is_none() { + self.i2p_port = Some(4444); + } + + let parsed_url = Self::parse_url(url); + let host = parsed_url.host_str().unwrap_or(""); + let path = parsed_url.path(); + // Wrong logic, unused, now used the proxy, not webpath + let combined = format!("{}{}", host, path); + let i2p_url = format!("http://127.0.0.1:{}/{}", self.i2p_port.unwrap(), combined); + let i2p_url = I2ProxySettings::parse_url(&i2p_url); + + i2p_url.to_string() + } +} diff --git a/src/resources/itoope.png b/src/resources/itoope.png Binary files differnew file mode 100644 index 0000000..e5ce55f --- /dev/null +++ b/src/resources/itoope.png diff --git a/src/resources/reload.png b/src/resources/reload.png Binary files differnew file mode 100644 index 0000000..a1a3a4f --- /dev/null +++ b/src/resources/reload.png diff --git a/src/resources/settings.png b/src/resources/settings.png Binary files differnew file mode 100644 index 0000000..5b77579 --- /dev/null +++ b/src/resources/settings.png diff --git a/src/settings_window.rs b/src/settings_window.rs index fbab489..105ff22 100644 --- a/src/settings_window.rs +++ b/src/settings_window.rs @@ -1,30 +1,23 @@ - use gtk4::prelude::*; -use gtk4::{ - ApplicationWindow, - Box, - Entry, - Label, - Orientation, - HeaderBar, - CheckButton -}; -use toml; -use serde::Serialize; +use gtk4::{ApplicationWindow, Box, Button, CheckButton, Entry, HeaderBar, Label, Orientation}; +use serde::{Deserialize, Serialize}; +use std::fs; +use std::path::PathBuf; +use crate::redirecting_i2p::I2ProxySettings; #[allow(dead_code)] -#[derive(Debug, Serialize)] +#[derive(Debug, Serialize, Deserialize)] struct Settings { - js: bool, - outproxy: bool, - i2p_proxy: bool, + js: bool, + outproxy: bool, + i2p_proxy: bool, web_rtc: bool, - home_addr: str, - -} + home_addr: String, + proxy_port: Option<isize>, + panel_port: Option<isize>, +} -#[allow(dead_code, unused_variables)] -pub fn window() -> ApplicationWindow { +pub fn settings_window() { let app = ApplicationWindow::builder() .title("Settings") .default_width(400) @@ -32,30 +25,233 @@ pub fn window() -> ApplicationWindow { .build(); let header_bar = HeaderBar::new(); - window().set_titlebar(Some(&header_bar)); + app.set_titlebar(Some(&header_bar)); let main_box = Box::new(Orientation::Vertical, 0); - window().set_child(Some(&main_box)); - - Label::new(Some("Settings")).set_markup("Settings"); - - let javascript = CheckButton::new().set_label(Some("Enable JS")); - let outproxy = CheckButton::new().set_label(Some("Enable outproxy")); - let i2pproxy = CheckButton::new().set_label(Some("Enable i2p proxy")); - let webrtc = CheckButton::new().set_label(Some("Enable WebRTC")); + app.set_child(Some(&main_box)); + + let label = Label::new(Some("Most wanted features")); + main_box.append(&label); + + let javascript = CheckButton::with_label("Enable JS"); + let outproxy = CheckButton::with_label("Enable outproxy"); + let i2pproxy = CheckButton::with_label("Enable i2p proxy"); + let webrtc = CheckButton::with_label("Enable WebRTC"); + let load = Button::with_label("Load"); + let load_i2pd_conf = Button::with_label("Load from i2pd conf"); + + main_box.append(&javascript); + main_box.append(&outproxy); + main_box.append(&i2pproxy); + main_box.append(&webrtc); + let homeaddr = Entry::new(); + homeaddr.set_placeholder_text(Some("Enter home address")); + main_box.append(&homeaddr); + + let proxy_port_entry = Entry::new(); + proxy_port_entry.set_placeholder_text(Some("Proxy port")); + main_box.append(&proxy_port_entry); + + let panel_port_entry = Entry::new(); + panel_port_entry.set_placeholder_text(Some("Panel port")); + main_box.append(&panel_port_entry); + + main_box.append(&load); + main_box.append(&load_i2pd_conf); + let save = Button::with_label("Save"); + main_box.append(&save); - app + if let Some(settings) = Settings::load_from_default_file() { + settings.apply_to_widgets( + &javascript, + &outproxy, + &i2pproxy, + &webrtc, + &homeaddr, + &proxy_port_entry, + &panel_port_entry, + ); + } + + let javascript_for_save = javascript.clone(); + let outproxy_for_save = outproxy.clone(); + let i2pproxy_for_save = i2pproxy.clone(); + let webrtc_for_save = webrtc.clone(); + let homeaddr_for_save = homeaddr.clone(); + let proxy_port_for_save = proxy_port_entry.clone(); + let panel_port_for_save = panel_port_entry.clone(); + save.connect_clicked(move |_| { + let settings = Settings::from_widgets( + &javascript_for_save, + &outproxy_for_save, + &i2pproxy_for_save, + &webrtc_for_save, + &homeaddr_for_save, + &proxy_port_for_save, + &panel_port_for_save, + ); + + if let Err(err) = Settings::serialize_write(&Settings::default_config_path(), &settings) { + eprintln!("Failed to save settings: {err}"); + } + }); + + let javascript_for_load = javascript.clone(); + let outproxy_for_load = outproxy.clone(); + let i2pproxy_for_load = i2pproxy.clone(); + let webrtc_for_load = webrtc.clone(); + let homeaddr_for_load = homeaddr.clone(); + let proxy_port_for_load = proxy_port_entry.clone(); + let panel_port_for_load = panel_port_entry.clone(); + load.connect_clicked(move |_| { + if let Some(settings) = Settings::load_from_default_file() { + settings.apply_to_widgets( + &javascript_for_load, + &outproxy_for_load, + &i2pproxy_for_load, + &webrtc_for_load, + &homeaddr_for_load, + &proxy_port_for_load, + &panel_port_for_load, + ); + } + }); + + let proxy_port_for_i2pd = proxy_port_entry.clone(); + let panel_port_for_i2pd = panel_port_entry.clone(); + let i2pproxy_for_i2pd = i2pproxy.clone(); + load_i2pd_conf.connect_clicked(move |_| { + match I2ProxySettings::from_i2pd_config(None) { + Ok(cfg) => { + if let Some(port) = cfg.i2p_port { + proxy_port_for_i2pd.set_text(&port.to_string()); + i2pproxy_for_i2pd.set_active(true); + } + if let Some(port) = cfg.panel_port { + panel_port_for_i2pd.set_text(&port.to_string()); + } + } + Err(err) => { + eprintln!("Failed to load i2pd config: {err}"); + } + } + }); + + app.present(); } #[allow(dead_code)] impl Settings { - pub fn serialize_write(path: &std::path::Path, - setts: &Settings) -> Result<(), toml::ser::Error> { - - let toml: String = toml::to_string(setts)?; - std::fs::write(path, toml).expect("Some error occured, is permission granted to ~/.config?"); + fn default_config_path() -> PathBuf { + let home = std::env::var("HOME").unwrap_or_else(|_| String::from(".")); + let mut path = PathBuf::from(home); + path.push(".config"); + path.push("i2p-browser"); + // Best effort to ensure directory exists + let _ = fs::create_dir_all(&path); + + path.push("settings.toml"); + path + } + + fn from_widgets( + javascript: &CheckButton, + outproxy: &CheckButton, + i2p_proxy: &CheckButton, + web_rtc: &CheckButton, + home_addr: &Entry, + proxy_port_entry: &Entry, + panel_port_entry: &Entry, + ) -> Settings { + let proxy_port = proxy_port_entry + .text() + .trim() + .parse::<isize>() + .ok(); + let panel_port = panel_port_entry + .text() + .trim() + .parse::<isize>() + .ok(); + Settings { + js: javascript.is_active(), + outproxy: outproxy.is_active(), + i2p_proxy: i2p_proxy.is_active(), + web_rtc: web_rtc.is_active(), + home_addr: home_addr.text().to_string(), + proxy_port, + panel_port, + } + } + + fn apply_to_widgets( + &self, + javascript: &CheckButton, + outproxy: &CheckButton, + i2p_proxy: &CheckButton, + web_rtc: &CheckButton, + home_addr: &Entry, + proxy_port_entry: &Entry, + panel_port_entry: &Entry, + ) { + javascript.set_active(self.js); + outproxy.set_active(self.outproxy); + i2p_proxy.set_active(self.i2p_proxy); + web_rtc.set_active(self.web_rtc); + home_addr.set_text(&self.home_addr); + if let Some(port) = self.proxy_port { + proxy_port_entry.set_text(&port.to_string()); + } else { + proxy_port_entry.set_text(""); + } + if let Some(port) = self.panel_port { + panel_port_entry.set_text(&port.to_string()); + } else { + panel_port_entry.set_text(""); + } + } + + fn load_from_default_file() -> Option<Settings> { + let path = Self::default_config_path(); + let content = fs::read_to_string(path).ok()?; + toml::from_str(&content).ok() + } + + pub fn serialize_write( + path: &std::path::Path, + setts: &Settings, + ) -> Result<(), toml::ser::Error> { + let toml: String = toml::to_string(setts)?; + std::fs::write(path, toml) + .expect("Some error occurred, is permission granted to ~/.config?"); Ok(()) } -}
\ No newline at end of file +} + +pub fn load_js_enabled() -> Option<bool> { + Settings::load_from_default_file().map(|s| s.js) +} + +pub fn load_i2p_proxy_enabled() -> Option<bool> { + Settings::load_from_default_file().map(|s| s.i2p_proxy) +} + +pub fn save_i2p_proxy_enabled(enabled: bool) { + let mut settings = Settings::load_from_default_file().unwrap_or(Settings { + js: true, + outproxy: false, + i2p_proxy: enabled, + web_rtc: false, + home_addr: String::new(), + panel_port: Some(7070), + proxy_port: Some(4444), + }); + + settings.i2p_proxy = enabled; + + if let Err(err) = Settings::serialize_write(&Settings::default_config_path(), &settings) { + eprintln!("Failed to save i2p proxy setting: {err}"); + } +} |
