diff --git a/flutter/lib/common.dart b/flutter/lib/common.dart index c1dd7cd4e..5f5f11eef 100644 --- a/flutter/lib/common.dart +++ b/flutter/lib/common.dart @@ -42,6 +42,7 @@ import 'package:flutter_hbb/native/win32.dart' if (dart.library.html) 'package:flutter_hbb/web/win32.dart'; import 'package:flutter_hbb/native/common.dart' if (dart.library.html) 'package:flutter_hbb/web/common.dart'; +import 'package:http/http.dart' as http; final globalKey = GlobalKey(); final navigationBarKey = GlobalKey(); @@ -2753,7 +2754,7 @@ class ServerConfig { } catch (err) { final input = msg.split('').reversed.join(''); final bytes = base64Decode(base64.normalize(input)); - json = jsonDecode(utf8.decode(bytes)); + json = jsonDecode(utf8.decode(bytes, allowMalformed: true)); } idServer = json['host'] ?? ''; relayServer = json['relay'] ?? ''; @@ -3931,3 +3932,14 @@ String getConnectionText(bool secure, bool direct, String streamType) { return '$connectionText ($streamType)'; } } + +String decode_http_response(http.Response resp) { + try { + // https://github.com/rustdesk/rustdesk-server-pro/discussions/758 + return utf8.decode(resp.bodyBytes, allowMalformed: true); + } catch (e) { + debugPrint('Failed to decode response as UTF-8: $e'); + // Fallback to bodyString which handles encoding automatically + return resp.body; + } +} diff --git a/flutter/lib/models/ab_model.dart b/flutter/lib/models/ab_model.dart index 790bc62fe..355e2fdab 100644 --- a/flutter/lib/models/ab_model.dart +++ b/flutter/lib/models/ab_model.dart @@ -208,7 +208,7 @@ class AbModel { return false; } Map json = - _jsonDecodeRespMap(utf8.decode(resp.bodyBytes), resp.statusCode); + _jsonDecodeRespMap(decode_http_response(resp), resp.statusCode); if (json.containsKey('error')) { throw json['error']; } @@ -234,7 +234,7 @@ class AbModel { return false; } Map json = - _jsonDecodeRespMap(utf8.decode(resp.bodyBytes), resp.statusCode); + _jsonDecodeRespMap(decode_http_response(resp), resp.statusCode); if (json.containsKey('error')) { throw json['error']; } @@ -271,7 +271,7 @@ class AbModel { headers['Content-Type'] = "application/json"; final resp = await http.post(uri, headers: headers); Map json = - _jsonDecodeRespMap(utf8.decode(resp.bodyBytes), resp.statusCode); + _jsonDecodeRespMap(decode_http_response(resp), resp.statusCode); if (json.containsKey('error')) { throw json['error']; } @@ -925,7 +925,7 @@ class LegacyAb extends BaseAb { peers.clear(); } else if (resp.body.isNotEmpty) { Map json = - _jsonDecodeRespMap(utf8.decode(resp.bodyBytes), resp.statusCode); + _jsonDecodeRespMap(decode_http_response(resp), resp.statusCode); if (json.containsKey('error')) { throw json['error']; } else if (json.containsKey('data')) { @@ -983,7 +983,7 @@ class LegacyAb extends BaseAb { ret = true; } else { Map json = - _jsonDecodeRespMap(utf8.decode(resp.bodyBytes), resp.statusCode); + _jsonDecodeRespMap(decode_http_response(resp), resp.statusCode); if (json.containsKey('error')) { throw json['error']; } else if (resp.statusCode == 200) { @@ -1359,7 +1359,7 @@ class Ab extends BaseAb { final resp = await http.post(uri, headers: headers); statusCode = resp.statusCode; Map json = - _jsonDecodeRespMap(utf8.decode(resp.bodyBytes), resp.statusCode); + _jsonDecodeRespMap(decode_http_response(resp), resp.statusCode); if (json.containsKey('error')) { throw json['error']; } @@ -1416,7 +1416,7 @@ class Ab extends BaseAb { final resp = await http.post(uri, headers: headers); statusCode = resp.statusCode; List json = - _jsonDecodeRespList(utf8.decode(resp.bodyBytes), resp.statusCode); + _jsonDecodeRespList(decode_http_response(resp), resp.statusCode); if (resp.statusCode != 200) { throw 'HTTP ${resp.statusCode}'; } diff --git a/flutter/lib/models/group_model.dart b/flutter/lib/models/group_model.dart index 534e897e9..c6ba992d2 100644 --- a/flutter/lib/models/group_model.dart +++ b/flutter/lib/models/group_model.dart @@ -122,7 +122,7 @@ class GroupModel { final resp = await http.get(uri, headers: getHttpHeaders()); _statusCode = resp.statusCode; Map json = - _jsonDecodeResp(utf8.decode(resp.bodyBytes), resp.statusCode); + _jsonDecodeResp(decode_http_response(resp), resp.statusCode); if (json.containsKey('error')) { throw json['error']; } @@ -180,7 +180,7 @@ class GroupModel { final resp = await http.get(uri, headers: getHttpHeaders()); _statusCode = resp.statusCode; Map json = - _jsonDecodeResp(utf8.decode(resp.bodyBytes), resp.statusCode); + _jsonDecodeResp(decode_http_response(resp), resp.statusCode); if (json.containsKey('error')) { if (json['error'] == 'Admin required!' || json['error'] @@ -246,7 +246,7 @@ class GroupModel { _statusCode = resp.statusCode; Map json = - _jsonDecodeResp(utf8.decode(resp.bodyBytes), resp.statusCode); + _jsonDecodeResp(decode_http_response(resp), resp.statusCode); if (json.containsKey('error')) { throw json['error']; } diff --git a/flutter/lib/models/terminal_model.dart b/flutter/lib/models/terminal_model.dart index ae64e8183..c6ace8f8c 100644 --- a/flutter/lib/models/terminal_model.dart +++ b/flutter/lib/models/terminal_model.dart @@ -304,14 +304,14 @@ class TerminalModel with ChangeNotifier { // Try to decode as base64 first try { final bytes = base64Decode(data); - text = utf8.decode(bytes); + text = utf8.decode(bytes, allowMalformed: true); } catch (e) { // If base64 decode fails, treat as plain text text = data; } } else if (data is List) { // Handle if data comes as byte array - text = utf8.decode(List.from(data)); + text = utf8.decode(List.from(data), allowMalformed: true); } else { debugPrint('[TerminalModel] Unknown data type: ${data.runtimeType}'); return; diff --git a/flutter/lib/models/user_model.dart b/flutter/lib/models/user_model.dart index 99a538062..217d74aee 100644 --- a/flutter/lib/models/user_model.dart +++ b/flutter/lib/models/user_model.dart @@ -66,7 +66,7 @@ class UserModel { reset(resetOther: status == 401); return; } - final data = json.decode(utf8.decode(response.bodyBytes)); + final data = json.decode(decode_http_response(response)); final error = data['error']; if (error != null) { throw error; @@ -160,7 +160,7 @@ class UserModel { final Map body; try { - body = jsonDecode(utf8.decode(resp.bodyBytes)); + body = jsonDecode(decode_http_response(resp)); } catch (e) { debugPrint("login: jsonDecode resp body failed: ${e.toString()}"); if (resp.statusCode != 200) {