diff --git a/flutter/lib/main.dart b/flutter/lib/main.dart index 91773afe7..9bd68ed60 100644 --- a/flutter/lib/main.dart +++ b/flutter/lib/main.dart @@ -27,6 +27,7 @@ import 'common.dart'; import 'consts.dart'; import 'mobile/pages/home_page.dart'; import 'mobile/pages/server_page.dart'; +import 'mobile/widgets/deploy_dialog.dart'; import 'models/platform_model.dart'; import 'package:flutter_hbb/plugin/handlers.dart' @@ -575,6 +576,14 @@ _registerEventHandler() { NativeUiHandler.instance.onEvent(evt); }); } + if (isAndroid) { + platformFFI.registerEventHandler( + 'android_needs_deploy', 'android_needs_deploy', (_) async { + WidgetsBinding.instance.addPostFrameCallback((_) { + showDeployPromptDialog(); + }); + }); + } } Widget keyListenerBuilder(BuildContext context, Widget? child) { diff --git a/flutter/lib/mobile/pages/settings_page.dart b/flutter/lib/mobile/pages/settings_page.dart index 509260636..cb40aff81 100644 --- a/flutter/lib/mobile/pages/settings_page.dart +++ b/flutter/lib/mobile/pages/settings_page.dart @@ -17,6 +17,7 @@ import '../../common/widgets/login.dart'; import '../../consts.dart'; import '../../models/model.dart'; import '../../models/platform_model.dart'; +import '../widgets/deploy_dialog.dart'; import '../widgets/dialog.dart'; import 'home_page.dart'; import 'scan_page.dart'; @@ -728,6 +729,13 @@ class _SettingsState extends State with WidgetsBindingObserver { onPressed: (context) { changeSocks5Proxy(); }), + if (isAndroid && !bind.isOutgoingOnly()) + SettingsTile( + title: Text(translate('Deploy')), + leading: Icon(Icons.cloud_upload), + onPressed: (context) { + showDeployDialog(); + }), if (!disabledSettings && !_hideNetwork && !_hideWebSocket) SettingsTile.switchTile( title: Text(translate('Use WebSocket')), diff --git a/flutter/lib/mobile/widgets/deploy_dialog.dart b/flutter/lib/mobile/widgets/deploy_dialog.dart new file mode 100644 index 000000000..522172a8f --- /dev/null +++ b/flutter/lib/mobile/widgets/deploy_dialog.dart @@ -0,0 +1,114 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; + +import '../../common.dart'; +import '../../models/platform_model.dart'; + +const _deployDialogTag = 'android-deploy-device'; + +void showDeployPromptDialog() { + gFFI.dialogManager.dismissByTag(_deployDialogTag); + gFFI.dialogManager.show((setState, close, context) { + submit() => close(true); + return CustomAlertDialog( + title: Text(translate("Deploy")), + content: Text(translate("server_requires_deployment_tip")), + actions: [ + dialogButton("Cancel", onPressed: close, isOutline: true), + dialogButton("OK", onPressed: submit), + ], + onSubmit: submit, + onCancel: close, + ); + }, tag: _deployDialogTag).then((deploy) { + if (deploy == true) { + showDeployDialog(); + } + }); +} + +void showDeployDialog() { + gFFI.dialogManager.dismissByTag(_deployDialogTag); + final tokenController = TextEditingController(); + final idController = TextEditingController(); + var errorText = ""; + var isInProgress = false; + gFFI.dialogManager.show((setState, close, context) { + submit() async { + if (isInProgress) return; + final token = tokenController.text.trim(); + if (token.isEmpty) { + setState(() { + errorText = translate("token is required!"); + }); + return; + } + setState(() { + errorText = ""; + isInProgress = true; + }); + String res; + try { + res = await bind.mainDeployDevice( + token: token, id: idController.text.trim()); + } catch (e) { + setState(() { + errorText = translate(e.toString()); + isInProgress = false; + }); + return; + } + if (res.isEmpty) { + close(); + await gFFI.serverModel.fetchID(); + showToast(translate("Successful")); + } else { + setState(() { + errorText = translate(res.toString()); + isInProgress = false; + }); + } + } + + return CustomAlertDialog( + title: Text(translate("Deploy")), + content: Column( + mainAxisSize: MainAxisSize.min, + children: [ + TextField( + controller: tokenController, + decoration: InputDecoration(labelText: translate("API Token")), + obscureText: true, + enableSuggestions: false, + autocorrect: false, + autofocus: true, + ).workaroundFreezeLinuxMint(), + TextField( + controller: idController, + decoration: + InputDecoration(labelText: translate("Custom ID (optional)")), + ).workaroundFreezeLinuxMint(), + if (errorText.isNotEmpty) + Align( + alignment: Alignment.centerLeft, + child: SelectableText( + errorText, + style: TextStyle( + color: Theme.of(context).colorScheme.error, + fontSize: 12, + ), + ).paddingOnly(top: 8), + ), + if (isInProgress) const LinearProgressIndicator().paddingOnly(top: 8), + ], + ), + actions: [ + dialogButton("Cancel", + onPressed: isInProgress ? null : close, isOutline: true), + dialogButton("OK", onPressed: isInProgress ? null : submit), + ], + onSubmit: submit, + onCancel: isInProgress ? null : close, + ); + }, tag: _deployDialogTag); +} diff --git a/flutter/lib/web/bridge.dart b/flutter/lib/web/bridge.dart index 54e6a9a9b..2df0b3426 100644 --- a/flutter/lib/web/bridge.dart +++ b/flutter/lib/web/bridge.dart @@ -2034,7 +2034,14 @@ class RustdeskImpl { } String mainResolveAvatarUrl({required String avatar, dynamic hint}) { - return js.context.callMethod('getByName', ['resolve_avatar_url', avatar])?.toString() ?? avatar; + return js.context.callMethod( + 'getByName', ['resolve_avatar_url', avatar])?.toString() ?? + avatar; + } + + Future mainDeployDevice( + {required String token, required String id, dynamic hint}) { + throw UnimplementedError("mainDeployDevice"); } void dispose() {} diff --git a/src/core_main.rs b/src/core_main.rs index 225779a0f..2bd6c11b5 100644 --- a/src/core_main.rs +++ b/src/core_main.rs @@ -644,6 +644,8 @@ pub fn core_main() -> Option> { } else if args[0] == "--deploy" { if config::Config::no_register_device() { println!("Cannot deploy an unregistrable device!"); + } else if config::is_outgoing_only() { + println!("Cannot deploy Outgoing-only clients."); } else if crate::platform::is_installed() && is_root() { let max = args.len() - 1; let pos = args.iter().position(|x| x == "--token").unwrap_or(max); @@ -661,72 +663,28 @@ pub fn core_main() -> Option> { } }; let new_id = get_value("--id"); - let local_id = crate::ipc::get_id(); - let id_to_deploy = new_id.clone().unwrap_or_else(|| local_id.clone()); - let uuid = crate::encode64(hbb_common::get_uuid()); - let pk = crate::encode64( - hbb_common::config::Config::get_key_pair().1, - ); - let body = serde_json::json!({ - "id": id_to_deploy, - "uuid": uuid, - "pk": pk, - }); - let header = "Authorization: Bearer ".to_owned() + &token; - let url = crate::ui_interface::get_api_server() + "/api/devices/deploy"; - match crate::post_request_sync(url, body.to_string(), &header) { - Err(err) => { - println!("Request failed: {}", err); - std::process::exit(1); + match crate::ui_interface::deploy_device(token, new_id) { + crate::ui_interface::DeployResult::Ok => { + println!("Device deployed."); } - Ok(text) => { - let parsed: serde_json::Value = - serde_json::from_str(&text).unwrap_or(serde_json::Value::Null); - let result = parsed["result"].as_str().unwrap_or(""); - match result { - "OK" => { - if let Some(ref new_id) = new_id { - if *new_id != local_id { - if let Err(err) = - crate::ipc::set_config("id", new_id.clone()) - { - println!( - "Failed to persist deployed id locally: {}", - err - ); - std::process::exit(1); - } - } - } - if let Err(err) = crate::ipc::notify_deployed() { - log::warn!("Failed to notify deployed state: {}", err); - } - println!("Device deployed."); - } - "NOT_ENABLED" => { - println!("Server does not require deployment."); - std::process::exit(3); - } - "INVALID_INPUT" => { - println!("Invalid input."); - std::process::exit(5); - } - "ID_TAKEN" => { - println!( - "Id `{}` is already used by another machine on the server.", - id_to_deploy - ); - std::process::exit(6); - } - _ => { - if text.is_empty() { - println!("Unknown response."); - } else { - println!("{}", text); - } - std::process::exit(1); - } - } + crate::ui_interface::DeployResult::NotEnabled => { + println!("Server does not require deployment."); + std::process::exit(3); + } + crate::ui_interface::DeployResult::InvalidInput => { + println!("Invalid input."); + std::process::exit(5); + } + crate::ui_interface::DeployResult::IdTaken(id) => { + println!( + "Id `{}` is already used by another machine on the server.", + id + ); + std::process::exit(6); + } + crate::ui_interface::DeployResult::Error(err) => { + println!("{}", err); + std::process::exit(1); } } } else { diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs index 13a97cb43..09156cfd6 100644 --- a/src/flutter_ffi.rs +++ b/src/flutter_ffi.rs @@ -1153,6 +1153,22 @@ pub fn main_get_api_server() -> String { get_api_server() } +pub fn main_deploy_device(token: String, id: String) -> String { + #[cfg(target_os = "android")] + { + let new_id = match id.trim() { + "" => None, + id => Some(id.to_owned()), + }; + ui_interface::deploy_device(token, new_id).message() + } + #[cfg(not(target_os = "android"))] + { + let _ = (token, id); + "Deployment is not supported on this platform.".to_owned() + } +} + pub fn main_resolve_avatar_url(avatar: String) -> SyncReturn { SyncReturn(resolve_avatar_url(avatar)) } @@ -2116,6 +2132,7 @@ pub fn main_start_service() { #[cfg(target_os = "android")] { config::Config::set_option("stop-service".into(), "".into()); + crate::rendezvous_mediator::reset_needs_deploy_notification(); crate::rendezvous_mediator::RendezvousMediator::restart(); } } @@ -3055,6 +3072,7 @@ pub mod server_side { pub unsafe extern "system" fn Java_ffi_FFI_startService(_env: JNIEnv, _class: JClass) { log::debug!("startService from jvm"); config::Config::set_option("stop-service".into(), "".into()); + crate::rendezvous_mediator::reset_needs_deploy_notification(); crate::rendezvous_mediator::RendezvousMediator::restart(); } diff --git a/src/lang/ar.rs b/src/lang/ar.rs index e13404802..2e3042718 100644 --- a/src/lang/ar.rs +++ b/src/lang/ar.rs @@ -745,5 +745,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("preset-password-in-use-tip", "كلمة المرور المحددة مسبقًا قيد الاستخدام"), ("Enable privacy mode", ""), ("allow-remote-toolbar-docking-any-edge", ""), + ("API Token", ""), + ("Deploy", ""), + ("Custom ID (optional)", ""), + ("server_requires_deployment_tip", ""), + ("The server does not require explicit deployment.", ""), + ("Unknown response.", ""), ].iter().cloned().collect(); } diff --git a/src/lang/be.rs b/src/lang/be.rs index 9f6b69c8b..e212d03df 100644 --- a/src/lang/be.rs +++ b/src/lang/be.rs @@ -745,5 +745,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("preset-password-in-use-tip", "Пададзены пароль цяпер выкарыстоўваецца"), ("Enable privacy mode", ""), ("allow-remote-toolbar-docking-any-edge", ""), + ("API Token", ""), + ("Deploy", ""), + ("Custom ID (optional)", ""), + ("server_requires_deployment_tip", ""), + ("The server does not require explicit deployment.", ""), + ("Unknown response.", ""), ].iter().cloned().collect(); } diff --git a/src/lang/bg.rs b/src/lang/bg.rs index 0aa61b1eb..2eb70ea2d 100644 --- a/src/lang/bg.rs +++ b/src/lang/bg.rs @@ -745,5 +745,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("preset-password-in-use-tip", ""), ("Enable privacy mode", ""), ("allow-remote-toolbar-docking-any-edge", ""), + ("API Token", ""), + ("Deploy", ""), + ("Custom ID (optional)", ""), + ("server_requires_deployment_tip", ""), + ("The server does not require explicit deployment.", ""), + ("Unknown response.", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ca.rs b/src/lang/ca.rs index 2f706cc89..ea61d93f6 100644 --- a/src/lang/ca.rs +++ b/src/lang/ca.rs @@ -745,5 +745,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("preset-password-in-use-tip", ""), ("Enable privacy mode", ""), ("allow-remote-toolbar-docking-any-edge", ""), + ("API Token", ""), + ("Deploy", ""), + ("Custom ID (optional)", ""), + ("server_requires_deployment_tip", ""), + ("The server does not require explicit deployment.", ""), + ("Unknown response.", ""), ].iter().cloned().collect(); } diff --git a/src/lang/cn.rs b/src/lang/cn.rs index a90e5e194..2be989aff 100644 --- a/src/lang/cn.rs +++ b/src/lang/cn.rs @@ -745,5 +745,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("preset-password-in-use-tip", "当前使用预设密码"), ("Enable privacy mode", "允许隐私模式"), ("allow-remote-toolbar-docking-any-edge", ""), + ("API Token", "API 令牌"), + ("Deploy", "部署"), + ("Custom ID (optional)", "自定义 ID(可选)"), + ("server_requires_deployment_tip", "服务器要求显式部署此设备。是否立即部署?"), + ("The server does not require explicit deployment.", "服务器不需要显式部署。"), + ("Unknown response.", "未知响应。"), ].iter().cloned().collect(); } diff --git a/src/lang/cs.rs b/src/lang/cs.rs index 7f50d826f..b7951db29 100644 --- a/src/lang/cs.rs +++ b/src/lang/cs.rs @@ -745,5 +745,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("preset-password-in-use-tip", ""), ("Enable privacy mode", ""), ("allow-remote-toolbar-docking-any-edge", ""), + ("API Token", ""), + ("Deploy", ""), + ("Custom ID (optional)", ""), + ("server_requires_deployment_tip", ""), + ("The server does not require explicit deployment.", ""), + ("Unknown response.", ""), ].iter().cloned().collect(); } diff --git a/src/lang/da.rs b/src/lang/da.rs index c9d3b4eb0..db9ab0ae3 100644 --- a/src/lang/da.rs +++ b/src/lang/da.rs @@ -745,5 +745,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("preset-password-in-use-tip", ""), ("Enable privacy mode", ""), ("allow-remote-toolbar-docking-any-edge", ""), + ("API Token", ""), + ("Deploy", ""), + ("Custom ID (optional)", ""), + ("server_requires_deployment_tip", ""), + ("The server does not require explicit deployment.", ""), + ("Unknown response.", ""), ].iter().cloned().collect(); } diff --git a/src/lang/de.rs b/src/lang/de.rs index c989bfaa9..d8ba5e95a 100644 --- a/src/lang/de.rs +++ b/src/lang/de.rs @@ -745,5 +745,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("preset-password-in-use-tip", "Das voreingestellte Passwort wird derzeit verwendet."), ("Enable privacy mode", "Datenschutzmodus aktivieren"), ("allow-remote-toolbar-docking-any-edge", "Andocken der Remote-Symbolleiste an jeden Fensterrand zulassen"), + ("API Token", ""), + ("Deploy", ""), + ("Custom ID (optional)", ""), + ("server_requires_deployment_tip", ""), + ("The server does not require explicit deployment.", ""), + ("Unknown response.", ""), ].iter().cloned().collect(); } diff --git a/src/lang/el.rs b/src/lang/el.rs index d03bb069c..d45f77306 100644 --- a/src/lang/el.rs +++ b/src/lang/el.rs @@ -745,5 +745,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("preset-password-in-use-tip", ""), ("Enable privacy mode", ""), ("allow-remote-toolbar-docking-any-edge", ""), + ("API Token", ""), + ("Deploy", ""), + ("Custom ID (optional)", ""), + ("server_requires_deployment_tip", ""), + ("The server does not require explicit deployment.", ""), + ("Unknown response.", ""), ].iter().cloned().collect(); } diff --git a/src/lang/en.rs b/src/lang/en.rs index 595169b8a..49a8a6e06 100644 --- a/src/lang/en.rs +++ b/src/lang/en.rs @@ -275,5 +275,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("password-hidden-tip", "Permanent password is set (hidden)."), ("preset-password-in-use-tip", "Preset password is currently in use."), ("allow-remote-toolbar-docking-any-edge", "Allow docking remote toolbar to any window edge"), + ("server_requires_deployment_tip", "The server requires this device to be deployed explicitly. Deploy now?"), ].iter().cloned().collect(); } diff --git a/src/lang/eo.rs b/src/lang/eo.rs index 131a85fbf..e61a06c00 100644 --- a/src/lang/eo.rs +++ b/src/lang/eo.rs @@ -745,5 +745,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("preset-password-in-use-tip", ""), ("Enable privacy mode", ""), ("allow-remote-toolbar-docking-any-edge", ""), + ("API Token", ""), + ("Deploy", ""), + ("Custom ID (optional)", ""), + ("server_requires_deployment_tip", ""), + ("The server does not require explicit deployment.", ""), + ("Unknown response.", ""), ].iter().cloned().collect(); } diff --git a/src/lang/es.rs b/src/lang/es.rs index 5e73b58a8..a965f4690 100644 --- a/src/lang/es.rs +++ b/src/lang/es.rs @@ -745,5 +745,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("preset-password-in-use-tip", "Se está usando la contraseña predeterminada."), ("Enable privacy mode", "Habilitar modo privado"), ("allow-remote-toolbar-docking-any-edge", ""), + ("API Token", ""), + ("Deploy", ""), + ("Custom ID (optional)", ""), + ("server_requires_deployment_tip", ""), + ("The server does not require explicit deployment.", ""), + ("Unknown response.", ""), ].iter().cloned().collect(); } diff --git a/src/lang/et.rs b/src/lang/et.rs index 76abc8563..72d515043 100644 --- a/src/lang/et.rs +++ b/src/lang/et.rs @@ -745,5 +745,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("preset-password-in-use-tip", ""), ("Enable privacy mode", ""), ("allow-remote-toolbar-docking-any-edge", ""), + ("API Token", ""), + ("Deploy", ""), + ("Custom ID (optional)", ""), + ("server_requires_deployment_tip", ""), + ("The server does not require explicit deployment.", ""), + ("Unknown response.", ""), ].iter().cloned().collect(); } diff --git a/src/lang/eu.rs b/src/lang/eu.rs index 9e19d1fea..50a8903dd 100644 --- a/src/lang/eu.rs +++ b/src/lang/eu.rs @@ -745,5 +745,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("preset-password-in-use-tip", ""), ("Enable privacy mode", ""), ("allow-remote-toolbar-docking-any-edge", ""), + ("API Token", ""), + ("Deploy", ""), + ("Custom ID (optional)", ""), + ("server_requires_deployment_tip", ""), + ("The server does not require explicit deployment.", ""), + ("Unknown response.", ""), ].iter().cloned().collect(); } diff --git a/src/lang/fa.rs b/src/lang/fa.rs index 9e01b7eb0..a34304362 100644 --- a/src/lang/fa.rs +++ b/src/lang/fa.rs @@ -745,5 +745,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("preset-password-in-use-tip", ""), ("Enable privacy mode", ""), ("allow-remote-toolbar-docking-any-edge", ""), + ("API Token", ""), + ("Deploy", ""), + ("Custom ID (optional)", ""), + ("server_requires_deployment_tip", ""), + ("The server does not require explicit deployment.", ""), + ("Unknown response.", ""), ].iter().cloned().collect(); } diff --git a/src/lang/fi.rs b/src/lang/fi.rs index f8283685b..fad051846 100644 --- a/src/lang/fi.rs +++ b/src/lang/fi.rs @@ -745,5 +745,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("preset-password-in-use-tip", ""), ("Enable privacy mode", ""), ("allow-remote-toolbar-docking-any-edge", ""), + ("API Token", ""), + ("Deploy", ""), + ("Custom ID (optional)", ""), + ("server_requires_deployment_tip", ""), + ("The server does not require explicit deployment.", ""), + ("Unknown response.", ""), ].iter().cloned().collect(); } diff --git a/src/lang/fr.rs b/src/lang/fr.rs index 82a0e87d8..6a16e7928 100644 --- a/src/lang/fr.rs +++ b/src/lang/fr.rs @@ -745,5 +745,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("preset-password-in-use-tip", "Le mot de passe prédéfini est actuellement utilisé."), ("Enable privacy mode", "Activer le mode de confidentialité"), ("allow-remote-toolbar-docking-any-edge", "Autoriser l’ancrage de la barre d’outils à distance sur n’importe quel bord de la fenêtre"), + ("API Token", ""), + ("Deploy", ""), + ("Custom ID (optional)", ""), + ("server_requires_deployment_tip", ""), + ("The server does not require explicit deployment.", ""), + ("Unknown response.", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ge.rs b/src/lang/ge.rs index 2fc8f282d..f0b347b41 100644 --- a/src/lang/ge.rs +++ b/src/lang/ge.rs @@ -745,5 +745,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("preset-password-in-use-tip", ""), ("Enable privacy mode", ""), ("allow-remote-toolbar-docking-any-edge", ""), + ("API Token", ""), + ("Deploy", ""), + ("Custom ID (optional)", ""), + ("server_requires_deployment_tip", ""), + ("The server does not require explicit deployment.", ""), + ("Unknown response.", ""), ].iter().cloned().collect(); } diff --git a/src/lang/gu.rs b/src/lang/gu.rs index ac0a588a8..e07fd02cd 100644 --- a/src/lang/gu.rs +++ b/src/lang/gu.rs @@ -745,5 +745,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("preset-password-in-use-tip", "પ્રીસેટ પાસવર્ડ વપરાશમાં છે."), ("Enable privacy mode", ""), ("allow-remote-toolbar-docking-any-edge", ""), + ("API Token", ""), + ("Deploy", ""), + ("Custom ID (optional)", ""), + ("server_requires_deployment_tip", ""), + ("The server does not require explicit deployment.", ""), + ("Unknown response.", ""), ].iter().cloned().collect(); } diff --git a/src/lang/he.rs b/src/lang/he.rs index 44b940784..9d4a1a224 100644 --- a/src/lang/he.rs +++ b/src/lang/he.rs @@ -745,5 +745,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("preset-password-in-use-tip", ""), ("Enable privacy mode", ""), ("allow-remote-toolbar-docking-any-edge", ""), + ("API Token", ""), + ("Deploy", ""), + ("Custom ID (optional)", ""), + ("server_requires_deployment_tip", ""), + ("The server does not require explicit deployment.", ""), + ("Unknown response.", ""), ].iter().cloned().collect(); } diff --git a/src/lang/hi.rs b/src/lang/hi.rs index 904d43118..4e02108d2 100644 --- a/src/lang/hi.rs +++ b/src/lang/hi.rs @@ -745,5 +745,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("preset-password-in-use-tip", "पूर्व-निर्धारित पासवर्ड उपयोग में है।"), ("Enable privacy mode", ""), ("allow-remote-toolbar-docking-any-edge", ""), + ("API Token", ""), + ("Deploy", ""), + ("Custom ID (optional)", ""), + ("server_requires_deployment_tip", ""), + ("The server does not require explicit deployment.", ""), + ("Unknown response.", ""), ].iter().cloned().collect(); } diff --git a/src/lang/hr.rs b/src/lang/hr.rs index 0593ff6b7..daea88bd0 100644 --- a/src/lang/hr.rs +++ b/src/lang/hr.rs @@ -745,5 +745,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("preset-password-in-use-tip", ""), ("Enable privacy mode", ""), ("allow-remote-toolbar-docking-any-edge", ""), + ("API Token", ""), + ("Deploy", ""), + ("Custom ID (optional)", ""), + ("server_requires_deployment_tip", ""), + ("The server does not require explicit deployment.", ""), + ("Unknown response.", ""), ].iter().cloned().collect(); } diff --git a/src/lang/hu.rs b/src/lang/hu.rs index 3eb16890f..5ea94dc8f 100644 --- a/src/lang/hu.rs +++ b/src/lang/hu.rs @@ -745,5 +745,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("preset-password-in-use-tip", "Jelenleg az alapértelmezett jelszót használja."), ("Enable privacy mode", "Adatvédelmi mód aktiválása"), ("allow-remote-toolbar-docking-any-edge", ""), + ("API Token", ""), + ("Deploy", ""), + ("Custom ID (optional)", ""), + ("server_requires_deployment_tip", ""), + ("The server does not require explicit deployment.", ""), + ("Unknown response.", ""), ].iter().cloned().collect(); } diff --git a/src/lang/id.rs b/src/lang/id.rs index bcda0a3a8..0f38f399a 100644 --- a/src/lang/id.rs +++ b/src/lang/id.rs @@ -745,5 +745,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("preset-password-in-use-tip", ""), ("Enable privacy mode", ""), ("allow-remote-toolbar-docking-any-edge", ""), + ("API Token", ""), + ("Deploy", ""), + ("Custom ID (optional)", ""), + ("server_requires_deployment_tip", ""), + ("The server does not require explicit deployment.", ""), + ("Unknown response.", ""), ].iter().cloned().collect(); } diff --git a/src/lang/it.rs b/src/lang/it.rs index c2ac6bac6..88411bc6e 100644 --- a/src/lang/it.rs +++ b/src/lang/it.rs @@ -745,5 +745,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("preset-password-in-use-tip", "È attualmente in uso la password preimpostata."), ("Enable privacy mode", "Abilita modalità privacy"), ("allow-remote-toolbar-docking-any-edge", "Consenti ancoraggio barra strumenti remota a qualsiasi bordo della finestra"), + ("API Token", ""), + ("Deploy", ""), + ("Custom ID (optional)", ""), + ("server_requires_deployment_tip", ""), + ("The server does not require explicit deployment.", ""), + ("Unknown response.", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ja.rs b/src/lang/ja.rs index 2879e86bf..5151c52b0 100644 --- a/src/lang/ja.rs +++ b/src/lang/ja.rs @@ -745,5 +745,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("preset-password-in-use-tip", "プリセットパスワードが現在使用されています"), ("Enable privacy mode", ""), ("allow-remote-toolbar-docking-any-edge", ""), + ("API Token", ""), + ("Deploy", ""), + ("Custom ID (optional)", ""), + ("server_requires_deployment_tip", ""), + ("The server does not require explicit deployment.", ""), + ("Unknown response.", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ko.rs b/src/lang/ko.rs index 1b64ed551..6e76010c6 100644 --- a/src/lang/ko.rs +++ b/src/lang/ko.rs @@ -745,5 +745,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("preset-password-in-use-tip", "현재 사전 설정된 비밀번호가 사용 중입니다."), ("Enable privacy mode", "개인정보 보호 모드 사용함"), ("allow-remote-toolbar-docking-any-edge", "원격 도구 모음을 창 가장자리에 도킹 허용"), + ("API Token", ""), + ("Deploy", ""), + ("Custom ID (optional)", ""), + ("server_requires_deployment_tip", ""), + ("The server does not require explicit deployment.", ""), + ("Unknown response.", ""), ].iter().cloned().collect(); } diff --git a/src/lang/kz.rs b/src/lang/kz.rs index 4476fadc7..3d409f59b 100644 --- a/src/lang/kz.rs +++ b/src/lang/kz.rs @@ -745,5 +745,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("preset-password-in-use-tip", ""), ("Enable privacy mode", ""), ("allow-remote-toolbar-docking-any-edge", ""), + ("API Token", ""), + ("Deploy", ""), + ("Custom ID (optional)", ""), + ("server_requires_deployment_tip", ""), + ("The server does not require explicit deployment.", ""), + ("Unknown response.", ""), ].iter().cloned().collect(); } diff --git a/src/lang/lt.rs b/src/lang/lt.rs index 47ace51ae..f5344c579 100644 --- a/src/lang/lt.rs +++ b/src/lang/lt.rs @@ -745,5 +745,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("preset-password-in-use-tip", ""), ("Enable privacy mode", ""), ("allow-remote-toolbar-docking-any-edge", ""), + ("API Token", ""), + ("Deploy", ""), + ("Custom ID (optional)", ""), + ("server_requires_deployment_tip", ""), + ("The server does not require explicit deployment.", ""), + ("Unknown response.", ""), ].iter().cloned().collect(); } diff --git a/src/lang/lv.rs b/src/lang/lv.rs index c8c65cdba..ad7feb5a3 100644 --- a/src/lang/lv.rs +++ b/src/lang/lv.rs @@ -745,5 +745,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("preset-password-in-use-tip", "Iepriekš iestatītā parole pašlaik tiek izmantota."), ("Enable privacy mode", "Iespējot privātuma režīmu"), ("allow-remote-toolbar-docking-any-edge", "Atļaut attālās rīkjoslas piestiprināšanu pie jebkuras loga malas"), + ("API Token", ""), + ("Deploy", ""), + ("Custom ID (optional)", ""), + ("server_requires_deployment_tip", ""), + ("The server does not require explicit deployment.", ""), + ("Unknown response.", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ml.rs b/src/lang/ml.rs index 4dcfe9e74..84a8fe389 100644 --- a/src/lang/ml.rs +++ b/src/lang/ml.rs @@ -745,5 +745,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("preset-password-in-use-tip", "പ്രീസെറ്റ് പാസ്‌വേഡ് ഉപയോഗത്തിലാണ്."), ("Enable privacy mode", ""), ("allow-remote-toolbar-docking-any-edge", ""), + ("API Token", ""), + ("Deploy", ""), + ("Custom ID (optional)", ""), + ("server_requires_deployment_tip", ""), + ("The server does not require explicit deployment.", ""), + ("Unknown response.", ""), ].iter().cloned().collect(); } diff --git a/src/lang/nb.rs b/src/lang/nb.rs index 9325dfa1f..854ca2369 100644 --- a/src/lang/nb.rs +++ b/src/lang/nb.rs @@ -745,5 +745,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("preset-password-in-use-tip", ""), ("Enable privacy mode", ""), ("allow-remote-toolbar-docking-any-edge", ""), + ("API Token", ""), + ("Deploy", ""), + ("Custom ID (optional)", ""), + ("server_requires_deployment_tip", ""), + ("The server does not require explicit deployment.", ""), + ("Unknown response.", ""), ].iter().cloned().collect(); } diff --git a/src/lang/nl.rs b/src/lang/nl.rs index d905161da..94c3fd611 100644 --- a/src/lang/nl.rs +++ b/src/lang/nl.rs @@ -745,5 +745,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("preset-password-in-use-tip", "Het basis wachtwoord is momenteel in gebruik."), ("Enable privacy mode", "Privacymodus inschakelen"), ("allow-remote-toolbar-docking-any-edge", "Sta toe om de werkbalk-op-afstand aan de rand van het venster te plaatsen"), + ("API Token", ""), + ("Deploy", ""), + ("Custom ID (optional)", ""), + ("server_requires_deployment_tip", ""), + ("The server does not require explicit deployment.", ""), + ("Unknown response.", ""), ].iter().cloned().collect(); } diff --git a/src/lang/pl.rs b/src/lang/pl.rs index 75f13d035..2b325edca 100644 --- a/src/lang/pl.rs +++ b/src/lang/pl.rs @@ -745,5 +745,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("preset-password-in-use-tip", "Obecnie używane jest hasło domyślne."), ("Enable privacy mode", "Włącz tryb prywatny"), ("allow-remote-toolbar-docking-any-edge", "Zezwalaj na dokowanie zdalnego paska narzędzi do dowolnej krawędzi"), + ("API Token", ""), + ("Deploy", ""), + ("Custom ID (optional)", ""), + ("server_requires_deployment_tip", ""), + ("The server does not require explicit deployment.", ""), + ("Unknown response.", ""), ].iter().cloned().collect(); } diff --git a/src/lang/pt_PT.rs b/src/lang/pt_PT.rs index 4138b46e4..a6c80b65c 100644 --- a/src/lang/pt_PT.rs +++ b/src/lang/pt_PT.rs @@ -745,5 +745,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("preset-password-in-use-tip", ""), ("Enable privacy mode", ""), ("allow-remote-toolbar-docking-any-edge", ""), + ("API Token", ""), + ("Deploy", ""), + ("Custom ID (optional)", ""), + ("server_requires_deployment_tip", ""), + ("The server does not require explicit deployment.", ""), + ("Unknown response.", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ptbr.rs b/src/lang/ptbr.rs index a7960ad9a..823138d44 100644 --- a/src/lang/ptbr.rs +++ b/src/lang/ptbr.rs @@ -745,5 +745,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("preset-password-in-use-tip", "A senha predefinida está sendo usada."), ("Enable privacy mode", "Habilitar modo de privacidade"), ("allow-remote-toolbar-docking-any-edge", "Permitir fixar a barra de ferramentas remota em qualquer borda da janela"), + ("API Token", ""), + ("Deploy", ""), + ("Custom ID (optional)", ""), + ("server_requires_deployment_tip", ""), + ("The server does not require explicit deployment.", ""), + ("Unknown response.", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ro.rs b/src/lang/ro.rs index bde4a4201..939aa6c53 100644 --- a/src/lang/ro.rs +++ b/src/lang/ro.rs @@ -745,5 +745,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("preset-password-in-use-tip", "Se folosește o parolă prestabilită. Se recomandă setarea unei parole personalizate pentru securitate sporită."), ("Enable privacy mode", ""), ("allow-remote-toolbar-docking-any-edge", ""), + ("API Token", ""), + ("Deploy", ""), + ("Custom ID (optional)", ""), + ("server_requires_deployment_tip", ""), + ("The server does not require explicit deployment.", ""), + ("Unknown response.", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ru.rs b/src/lang/ru.rs index 8bfa7331c..f0d6fdf9d 100644 --- a/src/lang/ru.rs +++ b/src/lang/ru.rs @@ -745,5 +745,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("preset-password-in-use-tip", "Установленный пароль сейчас используется."), ("Enable privacy mode", "Использовать режим конфиденциальности"), ("allow-remote-toolbar-docking-any-edge", "Разрешать прикрепление удалённой панели инструментов к любому краю окна"), + ("API Token", ""), + ("Deploy", ""), + ("Custom ID (optional)", ""), + ("server_requires_deployment_tip", ""), + ("The server does not require explicit deployment.", ""), + ("Unknown response.", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sc.rs b/src/lang/sc.rs index 06919b752..e3973d906 100644 --- a/src/lang/sc.rs +++ b/src/lang/sc.rs @@ -745,5 +745,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("preset-password-in-use-tip", ""), ("Enable privacy mode", ""), ("allow-remote-toolbar-docking-any-edge", ""), + ("API Token", ""), + ("Deploy", ""), + ("Custom ID (optional)", ""), + ("server_requires_deployment_tip", ""), + ("The server does not require explicit deployment.", ""), + ("Unknown response.", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sk.rs b/src/lang/sk.rs index 963f48728..a8eedf618 100644 --- a/src/lang/sk.rs +++ b/src/lang/sk.rs @@ -745,5 +745,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("preset-password-in-use-tip", ""), ("Enable privacy mode", ""), ("allow-remote-toolbar-docking-any-edge", ""), + ("API Token", ""), + ("Deploy", ""), + ("Custom ID (optional)", ""), + ("server_requires_deployment_tip", ""), + ("The server does not require explicit deployment.", ""), + ("Unknown response.", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sl.rs b/src/lang/sl.rs index 0f85af0c3..c5a07ca2f 100755 --- a/src/lang/sl.rs +++ b/src/lang/sl.rs @@ -745,5 +745,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("preset-password-in-use-tip", ""), ("Enable privacy mode", ""), ("allow-remote-toolbar-docking-any-edge", ""), + ("API Token", ""), + ("Deploy", ""), + ("Custom ID (optional)", ""), + ("server_requires_deployment_tip", ""), + ("The server does not require explicit deployment.", ""), + ("Unknown response.", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sq.rs b/src/lang/sq.rs index 7c965cd45..6340d6366 100644 --- a/src/lang/sq.rs +++ b/src/lang/sq.rs @@ -745,5 +745,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("preset-password-in-use-tip", ""), ("Enable privacy mode", ""), ("allow-remote-toolbar-docking-any-edge", ""), + ("API Token", ""), + ("Deploy", ""), + ("Custom ID (optional)", ""), + ("server_requires_deployment_tip", ""), + ("The server does not require explicit deployment.", ""), + ("Unknown response.", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sr.rs b/src/lang/sr.rs index fc33e4671..2d0e4d9c7 100644 --- a/src/lang/sr.rs +++ b/src/lang/sr.rs @@ -745,5 +745,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("preset-password-in-use-tip", ""), ("Enable privacy mode", ""), ("allow-remote-toolbar-docking-any-edge", ""), + ("API Token", ""), + ("Deploy", ""), + ("Custom ID (optional)", ""), + ("server_requires_deployment_tip", ""), + ("The server does not require explicit deployment.", ""), + ("Unknown response.", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sv.rs b/src/lang/sv.rs index 664dc4745..565c026ef 100644 --- a/src/lang/sv.rs +++ b/src/lang/sv.rs @@ -745,5 +745,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("preset-password-in-use-tip", ""), ("Enable privacy mode", ""), ("allow-remote-toolbar-docking-any-edge", ""), + ("API Token", ""), + ("Deploy", ""), + ("Custom ID (optional)", ""), + ("server_requires_deployment_tip", ""), + ("The server does not require explicit deployment.", ""), + ("Unknown response.", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ta.rs b/src/lang/ta.rs index 93aeb6462..ecc3abdc6 100644 --- a/src/lang/ta.rs +++ b/src/lang/ta.rs @@ -745,5 +745,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("preset-password-in-use-tip", ""), ("Enable privacy mode", ""), ("allow-remote-toolbar-docking-any-edge", ""), + ("API Token", ""), + ("Deploy", ""), + ("Custom ID (optional)", ""), + ("server_requires_deployment_tip", ""), + ("The server does not require explicit deployment.", ""), + ("Unknown response.", ""), ].iter().cloned().collect(); } diff --git a/src/lang/template.rs b/src/lang/template.rs index 33b359c5e..fcf940e00 100644 --- a/src/lang/template.rs +++ b/src/lang/template.rs @@ -745,5 +745,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("preset-password-in-use-tip", ""), ("Enable privacy mode", ""), ("allow-remote-toolbar-docking-any-edge", ""), + ("API Token", ""), + ("Deploy", ""), + ("Custom ID (optional)", ""), + ("server_requires_deployment_tip", ""), + ("The server does not require explicit deployment.", ""), + ("Unknown response.", ""), ].iter().cloned().collect(); } diff --git a/src/lang/th.rs b/src/lang/th.rs index a24c60bf6..0deb97686 100644 --- a/src/lang/th.rs +++ b/src/lang/th.rs @@ -745,5 +745,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("preset-password-in-use-tip", ""), ("Enable privacy mode", ""), ("allow-remote-toolbar-docking-any-edge", ""), + ("API Token", ""), + ("Deploy", ""), + ("Custom ID (optional)", ""), + ("server_requires_deployment_tip", ""), + ("The server does not require explicit deployment.", ""), + ("Unknown response.", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tr.rs b/src/lang/tr.rs index a1e2652d1..0860f2fb3 100644 --- a/src/lang/tr.rs +++ b/src/lang/tr.rs @@ -745,5 +745,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("preset-password-in-use-tip", "Önceden ayarlanmış parola kullanılıyor"), ("Enable privacy mode", "Gizlilik modunu etkinleştir"), ("allow-remote-toolbar-docking-any-edge", "Uzak araç çubuğunun pencerenin herhangi bir kenarına sabitlenmesine izin ver"), + ("API Token", ""), + ("Deploy", ""), + ("Custom ID (optional)", ""), + ("server_requires_deployment_tip", ""), + ("The server does not require explicit deployment.", ""), + ("Unknown response.", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tw.rs b/src/lang/tw.rs index 6df025303..ce1a6e3f1 100644 --- a/src/lang/tw.rs +++ b/src/lang/tw.rs @@ -745,5 +745,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("preset-password-in-use-tip", "目前正在使用預設密碼"), ("Enable privacy mode", ""), ("allow-remote-toolbar-docking-any-edge", ""), + ("API Token", ""), + ("Deploy", ""), + ("Custom ID (optional)", ""), + ("server_requires_deployment_tip", ""), + ("The server does not require explicit deployment.", ""), + ("Unknown response.", ""), ].iter().cloned().collect(); } diff --git a/src/lang/uk.rs b/src/lang/uk.rs index 7107bc261..6985085ae 100644 --- a/src/lang/uk.rs +++ b/src/lang/uk.rs @@ -745,5 +745,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("preset-password-in-use-tip", ""), ("Enable privacy mode", ""), ("allow-remote-toolbar-docking-any-edge", ""), + ("API Token", ""), + ("Deploy", ""), + ("Custom ID (optional)", ""), + ("server_requires_deployment_tip", ""), + ("The server does not require explicit deployment.", ""), + ("Unknown response.", ""), ].iter().cloned().collect(); } diff --git a/src/lang/vi.rs b/src/lang/vi.rs index 0910025ed..fe71018ba 100644 --- a/src/lang/vi.rs +++ b/src/lang/vi.rs @@ -745,5 +745,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("preset-password-in-use-tip", ""), ("Enable privacy mode", ""), ("allow-remote-toolbar-docking-any-edge", ""), + ("API Token", ""), + ("Deploy", ""), + ("Custom ID (optional)", ""), + ("server_requires_deployment_tip", ""), + ("The server does not require explicit deployment.", ""), + ("Unknown response.", ""), ].iter().cloned().collect(); } diff --git a/src/rendezvous_mediator.rs b/src/rendezvous_mediator.rs index 89d7fa01e..98e75eaf3 100644 --- a/src/rendezvous_mediator.rs +++ b/src/rendezvous_mediator.rs @@ -42,6 +42,8 @@ static SHOULD_EXIT: AtomicBool = AtomicBool::new(false); static MANUAL_RESTARTED: AtomicBool = AtomicBool::new(false); static SENT_REGISTER_PK: AtomicBool = AtomicBool::new(false); pub(crate) static NEEDS_DEPLOY: AtomicBool = AtomicBool::new(false); +#[cfg(target_os = "android")] +static NOTIFIED_NEEDS_DEPLOY: AtomicBool = AtomicBool::new(false); // register_pk retry interval (ms) when device is awaiting deployment const DEPLOY_RETRY_INTERVAL: i64 = 30_000; lazy_static::lazy_static! { @@ -66,6 +68,26 @@ async fn deploy_register_throttled() -> bool { .unwrap_or(false) } +#[cfg(target_os = "android")] +fn notify_android_needs_deploy() { + if NOTIFIED_NEEDS_DEPLOY.load(Ordering::SeqCst) { + return; + } + let event = serde_json::json!({ "name": "android_needs_deploy" }).to_string(); + if matches!( + crate::flutter::push_global_event(crate::flutter::APP_TYPE_MAIN, event), + Some(true) + ) { + NOTIFIED_NEEDS_DEPLOY.store(true, Ordering::SeqCst); + } +} + +#[cfg(target_os = "android")] +pub(crate) fn reset_needs_deploy_notification() { + NEEDS_DEPLOY.store(false, Ordering::SeqCst); + NOTIFIED_NEEDS_DEPLOY.store(false, Ordering::SeqCst); +} + #[derive(Clone)] pub struct RendezvousMediator { addr: TargetAddr<'static>, @@ -117,6 +139,7 @@ impl RendezvousMediator { crate::platform::linux_desktop_manager::start_xdesktop(); } scrap::codec::test_av1(); + *LAST_NOT_DEPLOYED_REGISTER.lock().await = None; loop { let timeout = Arc::new(RwLock::new(CONNECT_TIMEOUT)); let conn_start_time = Instant::now(); @@ -322,6 +345,8 @@ impl RendezvousMediator { Config::set_host_key_confirmed(&self.host_prefix, true); *SOLVING_PK_MISMATCH.lock().await = "".to_owned(); NEEDS_DEPLOY.store(false, Ordering::SeqCst); + #[cfg(target_os = "android")] + reset_needs_deploy_notification(); } Ok(register_pk_response::Result::UUID_MISMATCH) => { self.handle_uuid_mismatch(sink).await?; @@ -336,6 +361,8 @@ impl RendezvousMediator { // was deleted by an admin while running. Config::set_key_confirmed(false); Config::set_host_key_confirmed(&self.host_prefix, false); + #[cfg(target_os = "android")] + notify_android_needs_deploy(); } _ => { log::error!("unknown RegisterPkResponse"); diff --git a/src/ui_interface.rs b/src/ui_interface.rs index f70021e5b..650972f72 100644 --- a/src/ui_interface.rs +++ b/src/ui_interface.rs @@ -1020,6 +1020,102 @@ pub fn get_api_server() -> String { ) } +pub enum DeployResult { + Ok, + NotEnabled, + InvalidInput, + IdTaken(String), + Error(String), +} + +impl DeployResult { + pub fn message(&self) -> String { + match self { + Self::Ok => "".to_owned(), + Self::NotEnabled => "The server does not require explicit deployment.".to_owned(), + Self::InvalidInput => "Invalid input.".to_owned(), + Self::IdTaken(id) => { + format!( + "Id `{}` is already used by another machine on the server.", + id + ) + } + Self::Error(err) => err.clone(), + } + } +} + +pub fn deploy_device(token: String, new_id: Option) -> DeployResult { + if Config::no_register_device() { + return DeployResult::Error("Cannot deploy an unregistrable device!".to_owned()); + } + let token = token.trim(); + if token.is_empty() { + return DeployResult::Error("token is required!".to_owned()); + } + #[cfg(any(target_os = "android", target_os = "ios"))] + let local_id = Config::get_id(); + #[cfg(not(any(target_os = "android", target_os = "ios")))] + let local_id = ipc::get_id(); + let id_to_deploy = new_id.clone().unwrap_or_else(|| local_id.clone()); + let uuid = crate::encode64(hbb_common::get_uuid()); + let pk = crate::encode64(Config::get_key_pair().1); + let body = serde_json::json!({ + "id": id_to_deploy, + "uuid": uuid, + "pk": pk, + }); + let header = "Authorization: Bearer ".to_owned() + token; + let url = get_api_server() + "/api/devices/deploy"; + let text = match crate::post_request_sync(url, body.to_string(), &header) { + Ok(text) => text, + Err(err) => return DeployResult::Error(format!("Request failed: {}", err)), + }; + let parsed: serde_json::Value = serde_json::from_str(&text).unwrap_or(serde_json::Value::Null); + match parsed["result"].as_str().unwrap_or("") { + "OK" => { + if let Some(new_id) = new_id { + if new_id != local_id { + #[cfg(any(target_os = "android", target_os = "ios"))] + { + Config::set_key_confirmed(false); + Config::set_id(&new_id); + } + #[cfg(not(any(target_os = "android", target_os = "ios")))] + if let Err(err) = ipc::set_config("id", new_id) { + return DeployResult::Error(format!( + "Failed to persist deployed id locally: {}", + err + )); + } + } + } + #[cfg(not(any(target_os = "android", target_os = "ios")))] + if let Err(err) = ipc::notify_deployed() { + log::warn!("Failed to notify deployed state: {}", err); + } + #[cfg(target_os = "android")] + { + crate::rendezvous_mediator::NEEDS_DEPLOY + .store(false, std::sync::atomic::Ordering::SeqCst); + crate::rendezvous_mediator::reset_needs_deploy_notification(); + crate::rendezvous_mediator::RendezvousMediator::restart(); + } + DeployResult::Ok + } + "NOT_ENABLED" => DeployResult::NotEnabled, + "INVALID_INPUT" => DeployResult::InvalidInput, + "ID_TAKEN" => DeployResult::IdTaken(id_to_deploy), + _ => { + if text.is_empty() { + DeployResult::Error("Unknown response.".to_owned()) + } else { + DeployResult::Error(text) + } + } + } +} + #[inline] pub fn has_hwcodec() -> bool { // Has real hardware codec using gpu