106 lines
3.2 KiB
Dart
106 lines
3.2 KiB
Dart
import 'dart:convert';
|
|
|
|
import 'package:hiddify/singbox/model/singbox_proxy_type.dart';
|
|
import 'package:hiddify/utils/validators.dart';
|
|
|
|
typedef ProfileLink = ({String url, String name});
|
|
|
|
// TODO: test and improve
|
|
abstract class LinkParser {
|
|
static String generateSubShareLink(String url, [String? name]) {
|
|
final uri = Uri.tryParse(url);
|
|
if (uri == null) return '';
|
|
return Uri(
|
|
scheme: 'hiddify',
|
|
host: 'install-sub',
|
|
queryParameters: {
|
|
"url": uri.toString(),
|
|
if (name != null) "name": name,
|
|
},
|
|
).toString();
|
|
}
|
|
|
|
// protocols schemas
|
|
static const protocols = {'clash', 'clashmeta', 'sing-box', 'hiddify'};
|
|
static const rawProtocols = {
|
|
'ss',
|
|
'vmess',
|
|
'vless',
|
|
'trojan',
|
|
'tuic',
|
|
'hysteria2',
|
|
'ssh',
|
|
};
|
|
|
|
static ProfileLink? parse(String link) {
|
|
return simple(link) ?? deep(link);
|
|
}
|
|
|
|
static ProfileLink? simple(String link) {
|
|
if (!isUrl(link)) return null;
|
|
final uri = Uri.parse(link.trim());
|
|
return (
|
|
url: uri.toString(),
|
|
name: uri.queryParameters['name'] ?? '',
|
|
);
|
|
}
|
|
|
|
static ({String content, String name})? protocol(String content) {
|
|
final lines = safeDecodeBase64(content).split('\n');
|
|
for (final line in lines) {
|
|
final uri = Uri.tryParse(line);
|
|
if (uri == null) continue;
|
|
final fragment =
|
|
uri.hasFragment ? Uri.decodeComponent(uri.fragment) : null;
|
|
final name = switch (uri.scheme) {
|
|
'ss' => fragment ?? ProxyType.shadowsocks.label,
|
|
'ssconf' => fragment ?? ProxyType.shadowsocks.label,
|
|
'vmess' => ProxyType.vmess.label,
|
|
'vless' => fragment ?? ProxyType.vless.label,
|
|
'trojan' => fragment ?? ProxyType.trojan.label,
|
|
'tuic' => fragment ?? ProxyType.tuic.label,
|
|
'hy2' || 'hysteria2' => fragment ?? ProxyType.hysteria2.label,
|
|
'hy' || 'hysteria' => fragment ?? ProxyType.hysteria.label,
|
|
'ssh' => fragment ?? ProxyType.ssh.label,
|
|
'wg' => fragment ?? ProxyType.wireguard.label,
|
|
_ => null,
|
|
};
|
|
if (name != null) {
|
|
return (content: content, name: name);
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
static ProfileLink? deep(String link) {
|
|
final uri = Uri.tryParse(link.trim());
|
|
if (uri == null || !uri.hasScheme || !uri.hasAuthority) return null;
|
|
final queryParams = uri.queryParameters;
|
|
switch (uri.scheme) {
|
|
case 'clash' || 'clashmeta' when uri.authority == 'install-config':
|
|
if (uri.authority != 'install-config' ||
|
|
!queryParams.containsKey('url')) return null;
|
|
return (url: queryParams['url']!, name: queryParams['name'] ?? '');
|
|
case 'sing-box':
|
|
if (uri.authority != 'import-remote-profile' ||
|
|
!queryParams.containsKey('url')) return null;
|
|
return (url: queryParams['url']!, name: queryParams['name'] ?? '');
|
|
case 'hiddify':
|
|
if ((uri.authority != 'install-config' &&
|
|
uri.authority != 'install-sub') ||
|
|
!queryParams.containsKey('url')) return null;
|
|
return (url: queryParams['url']!, name: queryParams['name'] ?? '');
|
|
default:
|
|
return null;
|
|
}
|
|
}
|
|
}
|
|
|
|
String safeDecodeBase64(String str) {
|
|
try {
|
|
return utf8.decode(base64Decode(str));
|
|
} catch (e) {
|
|
return str;
|
|
}
|
|
}
|