hippox-drivers 0.3.5

🦛All indivisible atomic driver units in Hippox.
//! WiFi proxy set skill - set proxy for WiFi connection
use crate::{
    DriverCallback, DriverCategory, DriverContext, DriverError, DriverResult,
    types::{Driver, DriverParameter},
};
use serde_json::{Value, json};
use std::collections::HashMap;
use std::process::Command;
use tracing::{debug, info};
/// Driver for setting proxy configuration
#[derive(Debug)]
pub struct WifiProxySetDriver;
#[async_trait::async_trait]
impl Driver for WifiProxySetDriver {
    fn name(&self) -> &str {
        return "wifi_proxy_set";
    }
    fn description(&self) -> &str {
        return "Set HTTP/HTTPS proxy settings for the current WiFi connection";
    }
    fn usage_hint(&self) -> &str {
        return "Use this skill to configure a proxy server for your WiFi connection. Supports HTTP, HTTPS, and SOCKS5 proxies.";
    }
    fn parameters(&self) -> Vec<DriverParameter> {
        return vec![
            DriverParameter {
                name: "proxy_type".to_string(),
                param_type: "string".to_string(),
                description: "Proxy type: 'http', 'https', 'socks5', or 'none' to disable".to_string(),
                required: true,
                default: None,
                example: Some(Value::String("http".to_string())),
                enum_values: Some(vec!["http".to_string(), "https".to_string(), "socks5".to_string(), "none".to_string()]),
            },
            DriverParameter {
                name: "host".to_string(),
                param_type: "string".to_string(),
                description: "Proxy server hostname or IP address (required unless proxy_type is 'none')".to_string(),
                required: false,
                default: None,
                example: Some(Value::String("127.0.0.1".to_string())),
                enum_values: None,
            },
            DriverParameter {
                name: "port".to_string(),
                param_type: "integer".to_string(),
                description: "Proxy server port (required unless proxy_type is 'none')".to_string(),
                required: false,
                default: None,
                example: Some(Value::Number(8080.into())),
                enum_values: None,
            },
            DriverParameter {
                name: "username".to_string(),
                param_type: "string".to_string(),
                description: "Username for proxy authentication (optional)".to_string(),
                required: false,
                default: None,
                example: Some(Value::String("user".to_string())),
                enum_values: None,
            },
            DriverParameter {
                name: "password".to_string(),
                param_type: "string".to_string(),
                description: "Password for proxy authentication (optional)".to_string(),
                required: false,
                default: None,
                example: Some(Value::String("pass".to_string())),
                enum_values: None,
            },
            DriverParameter {
                name: "bypass_list".to_string(),
                param_type: "array".to_string(),
                description: "List of addresses to bypass proxy (e.g., localhost, 192.168.*)".to_string(),
                required: false,
                default: Some(json!(["localhost", "127.0.0.1"])),
                example: Some(json!(["localhost", "127.0.0.1", "192.168.*"])),
                enum_values: None,
            },
        ];
    }
    fn example_call(&self) -> DriverResult<Value> {
        return Ok(json!({
            "action": "wifi_proxy_set",
            "parameters": {
                "proxy_type": "http",
                "host": "127.0.0.1",
                "port": 8080,
                "bypass_list": ["localhost", "127.0.0.1", "192.168.*"]
            }
        }));
    }
    fn example_output(&self) -> String {
        return "HTTP proxy configured: 127.0.0.1:8080".to_string();
    }
    fn category(&self) -> DriverCategory {
        return DriverCategory::Wifi;
    }
    async fn execute(
        &self,
        parameters: &HashMap<String, Value>,
        _callback: Option<&dyn DriverCallback>,
        _context: Option<&DriverContext>,
    ) -> DriverResult<String> {
        debug!("Executing wifi_proxy_set driver");
        let proxy_type = parameters.get("proxy_type").and_then(|v| v.as_str()).ok_or_else(|| {
            debug!("Missing 'proxy_type' parameter");
            return DriverError::missing_parameter("proxy_type");
        })?;
        if proxy_type == "none" {
            disable_proxy().map_err(|e| {
                debug!("Failed to disable proxy: {}", e);
                return DriverError::execution(format!("Failed to disable proxy: {}", e));
            })?;
            info!("Proxy disabled");
            return Ok("Proxy disabled".to_string());
        }
        let host = parameters.get("host").and_then(|v| v.as_str()).ok_or_else(|| {
            debug!("Missing 'host' parameter");
            return DriverError::missing_parameter("host");
        })?;
        let port = parameters.get("port").and_then(|v| v.as_u64()).ok_or_else(|| {
            debug!("Missing 'port' parameter");
            return DriverError::missing_parameter("port");
        })?;
        let username = parameters.get("username").and_then(|v| v.as_str());
        let password = parameters.get("password").and_then(|v| v.as_str());
        let bypass_list = parameters
            .get("bypass_list")
            .and_then(|v| v.as_array())
            .map(|arr| arr.iter().filter_map(|v| v.as_str()).collect::<Vec<&str>>().join(","))
            .unwrap_or_else(|| "localhost,127.0.0.1".to_string());
        set_proxy(proxy_type, host, port, username, password, &bypass_list).map_err(|e| {
            debug!("Failed to set proxy: {}", e);
            return DriverError::execution(format!("Failed to set proxy: {}", e));
        })?;
        info!("{} proxy configured: {}:{}", proxy_type.to_uppercase(), host, port);
        return Ok(format!("{} proxy configured: {}:{}", proxy_type.to_uppercase(), host, port));
    }
}
#[cfg(target_os = "windows")]
fn set_proxy(proxy_type: &str, host: &str, port: u64, username: Option<&str>, password: Option<&str>, bypass_list: &str) -> Result<(), String> {
    let proxy_url = match proxy_type {
        "http" => format!("http://{}:{}", host, port),
        "https" => format!("https://{}:{}", host, port),
        "socks5" => format!("socks5://{}:{}", host, port),
        _ => return Err(format!("Unsupported proxy type: {}", proxy_type)),
    };
    // Set proxy via netsh
    crate::common::hidden_cmd("netsh")
        .args(["winhttp", "set", "proxy", &proxy_url, &format!("bypass-list=\"{}\"", bypass_list)])
        .output()
        .map_err(|e| format!("Failed to set proxy: {}", e))?;
    // Set via registry for system-wide proxy
    crate::common::hidden_cmd("reg")
        .args([
            "add",
            "HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings",
            "/v",
            "ProxyEnable",
            "/t",
            "REG_DWORD",
            "/d",
            "1",
            "/f",
        ])
        .output()
        .map_err(|e| format!("Failed to set registry: {}", e))?;
    crate::common::hidden_cmd("reg")
        .args([
            "add",
            "HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings",
            "/v",
            "ProxyServer",
            "/t",
            "REG_SZ",
            "/d",
            &proxy_url,
            "/f",
        ])
        .output()
        .map_err(|e| format!("Failed to set registry: {}", e))?;
    if let Some(user) = username {
        if let Some(pass) = password {
            crate::common::hidden_cmd("reg")
                .args([
                    "add",
                    "HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings",
                    "/v",
                    "ProxyUser",
                    "/t",
                    "REG_SZ",
                    "/d",
                    user,
                    "/f",
                ])
                .output()
                .map_err(|e| format!("Failed to set registry: {}", e))?;
            crate::common::hidden_cmd("reg")
                .args([
                    "add",
                    "HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings",
                    "/v",
                    "ProxyPass",
                    "/t",
                    "REG_SZ",
                    "/d",
                    pass,
                    "/f",
                ])
                .output()
                .map_err(|e| format!("Failed to set registry: {}", e))?;
        }
    }
    return Ok(());
}
#[cfg(target_os = "linux")]
fn set_proxy(proxy_type: &str, host: &str, port: u64, username: Option<&str>, password: Option<&str>, bypass_list: &str) -> Result<(), String> {
    let auth = if let (Some(user), Some(pass)) = (username, password) { format!("{}:{}@", user, pass) } else { String::new() };
    // Set environment variables via gsettings
    crate::common::hidden_cmd("gsettings")
        .args(["set", "org.gnome.system.proxy", "mode", "manual"])
        .output()
        .map_err(|e| format!("Failed to set proxy mode: {}", e))?;
    crate::common::hidden_cmd("gsettings")
        .args(["set", "org.gnome.system.proxy.http", "host", host])
        .output()
        .map_err(|e| format!("Failed to set http host: {}", e))?;
    crate::common::hidden_cmd("gsettings")
        .args(["set", "org.gnome.system.proxy.http", "port", &port.to_string()])
        .output()
        .map_err(|e| format!("Failed to set http port: {}", e))?;
    crate::common::hidden_cmd("gsettings")
        .args(["set", "org.gnome.system.proxy.https", "host", host])
        .output()
        .map_err(|e| format!("Failed to set https host: {}", e))?;
    crate::common::hidden_cmd("gsettings")
        .args(["set", "org.gnome.system.proxy.https", "port", &port.to_string()])
        .output()
        .map_err(|e| format!("Failed to set https port: {}", e))?;
    if proxy_type == "socks5" {
        crate::common::hidden_cmd("gsettings")
            .args(["set", "org.gnome.system.proxy.socks", "host", host])
            .output()
            .map_err(|e| format!("Failed to set socks host: {}", e))?;
        crate::common::hidden_cmd("gsettings")
            .args(["set", "org.gnome.system.proxy.socks", "port", &port.to_string()])
            .output()
            .map_err(|e| format!("Failed to set socks port: {}", e))?;
    }
    let bypass_array = format!("['{}']", bypass_list.replace(",", "', '"));
    crate::common::hidden_cmd("gsettings")
        .args(["set", "org.gnome.system.proxy", "ignore-hosts", &bypass_array])
        .output()
        .map_err(|e| format!("Failed to set bypass list: {}", e))?;
    return Ok(());
}
#[cfg(target_os = "macos")]
fn set_proxy(proxy_type: &str, host: &str, port: u64, username: Option<&str>, password: Option<&str>, bypass_list: &str) -> Result<(), String> {
    let service_name = get_wifi_service_name_macos()?;
    let proxy_cmd = match proxy_type {
        "http" => "webproxy",
        "https" => "securewebproxy",
        "socks5" => "socksfirewallproxy",
        _ => return Err(format!("Unsupported proxy type: {}", proxy_type)),
    };
    crate::common::hidden_cmd("networksetup")
        .args(["-set", proxy_cmd, &service_name, host, &port.to_string()])
        .output()
        .map_err(|e| format!("Failed to set proxy: {}", e))?;
    if let (Some(user), Some(pass)) = (username, password) {
        crate::common::hidden_cmd("networksetup")
            .args(["-set", &format!("{}auth", proxy_cmd), &service_name, user, pass])
            .output()
            .map_err(|e| format!("Failed to set proxy auth: {}", e))?;
    }
    crate::common::hidden_cmd("networksetup")
        .args(["-setproxybypassdomains", &service_name, &bypass_list.replace(',', " ")])
        .output()
        .map_err(|e| format!("Failed to set bypass domains: {}", e))?;
    crate::common::hidden_cmd("networksetup")
        .args(["-set", &format!("{}state", proxy_cmd), &service_name, "on"])
        .output()
        .map_err(|e| format!("Failed to enable proxy: {}", e))?;
    return Ok(());
}
#[cfg(target_os = "windows")]
fn disable_proxy() -> Result<(), String> {
    crate::common::hidden_cmd("netsh").args(["winhttp", "reset", "proxy"]).output().map_err(|e| format!("Failed to reset proxy: {}", e))?;
    crate::common::hidden_cmd("reg")
        .args([
            "add",
            "HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings",
            "/v",
            "ProxyEnable",
            "/t",
            "REG_DWORD",
            "/d",
            "0",
            "/f",
        ])
        .output()
        .map_err(|e| format!("Failed to disable proxy: {}", e))?;
    return Ok(());
}
#[cfg(target_os = "linux")]
fn disable_proxy() -> Result<(), String> {
    crate::common::hidden_cmd("gsettings")
        .args(["set", "org.gnome.system.proxy", "mode", "none"])
        .output()
        .map_err(|e| format!("Failed to disable proxy: {}", e))?;
    return Ok(());
}
#[cfg(target_os = "macos")]
fn disable_proxy() -> Result<(), String> {
    let service_name = get_wifi_service_name_macos()?;
    crate::common::hidden_cmd("networksetup")
        .args(["-setwebproxystate", &service_name, "off"])
        .output()
        .map_err(|e| format!("Failed to disable web proxy: {}", e))?;
    crate::common::hidden_cmd("networksetup")
        .args(["-setsecurewebproxystate", &service_name, "off"])
        .output()
        .map_err(|e| format!("Failed to disable secure web proxy: {}", e))?;
    crate::common::hidden_cmd("networksetup")
        .args(["-setsocksfirewallproxystate", &service_name, "off"])
        .output()
        .map_err(|e| format!("Failed to disable socks proxy: {}", e))?;
    return Ok(());
}
#[cfg(target_os = "macos")]
fn get_wifi_service_name_macos() -> Result<String, String> {
    let output = crate::common::hidden_cmd("networksetup")
        .args(["-listallhardwareports"])
        .output()
        .map_err(|e| format!("Failed to list hardware ports: {}", e))?;
    let stdout = String::from_utf8_lossy(&output.stdout);
    let mut current_device = String::new();
    for line in stdout.lines() {
        if line.contains("Hardware Port: Wi-Fi") || line.contains("Hardware Port: AirPort") {
            if let Some(device_line) = stdout.lines().skip_while(|l| *l != line).nth(1) {
                if device_line.contains("Device:") {
                    if let Some(device) = device_line.split(':').nth(1) {
                        current_device = device.trim().to_string();
                    }
                }
            }
        }
    }
    if current_device.is_empty() {
        return Ok("Wi-Fi".to_string());
    }
    return Ok(current_device);
}
#[cfg(not(any(target_os = "windows", target_os = "linux", target_os = "macos")))]
fn set_proxy(_proxy_type: &str, _host: &str, _port: u64, _username: Option<&str>, _password: Option<&str>, _bypass_list: &str) -> Result<(), String> {
    return Err("Proxy configuration not implemented on this platform".to_string());
}
#[cfg(not(any(target_os = "windows", target_os = "linux", target_os = "macos")))]
fn disable_proxy() -> Result<(), String> {
    return Err("Proxy configuration not implemented on this platform".to_string());
}