From 569088aa1aeebfd43ab28458a30a5a5bcd13a460 Mon Sep 17 00:00:00 2001 From: Hiddify Date: Sat, 27 Jan 2024 03:11:52 +0100 Subject: [PATCH] new: add link parser and allow custom configs to be imported --- .../profile/data/profile_repository.dart | 18 +++++- lib/utils/link_parsers.dart | 58 +++++++++++-------- 2 files changed, 50 insertions(+), 26 deletions(-) diff --git a/lib/features/profile/data/profile_repository.dart b/lib/features/profile/data/profile_repository.dart index 24fb1594..09ec74e2 100644 --- a/lib/features/profile/data/profile_repository.dart +++ b/lib/features/profile/data/profile_repository.dart @@ -361,7 +361,7 @@ class ProfileRepositoryImpl ); } - final _subInfoHeaders = [ + static final _subInfoHeaders = [ 'profile-title', 'content-disposition', 'subscription-userinfo', @@ -418,8 +418,20 @@ class ProfileRepositoryImpl "only [$headersFound] headers found, checking file content for possible information", ); var content = await File(path).readAsString(); - content = safeDecodeBase64(content); - final lines = content.split("\n"); + final contentHeaders = parseHeadersFromContent(content); + for (final entry in contentHeaders.entries) { + if (!headers.keys.contains(entry.key) && entry.value.isNotEmpty) { + headers[entry.key] = entry.value; + } + } + + return headers; + } + + static Map> parseHeadersFromContent(String content) { + final headers = >{}; + final content_ = safeDecodeBase64(content); + final lines = content_.split("\n"); final linesToProcess = lines.length < 10 ? lines.length : 10; for (int i = 0; i < linesToProcess; i++) { final line = lines[i]; diff --git a/lib/utils/link_parsers.dart b/lib/utils/link_parsers.dart index 869ce8f5..9e1d17d0 100644 --- a/lib/utils/link_parsers.dart +++ b/lib/utils/link_parsers.dart @@ -1,5 +1,8 @@ import 'dart:convert'; +import 'package:dartx/dartx.dart'; +import 'package:hiddify/features/profile/data/profile_parser.dart'; +import 'package:hiddify/features/profile/data/profile_repository.dart'; import 'package:hiddify/singbox/model/singbox_proxy_type.dart'; import 'package:hiddify/utils/validators.dart'; @@ -15,16 +18,14 @@ abstract class LinkParser { host: uri.host, path: uri.path, query: uri.query, - fragment: name??uri.fragment, + fragment: name ?? uri.fragment, ); // return 'hiddify://import/$modifiedUri'; return '$modifiedUri'; - } // protocols schemas static const protocols = {'clash', 'clashmeta', 'sing-box', 'hiddify'}; - static ProfileLink? parse(String link) { return simple(link) ?? deep(link); @@ -40,31 +41,39 @@ abstract class LinkParser { } static ({String content, String name})? protocol(String content) { - final lines = safeDecodeBase64(content).split('\n'); + final normalContent = safeDecodeBase64(content); + final lines = normalContent.split('\n'); + String? name; 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, - 'warp'=>fragment ?? ProxyType.warp.label, - _ => ProxyType.unknown.label, - }; if (name != null) { - return (content: content, name: name); + 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, + 'warp' => fragment ?? ProxyType.warp.label, + _ => null, + }; } } - return null; + final headers = ProfileRepositoryImpl.parseHeadersFromContent(content); + final subinfo = ProfileParser.parse("", headers); + + if (subinfo.name.isNotNullOrEmpty) { + name = subinfo.name; + } + + return (content: normalContent, name: name ?? ProxyType.unknown.label); } static ProfileLink? deep(String link) { @@ -81,9 +90,12 @@ abstract class LinkParser { !queryParams.containsKey('url')) return null; return (url: queryParams['url']!, name: queryParams['name'] ?? ''); case 'hiddify': - if (uri.authority=="import") { - return (url: uri.path.substring(1)+(uri.hasQuery?"?${uri.query}":""), name: uri.fragment); - } + if (uri.authority == "import") { + return ( + url: uri.path.substring(1) + (uri.hasQuery ? "?${uri.query}" : ""), + name: uri.fragment + ); + } //for backward compatibility if ((uri.authority != 'install-config' && uri.authority != 'install-sub') ||