summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorNamilskyy <alive6863@gmail.com>2025-12-04 23:37:33 +0300
committerNamilskyy <alive6863@gmail.com>2025-12-04 23:37:33 +0300
commitc9d526d6dc5d2c31abe689999497b00237a0bc12 (patch)
tree96395cb62bdc904f1046c17240de24eeda48f1df /src
parentd5abad30ba847fa0e09aa711d90db29a473e9bef (diff)
Implemented full functional. Release v0.0.1
Diffstat (limited to 'src')
-rw-r--r--src/image.pngbin0 -> 36839 bytes
-rw-r--r--src/main.rs96
-rw-r--r--src/redirecting_i2p.rs76
-rw-r--r--src/resources/itoope.pngbin0 -> 3938 bytes
-rw-r--r--src/resources/reload.pngbin0 -> 45649 bytes
-rw-r--r--src/resources/settings.pngbin0 -> 172525 bytes
-rw-r--r--src/settings_window.rs270
7 files changed, 386 insertions, 56 deletions
diff --git a/src/image.png b/src/image.png
new file mode 100644
index 0000000..506eaf2
--- /dev/null
+++ b/src/image.png
Binary files differ
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
new file mode 100644
index 0000000..e5ce55f
--- /dev/null
+++ b/src/resources/itoope.png
Binary files differ
diff --git a/src/resources/reload.png b/src/resources/reload.png
new file mode 100644
index 0000000..a1a3a4f
--- /dev/null
+++ b/src/resources/reload.png
Binary files differ
diff --git a/src/resources/settings.png b/src/resources/settings.png
new file mode 100644
index 0000000..5b77579
--- /dev/null
+++ b/src/resources/settings.png
Binary files differ
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}");
+ }
+}