use crate::models::{Proxy, SS_DEFAULT_GROUP};
use crate::utils::url::url_decode;
use base64::engine::general_purpose::STANDARD;
use base64::Engine;
use serde_json::Value;
pub fn explode_ss(ss: &str, node: &mut Proxy) -> bool {
if !ss.starts_with("ss://") {
return false;
}
let mut ss_content = ss[5..].to_string();
ss_content = ss_content.replace("/?", "?");
let mut ps = String::new();
if let Some(hash_pos) = ss_content.find('#') {
ps = url_decode(&ss_content[hash_pos + 1..]);
ss_content = ss_content[..hash_pos].to_string();
}
let mut plugin = String::new();
let mut plugin_opts = String::new();
let mut group = SS_DEFAULT_GROUP.to_string();
if let Some(query_pos) = ss_content.find('?') {
let addition = ss_content[query_pos + 1..].to_string();
ss_content = ss_content[..query_pos].to_string();
for (key, value) in url::form_urlencoded::parse(addition.as_bytes()) {
if key == "plugin" {
let plugins = url_decode(&value);
if let Some(semicolon_pos) = plugins.find(';') {
plugin = plugins[..semicolon_pos].to_string();
plugin_opts = plugins[semicolon_pos + 1..].to_string();
} else {
plugin = plugins;
}
} else if key == "group" {
if !value.is_empty() {
group = crate::utils::base64::url_safe_base64_decode(&value);
}
}
}
}
let method;
let password;
let server;
let port;
if ss_content.contains('@') {
let parts: Vec<&str> = ss_content.split('@').collect();
if parts.len() < 2 {
return false;
}
let secret = parts[0];
let server_port = parts[1];
let server_port_parts: Vec<&str> = server_port.split(':').collect();
if server_port_parts.len() < 2 {
return false;
}
server = server_port_parts[0].to_string();
port = match server_port_parts[1].parse::<u16>() {
Ok(p) => p,
Err(_) => return false,
};
let decoded_secret = crate::utils::base64::url_safe_base64_decode(secret);
let method_pass: Vec<&str> = decoded_secret.split(':').collect();
if method_pass.len() < 2 {
return false;
}
method = method_pass[0].to_string();
password = method_pass[1..].join(":"); } else {
let decoded = crate::utils::base64::url_safe_base64_decode(&ss_content);
if decoded.is_empty() {
return false;
}
let parts: Vec<&str> = decoded.split('@').collect();
if parts.len() < 2 {
return false;
}
let method_pass = parts[0];
let server_port = parts[1];
let method_pass_parts: Vec<&str> = method_pass.split(':').collect();
if method_pass_parts.len() < 2 {
return false;
}
method = method_pass_parts[0].to_string();
password = method_pass_parts[1..].join(":");
let server_port_parts: Vec<&str> = server_port.split(':').collect();
if server_port_parts.len() < 2 {
return false;
}
server = server_port_parts[0].to_string();
port = match server_port_parts[1].parse::<u16>() {
Ok(p) => p,
Err(_) => return false,
};
}
if port == 0 {
return false;
}
if ps.is_empty() {
ps = format!("{} ({})", server, port);
}
*node = Proxy::ss_construct(
&group,
&ps,
&server,
port,
&password,
&method,
&plugin,
&plugin_opts,
None,
None,
None,
None,
"",
);
true
}
pub fn explode_ssd(link: &str, nodes: &mut Vec<Proxy>) -> bool {
if !link.starts_with("ssd://") {
return false;
}
let encoded = &link[6..];
let decoded = match STANDARD.decode(encoded) {
Ok(bytes) => match String::from_utf8(bytes) {
Ok(s) => s,
Err(_) => return false,
},
Err(_) => return false,
};
let json: Value = match serde_json::from_str(&decoded) {
Ok(json) => json,
Err(_) => return false,
};
let airport = json["airport"].as_str().unwrap_or("");
let port = json["port"].as_u64().unwrap_or(0) as u16;
let encryption = json["encryption"].as_str().unwrap_or("");
let password = json["password"].as_str().unwrap_or("");
if !json["servers"].is_array() {
return false;
}
let servers = json["servers"].as_array().unwrap();
for server in servers {
let server_host = server["server"].as_str().unwrap_or("");
let server_port = server["port"].as_u64().unwrap_or(port as u64) as u16;
let server_encryption = server["encryption"].as_str().unwrap_or(encryption);
let server_password = server["password"].as_str().unwrap_or(password);
let server_remark = server["remarks"].as_str().unwrap_or("");
let server_plugin = server["plugin"].as_str().unwrap_or("");
let server_plugin_opts = server["plugin_options"].as_str().unwrap_or("");
let formatted_remark = format!("{} - {}", airport, server_remark);
let node = Proxy::ss_construct(
SS_DEFAULT_GROUP,
&formatted_remark,
server_host,
server_port,
server_password,
server_encryption,
server_plugin,
server_plugin_opts,
None,
None,
None,
None,
"",
);
nodes.push(node);
}
!nodes.is_empty()
}
pub fn explode_ss_android(content: &str, nodes: &mut Vec<Proxy>) -> bool {
let json: Value = match serde_json::from_str(content) {
Ok(json) => json,
Err(_) => {
println!(
"Error parsing Android Shadowsocks configuration: {}",
content
);
return false;
}
};
if !json["configs"].is_array() && !json["proxies"].is_array() {
return false;
}
let configs = if json["configs"].is_array() {
json["configs"].as_array().unwrap()
} else {
json["proxies"].as_array().unwrap()
};
let mut index = nodes.len();
for config in configs {
let server = config["server"].as_str().unwrap_or("");
if server.is_empty() {
continue;
}
let port_num = config["server_port"].as_u64().unwrap_or(0) as u16;
if port_num == 0 {
continue;
}
let method = config["method"].as_str().unwrap_or("");
let password = config["password"].as_str().unwrap_or("");
let remark = if config["remarks"].is_string() {
config["remarks"].as_str().unwrap_or("").to_string()
} else if config["name"].is_string() {
config["name"].as_str().unwrap_or("").to_string()
} else {
format!("{} ({})", server, port_num)
};
let plugin = config["plugin"].as_str().unwrap_or("");
let plugin_opts = config["plugin_opts"].as_str().unwrap_or("");
let mut node = Proxy::ss_construct(
SS_DEFAULT_GROUP,
&remark,
server,
port_num,
password,
method,
plugin,
plugin_opts,
None,
None,
None,
None,
"",
);
node.id = index as u32;
nodes.push(node);
index += 1;
}
!nodes.is_empty()
}
pub fn explode_ss_conf(content: &str, nodes: &mut Vec<Proxy>) -> bool {
let json: Value = match serde_json::from_str(content) {
Ok(json) => json,
Err(_) => return false,
};
if json["configs"].is_array() || json["proxies"].is_array() {
return explode_ss_android(content, nodes);
}
if json["server"].is_string() && json["server_port"].is_u64() {
let index = nodes.len();
let server = json["server"].as_str().unwrap_or("");
let port_num = json["server_port"].as_u64().unwrap_or(0) as u16;
if server.is_empty() || port_num == 0 {
return false;
}
let method = json["method"].as_str().unwrap_or("");
let password = json["password"].as_str().unwrap_or("");
let remark = if json["remarks"].is_string() {
json["remarks"].as_str().unwrap_or("")
} else {
&format!("{} ({})", server, port_num)
};
let plugin = json["plugin"].as_str().unwrap_or("");
let plugin_opts = json["plugin_opts"].as_str().unwrap_or("");
let mut node = Proxy::ss_construct(
SS_DEFAULT_GROUP,
remark,
server,
port_num,
password,
method,
plugin,
plugin_opts,
None,
None,
None,
None,
"",
);
node.id = index as u32;
nodes.push(node);
return true;
}
if json["servers"].is_array() {
let servers = json["servers"].as_array().unwrap();
let mut index = nodes.len();
for server_json in servers {
let server = server_json["server"].as_str().unwrap_or("");
let port_num = server_json["server_port"].as_u64().unwrap_or(0) as u16;
if server.is_empty() || port_num == 0 {
continue;
}
let method = server_json["method"].as_str().unwrap_or("");
let password = server_json["password"].as_str().unwrap_or("");
let remark = if server_json["remarks"].is_string() {
server_json["remarks"].as_str().unwrap_or("")
} else {
&format!("{} ({})", server, port_num)
};
let plugin = server_json["plugin"].as_str().unwrap_or("");
let plugin_opts = server_json["plugin_opts"].as_str().unwrap_or("");
let mut node = Proxy::ss_construct(
SS_DEFAULT_GROUP,
remark,
server,
port_num,
password,
method,
plugin,
plugin_opts,
None,
None,
None,
None,
"",
);
node.id = index as u32;
nodes.push(node);
index += 1;
}
return !nodes.is_empty();
}
false
}