From 96c1ed612d7313a9cfe880e926fe8812a52a1744 Mon Sep 17 00:00:00 2001 From: Imran Remtulla Date: Sat, 27 Aug 2022 22:22:59 -0400 Subject: [PATCH] Added F-Droid, Mullvad. Bug fixes. --- lib/providers/source_provider.dart | 97 ++++++++++++++++++++++++++++-- 1 file changed, 91 insertions(+), 6 deletions(-) diff --git a/lib/providers/source_provider.dart b/lib/providers/source_provider.dart index 3e03716..8887d1c 100644 --- a/lib/providers/source_provider.dart +++ b/lib/providers/source_provider.dart @@ -95,7 +95,7 @@ class GitHub implements AppSource { @override String standardizeURL(String url) { - RegExp standardUrlRegEx = RegExp('^https?://$host/[^/]*/[^/]*'); + RegExp standardUrlRegEx = RegExp('^https?://$host/[^/]+/[^/]+'); RegExpMatch? match = standardUrlRegEx.firstMatch(url.toLowerCase()); if (match == null) { throw 'Not a valid URL'; @@ -105,7 +105,6 @@ class GitHub implements AppSource { @override Future getLatestAPKDetails(String standardUrl) async { - // The GitHub RSS feed does not contain asset download details, so we use web scraping (avoid API due to rate limits) Response res = await get(Uri.parse('$standardUrl/releases/latest')); if (res.statusCode == 200) { var standardUri = Uri.parse(standardUrl); @@ -154,7 +153,7 @@ class GitLab implements AppSource { @override String standardizeURL(String url) { - RegExp standardUrlRegEx = RegExp('^https?://$host/[^/]*/[^/]*'); + RegExp standardUrlRegEx = RegExp('^https?://$host/[^/]+/[^/]+'); RegExpMatch? match = standardUrlRegEx.firstMatch(url.toLowerCase()); if (match == null) { throw 'Not a valid URL'; @@ -164,7 +163,6 @@ class GitLab implements AppSource { @override Future getLatestAPKDetails(String standardUrl) async { - // GitLab provides an RSS feed with all the details we need Response res = await get(Uri.parse('$standardUrl/-/tags?format=atom')); if (res.statusCode == 200) { var standardUri = Uri.parse(standardUrl); @@ -231,11 +229,98 @@ class Signal implements AppSource { } @override - AppNames getAppNames(String standardUrl) => AppNames('signal', 'signal'); + AppNames getAppNames(String standardUrl) => AppNames('Signal', 'Signal'); +} + +class FDroid implements AppSource { + @override + late String host = 'f-droid.org'; + + @override + String standardizeURL(String url) { + RegExp standardUrlRegEx = RegExp('^https?://$host/[^/]+/packages/[^/]+'); + RegExpMatch? match = standardUrlRegEx.firstMatch(url.toLowerCase()); + if (match == null) { + throw 'Not a valid URL'; + } + return url.substring(0, match.end); + } + + @override + Future getLatestAPKDetails(String standardUrl) async { + Response res = await get(Uri.parse(standardUrl)); + if (res.statusCode == 200) { + var latestReleaseDiv = + parse(res.body).querySelector('#latest.package-version'); + var apkUrl = latestReleaseDiv + ?.querySelector('.package-version-download a') + ?.attributes['href']; + if (apkUrl == null) { + throw 'No APK found'; + } + var version = latestReleaseDiv + ?.querySelector('.package-version-header b') + ?.innerHtml + .split(' ') + .last; + if (version == null) { + throw 'Could not determine latest release version'; + } + return APKDetails(version, [apkUrl]); + } else { + throw 'Unable to fetch release info'; + } + } + + @override + AppNames getAppNames(String standardUrl) { + var name = Uri.parse(standardUrl).pathSegments.last; + return AppNames(name, name); + } +} + +class Mullvad implements AppSource { + @override + late String host = 'mullvad.net'; + + @override + String standardizeURL(String url) { + RegExp standardUrlRegEx = RegExp('^https?://$host'); + RegExpMatch? match = standardUrlRegEx.firstMatch(url.toLowerCase()); + if (match == null) { + throw 'Not a valid URL'; + } + return url.substring(0, match.end); + } + + @override + Future getLatestAPKDetails(String standardUrl) async { + Response res = await get(Uri.parse('$standardUrl/en/download/android')); + if (res.statusCode == 200) { + var version = parse(res.body) + .querySelector('p.subtitle.is-6') + ?.querySelector('a') + ?.attributes['href'] + ?.split('/') + .last; + if (version == null) { + throw 'Could not determine the latest release version'; + } + return APKDetails( + version, ['https://mullvad.net/download/app/apk/latest']); + } else { + throw 'Unable to fetch release info'; + } + } + + @override + AppNames getAppNames(String standardUrl) { + return AppNames('Mullvad-VPN', 'Mullvad-VPN'); + } } class SourceProvider { - List sources = [GitHub(), GitLab(), Signal()]; + List sources = [GitHub(), GitLab(), FDroid(), Mullvad(), Signal()]; // Add more source classes here so they are available via the service AppSource getSource(String url) {