// e-digiroot: automatic registration of this phone's RustDesk ID in the customer's e-shop account. // // The e-shop (Môj účet → Podpora na diaľku → Spustiť podporu) opens the app with a link // edigiroot-podpora://registracia?api=&u=&e=&t= // The signed token (u, e, t – HMAC, valid 30 days) allows only POST /rustdesk/id, the same call the // Windows and Linux launchers use. The customer types nothing. import 'dart:async'; import 'dart:convert'; import 'package:device_info_plus/device_info_plus.dart'; import 'package:http/http.dart' as http; import 'package:uni_links/uni_links.dart'; import 'common.dart'; import 'models/platform_model.dart'; const _kScheme = 'edigiroot-podpora'; const _kReg = 'edr-registracia'; const _kDone = 'edr-registracia-id'; StreamSubscription? _edrSub; /// Called once after the app starts (main.dart, runMobileApp). /// A link that started the app goes through RustDesk's initUniLinks → handleUriLink → edrSpracujOdkaz. Future edrInit() async { _edrSub ??= uriLinkStream.listen((uri) { if (_edrUlozOdkaz(uri)) edrRegistruj(force: true); }, onError: (_) {}); edrRegistruj(); } /// Hook in RustDesk's handleUriLink (common.dart): Android delivers a link only to the last uni_links /// listener (e.g. the Connect page), so our link is also caught there. Returns true when the link was ours. bool edrSpracujOdkaz({Uri? uri, String? uriString}) { final u = uri ?? (uriString != null ? Uri.tryParse(uriString) : null); if (!_edrUlozOdkaz(u)) return false; edrRegistruj(force: true); return true; } /// Stores the token from our link. Returns true when the link was ours. bool _edrUlozOdkaz(Uri? uri) { if (uri == null || uri.scheme != _kScheme) return false; final q = uri.queryParameters; final api = q['api'] ?? ''; if (!api.startsWith('https://') || (q['u'] ?? '').isEmpty || (q['e'] ?? '').isEmpty || (q['t'] ?? '').isEmpty) { return false; } bind.mainSetLocalOption( key: _kReg, value: jsonEncode({'api': api, 'u': q['u'], 'e': q['e'], 't': q['t']})); bind.mainSetLocalOption(key: _kDone, value: ''); return true; } /// Sends the ID to the e-shop account (once per ID; again after a new link). Future edrRegistruj({bool force = false}) async { Map reg; try { final s = bind.mainGetLocalOption(key: _kReg); if (s.isEmpty) return; reg = jsonDecode(s) as Map; } catch (_) { return; } final exp = int.tryParse('${reg['e']}') ?? 0; if (exp * 1000 < DateTime.now().millisecondsSinceEpoch) return; String id = ''; for (var i = 0; i < 30 && id.isEmpty; i++) { id = (await bind.mainGetMyId()).replaceAll(RegExp(r'\s'), ''); if (id.isEmpty) await Future.delayed(const Duration(seconds: 2)); } if (!RegExp(r'^\d{6,12}$').hasMatch(id)) return; if (!force && bind.mainGetLocalOption(key: _kDone) == id) return; var pc = 'Android'; var os = 'Android'; try { final a = await DeviceInfoPlugin().androidInfo; pc = '${a.manufacturer} ${a.model}'.trim(); os = 'Android ${a.version.release}'; } catch (_) {} try { final r = await http.post(Uri.parse('${reg['api']}/rustdesk/id'), body: { 'u': '${reg['u']}', 'e': '${reg['e']}', 't': '${reg['t']}', 'id': id, 'pc': pc, 'os': os, }).timeout(const Duration(seconds: 30)); if (r.statusCode == 200) { bind.mainSetLocalOption(key: _kDone, value: id); if (force) showToast(translate('Successful')); } } catch (_) {} }