libsubconverter/parser/explodes/
ss.rs1use crate::models::{Proxy, SS_DEFAULT_GROUP};
2use crate::utils::url::url_decode;
3use base64::engine::general_purpose::STANDARD;
4use base64::Engine;
5use serde_json::Value;
6
7pub fn explode_ss(ss: &str, node: &mut Proxy) -> bool {
10 if !ss.starts_with("ss://") {
12 return false;
13 }
14
15 let mut ss_content = ss[5..].to_string();
17 ss_content = ss_content.replace("/?", "?");
19
20 let mut ps = String::new();
22 if let Some(hash_pos) = ss_content.find('#') {
23 ps = url_decode(&ss_content[hash_pos + 1..]);
24 ss_content = ss_content[..hash_pos].to_string();
25 }
26
27 let mut plugin = String::new();
29 let mut plugin_opts = String::new();
30 let mut group = SS_DEFAULT_GROUP.to_string();
31
32 if let Some(query_pos) = ss_content.find('?') {
33 let addition = ss_content[query_pos + 1..].to_string();
34 ss_content = ss_content[..query_pos].to_string();
35
36 for (key, value) in url::form_urlencoded::parse(addition.as_bytes()) {
38 if key == "plugin" {
39 let plugins = url_decode(&value);
40 if let Some(semicolon_pos) = plugins.find(';') {
41 plugin = plugins[..semicolon_pos].to_string();
42 plugin_opts = plugins[semicolon_pos + 1..].to_string();
43 } else {
44 plugin = plugins;
45 }
46 } else if key == "group" {
47 if !value.is_empty() {
48 group = crate::utils::base64::url_safe_base64_decode(&value);
49 }
50 }
51 }
52 }
53
54 let method;
56 let password;
57 let server;
58 let port;
59
60 if ss_content.contains('@') {
61 let parts: Vec<&str> = ss_content.split('@').collect();
63 if parts.len() < 2 {
64 return false;
65 }
66
67 let secret = parts[0];
68 let server_port = parts[1];
69
70 let server_port_parts: Vec<&str> = server_port.split(':').collect();
72 if server_port_parts.len() < 2 {
73 return false;
74 }
75 server = server_port_parts[0].to_string();
76 port = match server_port_parts[1].parse::<u16>() {
77 Ok(p) => p,
78 Err(_) => return false,
79 };
80
81 let decoded_secret = crate::utils::base64::url_safe_base64_decode(secret);
83 let method_pass: Vec<&str> = decoded_secret.split(':').collect();
84 if method_pass.len() < 2 {
85 return false;
86 }
87 method = method_pass[0].to_string();
88 password = method_pass[1..].join(":"); } else {
90 let decoded = crate::utils::base64::url_safe_base64_decode(&ss_content);
92 if decoded.is_empty() {
93 return false;
94 }
95
96 let parts: Vec<&str> = decoded.split('@').collect();
98 if parts.len() < 2 {
99 return false;
100 }
101
102 let method_pass = parts[0];
103 let server_port = parts[1];
104
105 let method_pass_parts: Vec<&str> = method_pass.split(':').collect();
107 if method_pass_parts.len() < 2 {
108 return false;
109 }
110 method = method_pass_parts[0].to_string();
111 password = method_pass_parts[1..].join(":"); let server_port_parts: Vec<&str> = server_port.split(':').collect();
115 if server_port_parts.len() < 2 {
116 return false;
117 }
118 server = server_port_parts[0].to_string();
119 port = match server_port_parts[1].parse::<u16>() {
120 Ok(p) => p,
121 Err(_) => return false,
122 };
123 }
124
125 if port == 0 {
127 return false;
128 }
129
130 if ps.is_empty() {
132 ps = format!("{} ({})", server, port);
133 }
134
135 *node = Proxy::ss_construct(
137 &group,
138 &ps,
139 &server,
140 port,
141 &password,
142 &method,
143 &plugin,
144 &plugin_opts,
145 None,
146 None,
147 None,
148 None,
149 "",
150 );
151
152 true
153}
154
155pub fn explode_ssd(link: &str, nodes: &mut Vec<Proxy>) -> bool {
157 if !link.starts_with("ssd://") {
159 return false;
160 }
161
162 let encoded = &link[6..];
164
165 let decoded = match STANDARD.decode(encoded) {
167 Ok(bytes) => match String::from_utf8(bytes) {
168 Ok(s) => s,
169 Err(_) => return false,
170 },
171 Err(_) => return false,
172 };
173
174 let json: Value = match serde_json::from_str(&decoded) {
176 Ok(json) => json,
177 Err(_) => return false,
178 };
179
180 let airport = json["airport"].as_str().unwrap_or("");
182 let port = json["port"].as_u64().unwrap_or(0) as u16;
183 let encryption = json["encryption"].as_str().unwrap_or("");
184 let password = json["password"].as_str().unwrap_or("");
185
186 if !json["servers"].is_array() {
188 return false;
189 }
190
191 let servers = json["servers"].as_array().unwrap();
192
193 for server in servers {
194 let server_host = server["server"].as_str().unwrap_or("");
195 let server_port = server["port"].as_u64().unwrap_or(port as u64) as u16;
196 let server_encryption = server["encryption"].as_str().unwrap_or(encryption);
197 let server_password = server["password"].as_str().unwrap_or(password);
198 let server_remark = server["remarks"].as_str().unwrap_or("");
199 let server_plugin = server["plugin"].as_str().unwrap_or("");
200 let server_plugin_opts = server["plugin_options"].as_str().unwrap_or("");
201
202 let formatted_remark = format!("{} - {}", airport, server_remark);
204
205 let node = Proxy::ss_construct(
207 SS_DEFAULT_GROUP,
208 &formatted_remark,
209 server_host,
210 server_port,
211 server_password,
212 server_encryption,
213 server_plugin,
214 server_plugin_opts,
215 None,
216 None,
217 None,
218 None,
219 "",
220 );
221
222 nodes.push(node);
223 }
224
225 !nodes.is_empty()
226}
227
228pub fn explode_ss_android(content: &str, nodes: &mut Vec<Proxy>) -> bool {
230 let json: Value = match serde_json::from_str(content) {
232 Ok(json) => json,
233 Err(_) => {
234 println!(
235 "Error parsing Android Shadowsocks configuration: {}",
236 content
237 );
238 return false;
239 }
240 };
241
242 if !json["configs"].is_array() && !json["proxies"].is_array() {
244 return false;
245 }
246
247 let configs = if json["configs"].is_array() {
249 json["configs"].as_array().unwrap()
250 } else {
251 json["proxies"].as_array().unwrap()
252 };
253
254 let mut index = nodes.len();
255
256 for config in configs {
257 let server = config["server"].as_str().unwrap_or("");
259 if server.is_empty() {
260 continue;
261 }
262
263 let port_num = config["server_port"].as_u64().unwrap_or(0) as u16;
264 if port_num == 0 {
265 continue;
266 }
267
268 let method = config["method"].as_str().unwrap_or("");
269 let password = config["password"].as_str().unwrap_or("");
270
271 let remark = if config["remarks"].is_string() {
273 config["remarks"].as_str().unwrap_or("").to_string()
274 } else if config["name"].is_string() {
275 config["name"].as_str().unwrap_or("").to_string()
276 } else {
277 format!("{} ({})", server, port_num)
278 };
279
280 let plugin = config["plugin"].as_str().unwrap_or("");
282 let plugin_opts = config["plugin_opts"].as_str().unwrap_or("");
283
284 let mut node = Proxy::ss_construct(
286 SS_DEFAULT_GROUP,
287 &remark,
288 server,
289 port_num,
290 password,
291 method,
292 plugin,
293 plugin_opts,
294 None,
295 None,
296 None,
297 None,
298 "",
299 );
300
301 node.id = index as u32;
302 nodes.push(node);
303 index += 1;
304 }
305
306 !nodes.is_empty()
307}
308
309pub fn explode_ss_conf(content: &str, nodes: &mut Vec<Proxy>) -> bool {
311 let json: Value = match serde_json::from_str(content) {
313 Ok(json) => json,
314 Err(_) => return false,
315 };
316
317 if json["configs"].is_array() || json["proxies"].is_array() {
319 return explode_ss_android(content, nodes);
320 }
321
322 if json["server"].is_string() && json["server_port"].is_u64() {
324 let index = nodes.len();
325
326 let server = json["server"].as_str().unwrap_or("");
328 let port_num = json["server_port"].as_u64().unwrap_or(0) as u16;
329 if server.is_empty() || port_num == 0 {
330 return false;
331 }
332
333 let method = json["method"].as_str().unwrap_or("");
334 let password = json["password"].as_str().unwrap_or("");
335
336 let remark = if json["remarks"].is_string() {
338 json["remarks"].as_str().unwrap_or("")
339 } else {
340 &format!("{} ({})", server, port_num)
341 };
342
343 let plugin = json["plugin"].as_str().unwrap_or("");
345 let plugin_opts = json["plugin_opts"].as_str().unwrap_or("");
346
347 let mut node = Proxy::ss_construct(
349 SS_DEFAULT_GROUP,
350 remark,
351 server,
352 port_num,
353 password,
354 method,
355 plugin,
356 plugin_opts,
357 None,
358 None,
359 None,
360 None,
361 "",
362 );
363
364 node.id = index as u32;
365 nodes.push(node);
366
367 return true;
368 }
369
370 if json["servers"].is_array() {
372 let servers = json["servers"].as_array().unwrap();
373 let mut index = nodes.len();
374
375 for server_json in servers {
376 let server = server_json["server"].as_str().unwrap_or("");
378 let port_num = server_json["server_port"].as_u64().unwrap_or(0) as u16;
379 if server.is_empty() || port_num == 0 {
380 continue;
381 }
382
383 let method = server_json["method"].as_str().unwrap_or("");
384 let password = server_json["password"].as_str().unwrap_or("");
385
386 let remark = if server_json["remarks"].is_string() {
388 server_json["remarks"].as_str().unwrap_or("")
389 } else {
390 &format!("{} ({})", server, port_num)
391 };
392
393 let plugin = server_json["plugin"].as_str().unwrap_or("");
395 let plugin_opts = server_json["plugin_opts"].as_str().unwrap_or("");
396
397 let mut node = Proxy::ss_construct(
399 SS_DEFAULT_GROUP,
400 remark,
401 server,
402 port_num,
403 password,
404 method,
405 plugin,
406 plugin_opts,
407 None,
408 None,
409 None,
410 None,
411 "",
412 );
413
414 node.id = index as u32;
415 nodes.push(node);
416 index += 1;
417 }
418
419 return !nodes.is_empty();
420 }
421
422 false
423}