Skip to main content

eggress_admin/
pac.rs

1use crate::PacConfig;
2
3fn js_escape(s: &str) -> String {
4    let mut result = String::with_capacity(s.len());
5    for c in s.chars() {
6        match c {
7            '\\' => result.push_str("\\\\"),
8            '"' => result.push_str("\\\""),
9            '\n' => result.push_str("\\n"),
10            '\r' => result.push_str("\\r"),
11            '\t' => result.push_str("\\t"),
12            // U+2028/U+2029 are line terminators in JavaScript and break
13            // string literals even though they are not ASCII controls.
14            '\u{2028}' => result.push_str("\\u2028"),
15            '\u{2029}' => result.push_str("\\u2029"),
16            // Raw C0 controls (e.g. NUL) are invalid inside JS strings.
17            c if (c as u32) < 0x20 => {
18                result.push_str(&format!("\\u{:04x}", c as u32));
19            }
20            _ => result.push(c),
21        }
22    }
23    result
24}
25
26pub fn generate_pac(config: &PacConfig) -> String {
27    let mut body = String::new();
28
29    body.push_str("function FindProxyForURL(url, host) {\n");
30    body.push_str("  if (isPlainHostName(host)) return \"DIRECT\";\n");
31
32    let mut direct_hosts = config.direct_hosts.clone();
33    direct_hosts.sort();
34
35    if !direct_hosts.is_empty() {
36        body.push_str("  var directHosts = [");
37        for (i, h) in direct_hosts.iter().enumerate() {
38            if i > 0 {
39                body.push_str(", ");
40            }
41            body.push('"');
42            body.push_str(&js_escape(h));
43            body.push('"');
44        }
45        body.push_str("];\n");
46        body.push_str("  for (var i = 0; i < directHosts.length; i++) {\n");
47        body.push_str(
48            "    if (host == directHosts[i] || shExpMatch(host, \"*.\" + directHosts[i]))\n",
49        );
50        body.push_str("      return \"DIRECT\";\n");
51        body.push_str("  }\n");
52    }
53
54    let mut direct_suffixes = config.direct_suffixes.clone();
55    direct_suffixes.sort();
56
57    if !direct_suffixes.is_empty() {
58        body.push_str("  var directSuffixes = [");
59        for (i, s) in direct_suffixes.iter().enumerate() {
60            if i > 0 {
61                body.push_str(", ");
62            }
63            body.push('"');
64            body.push_str(&js_escape(s));
65            body.push('"');
66        }
67        body.push_str("];\n");
68        body.push_str("  for (var i = 0; i < directSuffixes.length; i++) {\n");
69        body.push_str("    if (shExpMatch(host, \"*.\" + directSuffixes[i]))\n");
70        body.push_str("      return \"DIRECT\";\n");
71        body.push_str("  }\n");
72    }
73
74    let proxy = js_escape(&config.proxy_directive);
75    body.push_str(&format!(
76        "  return \"PROXY {}{}",
77        proxy,
78        if config.direct_fallback {
79            "; DIRECT\";\n"
80        } else {
81            "\";\n"
82        }
83    ));
84
85    body.push_str("}\n");
86    body
87}