From fb887bec10958a824a2e6664fd27ee13c680c267 Mon Sep 17 00:00:00 2001 From: "Lucas.Xu" Date: Mon, 11 Mar 2024 11:02:29 +0800 Subject: [PATCH] fix: potential risk of being unable to launch URLs without HTTP(S) scheme (#4867) --- .../lib/core/helpers/url_launcher.dart | 73 +++++++++++++------ 1 file changed, 49 insertions(+), 24 deletions(-) diff --git a/frontend/appflowy_flutter/lib/core/helpers/url_launcher.dart b/frontend/appflowy_flutter/lib/core/helpers/url_launcher.dart index 28820b9968..91e1beac39 100644 --- a/frontend/appflowy_flutter/lib/core/helpers/url_launcher.dart +++ b/frontend/appflowy_flutter/lib/core/helpers/url_launcher.dart @@ -1,10 +1,10 @@ -import 'package:flutter/services.dart'; -import 'package:flutter/widgets.dart'; - import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:appflowy/workspace/presentation/home/toast.dart'; import 'package:appflowy_backend/log.dart'; import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter/widgets.dart'; +import 'package:string_validator/string_validator.dart'; import 'package:url_launcher/url_launcher.dart' as launcher; typedef OnFailureCallback = void Function(Uri uri); @@ -16,35 +16,60 @@ Future afLaunchUrl( launcher.LaunchMode mode = launcher.LaunchMode.platformDefault, String? webOnlyWindowName, }) async { + // try to launch the uri directly + bool result; try { - return await launcher.launchUrl( - uri, - mode: mode, - webOnlyWindowName: webOnlyWindowName, - ); + result = await launcher.launchUrl(uri); } on PlatformException catch (e) { - Log.error("Failed to open uri: $e"); - if (onFailure != null) { - onFailure(uri); - } else { - showMessageToast( - LocaleKeys.failedToOpenUrl.tr(args: [e.message ?? "PlatformException"]), - context: context, - ); + Log.error('Failed to open uri: $e'); + } finally { + result = false; + } + + // if the uri is not a valid url, try to launch it with https scheme + final url = uri.toString(); + if (!result && !isURL(url, {'require_protocol': true})) { + try { + final uriWithScheme = Uri.parse('https://$url'); + result = await launcher.launchUrl(uriWithScheme); + } on PlatformException catch (e) { + Log.error('Failed to open uri: $e'); + if (context != null && context.mounted) { + _errorHandler(uri, context: context, onFailure: onFailure, e: e); + } } } - return false; + return result; } -Future afLaunchUrlString(String url) async { +Future afLaunchUrlString(String url) async { + final Uri uri; try { - final uri = Uri.parse(url); - - await launcher.launchUrl(uri); - } on PlatformException catch (e) { - Log.error("Failed to open uri: $e"); + uri = Uri.parse(url); } on FormatException catch (e) { - Log.error("Failed to parse url: $e"); + Log.error('Failed to parse url: $e'); + return false; + } + + // try to launch the uri directly + return afLaunchUrl(uri); +} + +void _errorHandler( + Uri uri, { + BuildContext? context, + OnFailureCallback? onFailure, + PlatformException? e, +}) { + Log.error('Failed to open uri: $e'); + + if (onFailure != null) { + onFailure(uri); + } else { + showMessageToast( + LocaleKeys.failedToOpenUrl.tr(args: [e?.message ?? "PlatformException"]), + context: context, + ); } }