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};
#[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)),
};
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))?;
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() };
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());
}