Issues (4)

functions.php (4 issues)

1
<?php
2
/** Detect Type of Config */
3
function detect_type($input)
4
{
5
    $type = "";
6
    if (substr($input, 0, 8) === "vmess://") {
7
        $type = "vmess";
8
    } elseif (substr($input, 0, 8) === "vless://") {
9
        $type = "vless";
10
    } elseif (substr($input, 0, 9) === "trojan://") {
11
        $type = "trojan";
12
    } elseif (substr($input, 0, 5) === "ss://") {
13
        $type = "ss";
14
    }
15
16
    return $type;
17
}
18
19
function parse_config($input)
20
{
21
    $type = detect_type($input);
22
    $parsed_config = [];
23
    switch ($type) {
24
        case "vmess":
25
            $parsed_config = decode_vmess($input);
26
            break;
27
        case "vless":
28
        case "trojan":
29
            $parsed_config = parseProxyUrl($input, $type);
30
            break;
31
        case "ss":
32
            $parsed_config = ParseShadowsocks($input);
33
            break;
34
    }
35
    return $parsed_config;
36
}
37
38
function build_config($input, $type)
39
{
40
    $build_config = "";
41
    switch ($type) {
42
        case "vmess":
43
            $build_config = encode_vmess($input);
44
            break;
45
        case "vless":
46
        case "trojan":
47
            $build_config = buildProxyUrl($input, $type);
48
            break;
49
        case "ss":
50
            $build_config = BuildShadowsocks($input);
51
            break;
52
    }
53
    return $build_config;
54
}
55
56
/** parse vmess configs */
57
function decode_vmess($vmess_config)
58
{
59
    $vmess_data = substr($vmess_config, 8); // remove "vmess://"
60
    $decoded_data = json_decode(base64_decode($vmess_data), true);
61
    return $decoded_data;
62
}
63
64
/** build vmess configs */
65
function encode_vmess($config)
66
{
67
    $encoded_data = base64_encode(json_encode($config));
68
    $vmess_config = "vmess://" . $encoded_data;
69
    return $vmess_config;
70
}
71
72
/** remove duplicate vmess configs */
73
function remove_duplicate_vmess($input)
74
{
75
    $array = explode("\n", $input);
76
    $result = [];
77
    foreach ($array as $item) {
78
        $parts = decode_vmess($item);
79
        if ($parts !== null) {
80
            $part_ps = $parts["ps"];
81
            unset($parts["ps"]);
82
            if (count($parts) >= 3) {
83
                ksort($parts);
84
                $part_serialize = serialize($parts);
85
                $result[$part_serialize][] = $part_ps ?? "";
86
            }
87
        }
88
    }
89
    $finalResult = [];
90
    foreach ($result as $serial => $ps) {
91
        $partAfterHash = $ps[0] ?? "";
92
        $part_serialize = unserialize($serial);
93
        $part_serialize["ps"] = $partAfterHash;
94
        $finalResult[] = encode_vmess($part_serialize);
95
    }
96
    $output = "";
97
    foreach ($finalResult as $config) {
98
        $output .= $output == "" ? $config : "\n" . $config;
99
    }
100
    return $output;
101
}
102
103
/** Parse vless and trojan config*/
104
function parseProxyUrl($url, $type = "trojan")
105
{
106
    // Parse the URL into components
107
    $parsedUrl = parse_url($url);
108
109
    // Extract the parameters from the query string
110
    $params = [];
111
    if (isset($parsedUrl["query"])) {
112
        parse_str($parsedUrl["query"], $params);
113
    }
114
115
    // Construct the output object
116
    $output = [
117
        "protocol" => $type,
118
        "username" => isset($parsedUrl["user"]) ? $parsedUrl["user"] : "",
119
        "hostname" => isset($parsedUrl["host"]) ? $parsedUrl["host"] : "",
120
        "port" => isset($parsedUrl["port"]) ? $parsedUrl["port"] : "",
121
        "params" => $params,
122
        "hash" => isset($parsedUrl["fragment"]) ? $parsedUrl["fragment"] : "",
123
    ];
124
125
    return $output;
126
}
127
128
/** Build vless and trojan config*/
129
function buildProxyUrl($obj, $type = "trojan")
130
{
131
    $url = $type . "://";
132
    $url .= addUsernameAndPassword($obj);
133
    $url .= $obj["hostname"];
134
    $url .= addPort($obj);
135
    $url .= addParams($obj);
136
    $url .= addHash($obj);
137
    return $url;
138
}
139
140
function addUsernameAndPassword($obj)
141
{
142
    $url = "";
143
    if ($obj["username"] !== "") {
144
        $url .= $obj["username"];
145
        if (isset($obj["pass"]) && $obj["pass"] !== "") {
146
            $url .= ":" . $obj["pass"];
147
        }
148
        $url .= "@";
149
    }
150
    return $url;
151
}
152
153
function addPort($obj)
154
{
155
    $url = "";
156
    if (isset($obj["port"]) && $obj["port"] !== "") {
157
        $url .= ":" . $obj["port"];
158
    }
159
    return $url;
160
}
161
162
function addParams($obj)
163
{
164
    $url = "";
165
    if (!empty($obj["params"])) {
166
        $url .= "?" . http_build_query($obj["params"]);
167
    }
168
    return $url;
169
}
170
171
function addHash($obj)
172
{
173
    $url = "";
174
    if (isset($obj["hash"]) && $obj["hash"] !== "") {
175
        $url .= "#" . $obj["hash"];
176
    }
177
    return $url;
178
}
179
180
/** remove duplicate vless and trojan config*/
181
function remove_duplicate_xray($input, $type)
182
{
183
    $array = explode("\n", $input);
184
185
    foreach ($array as $item) {
186
        $parts = parseProxyUrl($item, $type);
187
        $part_hash = $parts["hash"];
188
        unset($parts["hash"]);
189
        ksort($parts["params"]);
190
        $part_serialize = serialize($parts);
191
        $result[$part_serialize][] = $part_hash ?? "";
192
    }
193
194
    $finalResult = [];
195
    foreach ($result as $url => $parts) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $result seems to be defined by a foreach iteration on line 185. Are you sure the iterator is never empty, otherwise this variable is not defined?
Loading history...
196
        $partAfterHash = $parts[0] ?? "";
197
        $part_serialize = unserialize($url);
198
        $part_serialize["hash"] = $partAfterHash;
199
        $finalResult[] = buildProxyUrl($part_serialize, $type);
200
    }
201
202
    $output = "";
203
    foreach ($finalResult as $config) {
204
        $output .= $output == "" ? $config : "\n" . $config;
205
    }
206
    return $output;
207
}
208
209
/** parse shadowsocks configs */
210
function ParseShadowsocks($config_str)
211
{
212
    // Parse the config string as a URL
213
    $url = parse_url($config_str);
214
215
    // Extract the encryption method and password from the user info
216
    list($encryption_method, $password) = explode(
217
        ":",
218
        base64_decode($url["user"])
219
    );
220
221
    // Extract the server address and port from the host and path
222
    $server_address = $url["host"];
223
    $server_port = $url["port"];
224
225
    // Extract the name from the fragment (if present)
226
    $name = isset($url["fragment"]) ? urldecode($url["fragment"]) : null;
227
228
    // Create an array to hold the server configuration
229
    $server = [
230
        "encryption_method" => $encryption_method,
231
        "password" => $password,
232
        "server_address" => $server_address,
233
        "server_port" => $server_port,
234
        "name" => $name,
235
    ];
236
237
    // Return the server configuration as a JSON string
238
    return $server;
239
}
240
241
/** build shadowsocks configs */
242
function BuildShadowsocks($server)
243
{
244
    // Encode the encryption method and password as a Base64-encoded string
245
    $user = base64_encode(
246
        $server["encryption_method"] . ":" . $server["password"]
247
    );
248
249
    // Construct the URL from the server address, port, and user info
250
    $url = "ss://$user@{$server["server_address"]}:{$server["server_port"]}";
251
252
    // If the name is present, add it as a fragment to the URL
253
    if (!empty($server["name"])) {
254
        $url .= "#" . urlencode($server["name"]);
255
    }
256
257
    // Return the URL as a string
258
    return $url;
259
}
260
261
/** remove duplicate shadowsocks configs */
262
function remove_duplicate_ss($input)
263
{
264
    $array = explode("\n", $input);
265
266
    foreach ($array as $item) {
267
        $parts = ParseShadowsocks($item);
268
        $part_hash = $parts["name"];
269
        unset($parts["name"]);
270
        ksort($parts);
271
        $part_serialize = serialize($parts);
272
        $result[$part_serialize][] = $part_hash ?? "";
273
    }
274
275
    $finalResult = [];
276
    foreach ($result as $url => $parts) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $result seems to be defined by a foreach iteration on line 266. Are you sure the iterator is never empty, otherwise this variable is not defined?
Loading history...
277
        $partAfterHash = $parts[0] ?? "";
278
        $part_serialize = unserialize($url);
279
        $part_serialize["name"] = $partAfterHash;
280
        $finalResult[] = BuildShadowsocks($part_serialize);
281
    }
282
283
    $output = "";
284
    foreach ($finalResult as $config) {
285
        $output .= $output == "" ? $config : "\n" . $config;
286
    }
287
    return $output;
288
}
289
290
function is_ip($string)
291
{
292
    $ipv4_pattern = '/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/';
293
    $ipv6_pattern = '/^[0-9a-fA-F:]+$/'; // matches any valid IPv6 address
294
295
    if (preg_match($ipv4_pattern, $string) || preg_match($ipv6_pattern, $string)) {
296
        return true;
297
    } else {
298
        return false;
299
    }
300
}
301
302
function ip_info($ip)
303
{
304
    if (is_ip($ip) === false) {
305
        $ip_address_array = dns_get_record($ip, DNS_A);
306
        if (is_array($ip_address_array)) {
0 ignored issues
show
The condition is_array($ip_address_array) is always true.
Loading history...
307
            $randomKey = array_rand($ip_address_array);
308
            $ip = $ip_address_array[$randomKey]["ip"];
309
        }
310
    }
311
    $ipinfo = json_decode(
312
        file_get_contents("https://api.country.is/" . $ip),
313
        true
314
    );
315
    return $ipinfo;
316
}
317
318
function get_flag($ip)
319
{
320
    $flag = "";
321
    $ip_info = ip_info($ip);
322
    if (isset($ip_info["country"])) {
323
        $location = $ip_info["country"];
324
        $flag = $location . getFlags($location);
325
    } else {
326
        $flag = "RELAY🚩";
327
    }
328
    return $flag;
329
}
330
331
function getFlags($country_code)
332
{
333
    $flag = mb_convert_encoding(
334
        "&#" . (127397 + ord($country_code[0])) . ";",
335
        "UTF-8",
336
        "HTML-ENTITIES"
337
    );
338
    $flag .= mb_convert_encoding(
339
        "&#" . (127397 + ord($country_code[1])) . ";",
340
        "UTF-8",
341
        "HTML-ENTITIES"
342
    );
343
    return $flag;
344
}
345
346
347
function get_ip($config, $type, $is_reality)
348
{
349
    switch ($type) {
350
        case "vmess":
351
            return get_vmess_ip($config);
352
        case "vless":
353
            return get_vless_ip($config, $is_reality);
354
        case "trojan":
355
            return get_trojan_ip($config);
356
        case "ss":
357
            return get_ss_ip($config);
358
    }
359
}
360
361
function get_vmess_ip($input)
362
{
363
    return !empty($input["sni"])
364
        ? $input["sni"]
365
        : (!empty($input["host"])
366
            ? $input["host"]
367
            : $input["add"]);
368
}
369
370
function get_vless_ip($input, $is_reality)
371
{
372
    return $is_reality
373
        ? $input["hostname"]
374
        : (!empty($input["params"]["sni"])
375
            ? $input["params"]["sni"]
376
            : (!empty($input["params"]["host"])
377
                ? $input["params"]["host"]
378
                : $input["hostname"]));
379
}
380
381
function get_trojan_ip($input)
382
{
383
    return !empty($input["params"]["sni"])
384
        ? $input["params"]["sni"]
385
        : (!empty($input["params"]["host"])
386
            ? $input["params"]["host"]
387
            : $input["hostname"]);
388
}
389
390
function get_ss_ip($input)
391
{
392
    return $input["server_address"];
393
}
394
395
function get_port($input, $type)
396
{
397
    $port = "";
398
    switch ($type) {
399
        case "vmess":
400
            $port = $input["port"];
401
            break;
402
        case "vless":
403
            $port = $input["port"];
404
            break;
405
        case "trojan":
406
            $port = $input["port"];
407
            break;
408
        case "ss":
409
            $port = $input["server_port"];
410
            break;
411
    }
412
    return $port;
413
}
414
415
function ping($ip, $port)
416
{
417
    $it = microtime(true);
418
    $check = @fsockopen($ip, $port, $errno, $errstr, 0.5);
419
    $ft = microtime(true);
420
    $militime = round(($ft - $it) * 1e3, 2);
421
    if ($check) {
0 ignored issues
show
$check is of type false|resource, thus it always evaluated to false.
Loading history...
422
        fclose($check);
423
        return $militime;
424
    } else {
425
        return "unavailable";
426
    }
427
}
428
429
function generate_name($flag, $ip, $port, $ping, $is_reality)
430
{
431
    $name = "";
432
    switch ($is_reality) {
433
        case true:
434
            $name =
435
                "REALITY|" .
436
                $flag .
437
                " | " .
438
                $ip .
439
                "-" .
440
                $port .
441
                " | " .
442
                $ping .
443
                "ms";
444
            break;
445
        case false:
446
            $name =
447
                $flag .
448
                " | " .
449
                $ip .
450
                "-" .
451
                $port .
452
                " | " .
453
                $ping .
454
                "ms";
455
            break;
456
    }
457
    return $name;
458
}
459
460
function process_config($config)
461
{
462
    $name_array = [
463
        "vmess" => "ps",
464
        "vless" => "hash",
465
        "trojan" => "hash",
466
        "ss" => "name",
467
    ];
468
    $type = detect_type($config);
469
    $is_reality = stripos($config, "reality") !== false ? true : false;
470
    $parsed_config = parse_config($config);
471
    $ip = get_ip($parsed_config, $type, $is_reality);
472
    $port = get_port($parsed_config, $type);
473
    $ping_data = ping($ip, $port);
474
    if ($ping_data !== "unavailable") {
475
        $flag = get_flag($ip);
476
        $name_key = $name_array[$type];
477
        $parsed_config[$name_key] = generate_name(
478
            $flag,
479
            $ip,
480
            $port,
481
            $ping_data,
482
            $is_reality
483
        );
484
        $final_config = build_config($parsed_config, $type);
485
        return $final_config;
486
    }
487
    return false;
488
}
489
490
/** Extract reality configs */
491
function get_reality($input)
492
{
493
    $array = explode("\n", $input);
494
    $output = "";
495
    foreach ($array as $item) {
496
        if (stripos($item, "reality")) {
497
            $output .= $output === "" ? $item : "\n$item";
498
        }
499
    }
500
    return $output;
501
}
502
503
/** Check if subscription is base64 encoded or not */
504
function is_base64_encoded($string)
505
{
506
    if (base64_encode(base64_decode($string, true)) === $string) {
507
        return "true";
508
    } else {
509
        return "false";
510
    }
511
}
512
513
function process_subscriptions($input)
514
{
515
    $output = [];
516
    if (is_base64_encoded($input) === "true") {
517
        $data = base64_decode($input);
518
        $output = process_subscriptions_helper($data);
519
    } else {
520
        $output = process_subscriptions_helper($input);
521
    }
522
    return $output;
523
}
524
525
function process_subscriptions_helper($input)
526
{
527
    $output = [];
528
    $data_array = explode("\n", $input);
529
    foreach ($data_array as $config) {
530
        $processed_config = process_config($config);
531
        if ($processed_config !== false) {
532
            $type = detect_type($processed_config);
533
            switch ($type) {
534
                case "vmess":
535
                    $output["vmess"][] = $processed_config;
536
                    break;
537
                case "vless":
538
                    $output["vless"][] = $processed_config;
539
                    break;
540
                case "trojan":
541
                    $output["trojan"][] = $processed_config;
542
                    break;
543
                case "ss":
544
                    $output["ss"][] = $processed_config;
545
                    break;
546
            }
547
        }
548
    }
549
    return $output;
550
}
551
552
function merge_subscription($input)
553
{
554
    $output = [];
555
    $vmess = "";
556
    $vless = "";
557
    $trojan = "";
558
    $shadowsocks = "";
559
    foreach ($input as $subscription_url) {
560
        $subscription_data = file_get_contents($subscription_url);
561
        $processed_array = process_subscriptions($subscription_data);
562
        $vmess .= isset($processed_array["vmess"])
563
            ? implode("\n", $processed_array["vmess"]) . "\n"
564
            : null;
565
        $vless .= isset($processed_array["vless"])
566
            ? implode("\n", $processed_array["vless"]) . "\n"
567
            : null;
568
        $trojan .= isset($processed_array["trojan"])
569
            ? implode("\n", $processed_array["trojan"]) . "\n"
570
            : null;
571
        $shadowsocks .= isset($processed_array["ss"])
572
            ? implode("\n", $processed_array["ss"]) . "\n"
573
            : null;
574
    }
575
    $output['vmess'] = explode("\n", $vmess);
576
    $output['vless'] = explode("\n", $vless);
577
    $output['trojan'] = explode("\n", $trojan);
578
    $output['ss'] = explode("\n", $shadowsocks);
579
    return $output;
580
}
581
582
function array_to_subscription($input) {
583
    return implode("\n", $input);
584
}
585