libsubconverter/parser/explodes/
sstap.rs

1use crate::models::{
2    Proxy, SOCKS_DEFAULT_GROUP, SSR_DEFAULT_GROUP, SS_CIPHERS, SS_DEFAULT_GROUP,
3};
4use serde_json::{from_str, Value};
5
6/// Parse a SSTap JSON configuration into a vector of Proxy objects
7/// Based on the C++ implementation in explodeSSTap function
8pub fn explode_sstap(content: &str, nodes: &mut Vec<Proxy>) -> bool {
9    // Parse the JSON content
10    let json: Value = match from_str(content) {
11        Ok(json) => json,
12        Err(_) => return false,
13    };
14
15    // Check if it has configs array
16    if !json["configs"].is_array() {
17        return false;
18    }
19
20    let configs = json["configs"].as_array().unwrap();
21    if configs.is_empty() {
22        return false;
23    }
24
25    let mut index = nodes.len();
26    let mut success = false;
27
28    for config in configs {
29        // Extract common fields
30        let group = config["group"].as_str().unwrap_or("");
31        let remarks = config["remarks"].as_str().unwrap_or("");
32        let server = config["server"].as_str().unwrap_or("");
33        let port = config["server_port"].as_u64().unwrap_or(0) as u16;
34
35        // Skip if port is 0
36        if port == 0 {
37            continue;
38        }
39
40        // Use server:port as remark if not provided
41        let final_remarks = if remarks.is_empty() {
42            format!("{} ({})", server, port)
43        } else {
44            remarks.to_string()
45        };
46
47        // Extract password
48        let password = config["password"].as_str().unwrap_or("");
49
50        // Get config type
51        let config_type = config["type"].as_u64().unwrap_or(0);
52
53        match config_type {
54            5 => {
55                // Socks 5
56                let username = config["username"].as_str().unwrap_or("");
57
58                // Create Socks5 proxy
59                let mut node = Proxy::socks_construct(
60                    if group.is_empty() {
61                        SOCKS_DEFAULT_GROUP
62                    } else {
63                        group
64                    },
65                    &final_remarks,
66                    server,
67                    port,
68                    username,
69                    password,
70                    None,
71                    None,
72                    None,
73                    "",
74                );
75
76                node.id = index as u32;
77                nodes.push(node);
78                index += 1;
79                success = true;
80            }
81            6 => {
82                // SS/SSR
83                let protocol = config["protocol"].as_str().unwrap_or("");
84                let obfs = config["obfs"].as_str().unwrap_or("");
85                let method = config["method"].as_str().unwrap_or("");
86
87                // Check if it's SS or SSR
88                if SS_CIPHERS.iter().any(|c| *c == method)
89                    && protocol == "origin"
90                    && obfs == "plain"
91                {
92                    // Is Shadowsocks
93                    let mut node = Proxy::ss_construct(
94                        if group.is_empty() {
95                            SS_DEFAULT_GROUP
96                        } else {
97                            group
98                        },
99                        &final_remarks,
100                        server,
101                        port,
102                        password,
103                        method,
104                        "",
105                        "",
106                        None,
107                        None,
108                        None,
109                        None,
110                        "",
111                    );
112
113                    node.id = index as u32;
114                    nodes.push(node);
115                    index += 1;
116                    success = true;
117                } else {
118                    // Is ShadowsocksR
119                    let obfs_param = config["obfsparam"].as_str().unwrap_or("");
120                    let proto_param = config["protocolparam"].as_str().unwrap_or("");
121
122                    let mut node = Proxy::ssr_construct(
123                        if group.is_empty() {
124                            SSR_DEFAULT_GROUP
125                        } else {
126                            group
127                        },
128                        &final_remarks,
129                        server,
130                        port,
131                        protocol,
132                        method,
133                        obfs,
134                        password,
135                        obfs_param,
136                        proto_param,
137                        None,
138                        None,
139                        None,
140                        "",
141                    );
142
143                    node.id = index as u32;
144                    nodes.push(node);
145                    index += 1;
146                    success = true;
147                }
148            }
149            _ => continue, // Skip unknown type
150        }
151    }
152
153    success
154}