Compare commits

..

1 Commits

Author SHA1 Message Date
rustdesk
24deed80f5 move port mapping after auth in port forwarding 2026-03-03 09:31:55 +08:00
30 changed files with 113 additions and 366 deletions

View File

@@ -40,7 +40,7 @@ env:
VCPKG_COMMIT_ID: "120deac3062162151622ca4860575a33844ba10b" VCPKG_COMMIT_ID: "120deac3062162151622ca4860575a33844ba10b"
ARMV7_VCPKG_COMMIT_ID: "6f29f12e82a8293156836ad81cc9bf5af41fe836" # 2025.01.13, got "/opt/artifacts/vcpkg/vcpkg: No such file or directory" with latest version ARMV7_VCPKG_COMMIT_ID: "6f29f12e82a8293156836ad81cc9bf5af41fe836" # 2025.01.13, got "/opt/artifacts/vcpkg/vcpkg: No such file or directory" with latest version
VERSION: "1.4.6" VERSION: "1.4.6"
NDK_VERSION: "r28c" NDK_VERSION: "r27c"
#signing keys env variable checks #signing keys env variable checks
ANDROID_SIGNING_KEY: "${{ secrets.ANDROID_SIGNING_KEY }}" ANDROID_SIGNING_KEY: "${{ secrets.ANDROID_SIGNING_KEY }}"
MACOS_P12_BASE64: "${{ secrets.MACOS_P12_BASE64 }}" MACOS_P12_BASE64: "${{ secrets.MACOS_P12_BASE64 }}"

View File

@@ -167,7 +167,7 @@ target/release/rustdesk
- **[src/ui](https://github.com/rustdesk/rustdesk/tree/master/src/ui)**: графический пользовательский интерфейс на Sciter (устаревшее) - **[src/ui](https://github.com/rustdesk/rustdesk/tree/master/src/ui)**: графический пользовательский интерфейс на Sciter (устаревшее)
- **[src/server](https://github.com/rustdesk/rustdesk/tree/master/src/server)**: сервисы аудио, буфера обмена, ввода, видео и сетевых подключений - **[src/server](https://github.com/rustdesk/rustdesk/tree/master/src/server)**: сервисы аудио, буфера обмена, ввода, видео и сетевых подключений
- **[src/client.rs](https://github.com/rustdesk/rustdesk/tree/master/src/client.rs)**: одноранговое соединение - **[src/client.rs](https://github.com/rustdesk/rustdesk/tree/master/src/client.rs)**: одноранговое соединение
- **[src/rendezvous_mediator.rs](https://github.com/rustdesk/rustdesk/tree/master/src/rendezvous_mediator.rs)**: связь с [сервером RustDesk](https://github.com/rustdesk/rustdesk-server), ожидает удаленного прямого (через TCP hole punching) или ретранслируемого соединения - **[src/rendezvous_mediator.rs](https://github.com/rustdesk/rustdesk/tree/master/src/rendezvous_mediator.rs)**: связь с [сервером Rustdesk](https://github.com/rustdesk/rustdesk-server), ожидает удаленного прямого (через TCP hole punching) или ретранслируемого соединения
- **[src/platform](https://github.com/rustdesk/rustdesk/tree/master/src/platform)**: специфичный для платформы код - **[src/platform](https://github.com/rustdesk/rustdesk/tree/master/src/platform)**: специфичный для платформы код
- **[flutter](https://github.com/rustdesk/rustdesk/tree/master/flutter)**: код Flutter для ПК-версии и мобильных устройств - **[flutter](https://github.com/rustdesk/rustdesk/tree/master/flutter)**: код Flutter для ПК-версии и мобильных устройств
- **[flutter/web/js](https://github.com/rustdesk/rustdesk/tree/master/flutter/web/v1/js)**: JavaScript для Web-клиента Flutter - **[flutter/web/js](https://github.com/rustdesk/rustdesk/tree/master/flutter/web/v1/js)**: JavaScript для Web-клиента Flutter

View File

@@ -4118,43 +4118,3 @@ String mouseButtonsToPeer(int buttons) {
return ''; return '';
} }
} }
/// Build an avatar widget from an avatar URL or data URI string.
/// Returns [fallback] if avatar is empty or cannot be decoded.
/// [borderRadius] defaults to [size]/2 (circle).
Widget? buildAvatarWidget({
required String avatar,
required double size,
double? borderRadius,
Widget? fallback,
}) {
final trimmed = avatar.trim();
if (trimmed.isEmpty) return fallback;
ImageProvider? imageProvider;
if (trimmed.startsWith('data:image/')) {
final comma = trimmed.indexOf(',');
if (comma > 0) {
try {
imageProvider = MemoryImage(base64Decode(trimmed.substring(comma + 1)));
} catch (_) {}
}
} else if (trimmed.startsWith('http://') || trimmed.startsWith('https://')) {
imageProvider = NetworkImage(trimmed);
}
if (imageProvider == null) return fallback;
final radius = borderRadius ?? size / 2;
return ClipRRect(
borderRadius: BorderRadius.circular(radius),
child: Image(
image: imageProvider,
width: size,
height: size,
fit: BoxFit.cover,
errorBuilder: (_, __, ___) =>
fallback ?? SizedBox.shrink(),
),
);
}

View File

@@ -26,7 +26,6 @@ enum UserStatus { kDisabled, kNormal, kUnverified }
class UserPayload { class UserPayload {
String name = ''; String name = '';
String displayName = ''; String displayName = '';
String avatar = '';
String email = ''; String email = '';
String note = ''; String note = '';
String? verifier; String? verifier;
@@ -36,7 +35,6 @@ class UserPayload {
UserPayload.fromJson(Map<String, dynamic> json) UserPayload.fromJson(Map<String, dynamic> json)
: name = json['name'] ?? '', : name = json['name'] ?? '',
displayName = json['display_name'] ?? '', displayName = json['display_name'] ?? '',
avatar = json['avatar'] ?? '',
email = json['email'] ?? '', email = json['email'] ?? '',
note = json['note'] ?? '', note = json['note'] ?? '',
verifier = json['verifier'], verifier = json['verifier'],
@@ -51,7 +49,6 @@ class UserPayload {
final Map<String, dynamic> map = { final Map<String, dynamic> map = {
'name': name, 'name': name,
'display_name': displayName, 'display_name': displayName,
'avatar': avatar,
'status': status == UserStatus.kDisabled 'status': status == UserStatus.kDisabled
? 0 ? 0
: status == UserStatus.kUnverified : status == UserStatus.kUnverified

View File

@@ -2026,65 +2026,28 @@ class _AccountState extends State<_Account> {
} }
Widget useInfo() { Widget useInfo() {
text(String key, String value) {
return Align(
alignment: Alignment.centerLeft,
child: SelectionArea(child: Text('${translate(key)}: $value'))
.marginSymmetric(vertical: 4),
);
}
return Obx(() => Offstage( return Obx(() => Offstage(
offstage: gFFI.userModel.userName.value.isEmpty, offstage: gFFI.userModel.userName.value.isEmpty,
child: Container( child: Column(
padding: const EdgeInsets.all(12), children: [
decoration: BoxDecoration( if (gFFI.userModel.displayName.value.trim().isNotEmpty &&
color: Theme.of(context).colorScheme.surfaceContainerHighest, gFFI.userModel.displayName.value.trim() !=
borderRadius: BorderRadius.circular(10), gFFI.userModel.userName.value.trim())
), text('Display Name', gFFI.userModel.displayName.value.trim()),
child: Builder(builder: (context) { text('Username', gFFI.userModel.userName.value),
final avatarWidget = _buildUserAvatar(); // text('Group', gFFI.groupModel.groupName.value),
return Row( ],
children: [
if (avatarWidget != null) avatarWidget,
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
gFFI.userModel.displayNameOrUserName,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
),
),
const SizedBox(height: 2),
SelectionArea(
child: Text(
'@${gFFI.userModel.userName.value}',
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 13,
color:
Theme.of(context).textTheme.bodySmall?.color,
),
),
),
],
),
),
],
);
}),
), ),
)).marginOnly(left: 18, top: 16); )).marginOnly(left: 18, top: 16);
} }
Widget? _buildUserAvatar() {
// Resolve relative avatar path at display time
final avatar =
bind.mainResolveAvatarUrl(avatar: gFFI.userModel.avatar.value);
return buildAvatarWidget(
avatar: avatar,
size: 44,
);
}
} }
class _Checkbox extends StatefulWidget { class _Checkbox extends StatefulWidget {

View File

@@ -462,7 +462,23 @@ class _CmHeaderState extends State<_CmHeader>
child: Row( child: Row(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
_buildClientAvatar().marginOnly(right: 10.0), Container(
width: 70,
height: 70,
alignment: Alignment.center,
decoration: BoxDecoration(
color: str2color(client.name),
borderRadius: BorderRadius.circular(15.0),
),
child: Text(
client.name[0],
style: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.white,
fontSize: 55,
),
),
).marginOnly(right: 10.0),
Expanded( Expanded(
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start,
@@ -566,36 +582,6 @@ class _CmHeaderState extends State<_CmHeader>
@override @override
bool get wantKeepAlive => true; bool get wantKeepAlive => true;
Widget _buildClientAvatar() {
return buildAvatarWidget(
avatar: client.avatar,
size: 70,
borderRadius: 15,
fallback: _buildInitialAvatar(),
) ??
_buildInitialAvatar();
}
Widget _buildInitialAvatar() {
return Container(
width: 70,
height: 70,
alignment: Alignment.center,
decoration: BoxDecoration(
color: str2color(client.name),
borderRadius: BorderRadius.circular(15.0),
),
child: Text(
client.name.isNotEmpty ? client.name[0] : '?',
style: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.white,
fontSize: 55,
),
),
);
}
} }
class _PrivilegeBoard extends StatefulWidget { class _PrivilegeBoard extends StatefulWidget {

View File

@@ -1,4 +1,5 @@
import 'dart:async'; import 'dart:async';
import 'dart:ui' as ui;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
@@ -64,7 +65,9 @@ class _RemotePageState extends State<RemotePage> with WidgetsBindingObserver {
bool _showGestureHelp = false; bool _showGestureHelp = false;
String _value = ''; String _value = '';
Orientation? _currentOrientation; Orientation? _currentOrientation;
double _viewInsetsBottom = 0;
final _uniqueKey = UniqueKey(); final _uniqueKey = UniqueKey();
Timer? _timerDidChangeMetrics;
Timer? _iosKeyboardWorkaroundTimer; Timer? _iosKeyboardWorkaroundTimer;
final _blockableOverlayState = BlockableOverlayState(); final _blockableOverlayState = BlockableOverlayState();
@@ -137,6 +140,7 @@ class _RemotePageState extends State<RemotePage> with WidgetsBindingObserver {
_physicalFocusNode.dispose(); _physicalFocusNode.dispose();
await gFFI.close(); await gFFI.close();
_timer?.cancel(); _timer?.cancel();
_timerDidChangeMetrics?.cancel();
_iosKeyboardWorkaroundTimer?.cancel(); _iosKeyboardWorkaroundTimer?.cancel();
gFFI.dialogManager.dismissAll(); gFFI.dialogManager.dismissAll();
await SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, await SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual,
@@ -163,6 +167,26 @@ class _RemotePageState extends State<RemotePage> with WidgetsBindingObserver {
gFFI.invokeMethod("try_sync_clipboard"); gFFI.invokeMethod("try_sync_clipboard");
} }
@override
void didChangeMetrics() {
// If the soft keyboard is visible and the canvas has been changed(panned or scaled)
// Don't try reset the view style and focus the cursor.
if (gFFI.cursorModel.lastKeyboardIsVisible &&
gFFI.canvasModel.isMobileCanvasChanged) {
return;
}
final newBottom = MediaQueryData.fromView(ui.window).viewInsets.bottom;
_timerDidChangeMetrics?.cancel();
_timerDidChangeMetrics = Timer(Duration(milliseconds: 100), () async {
// We need this comparation because poping up the floating action will also trigger `didChangeMetrics()`.
if (newBottom != _viewInsetsBottom) {
gFFI.canvasModel.mobileFocusCanvasCursor();
_viewInsetsBottom = newBottom;
}
});
}
// to-do: It should be better to use transparent color instead of the bgColor. // to-do: It should be better to use transparent color instead of the bgColor.
// But for now, the transparent color will cause the canvas to be white. // But for now, the transparent color will cause the canvas to be white.
// I'm sure that the white color is caused by the Overlay widget in BlockableOverlay. // I'm sure that the white color is caused by the Overlay widget in BlockableOverlay.

View File

@@ -841,7 +841,13 @@ class ClientInfo extends StatelessWidget {
flex: -1, flex: -1,
child: Padding( child: Padding(
padding: const EdgeInsets.only(right: 12), padding: const EdgeInsets.only(right: 12),
child: _buildAvatar(context))), child: CircleAvatar(
backgroundColor: str2color(
client.name,
Theme.of(context).brightness == Brightness.light
? 255
: 150),
child: Text(client.name[0])))),
Expanded( Expanded(
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
@@ -854,20 +860,6 @@ class ClientInfo extends StatelessWidget {
), ),
])); ]));
} }
Widget _buildAvatar(BuildContext context) {
final fallback = CircleAvatar(
backgroundColor: str2color(client.name,
Theme.of(context).brightness == Brightness.light ? 255 : 150),
child: Text(client.name.isNotEmpty ? client.name[0] : '?'),
);
return buildAvatarWidget(
avatar: client.avatar,
size: 40,
fallback: fallback,
) ??
fallback;
}
} }
void androidChannelInit() { void androidChannelInit() {

View File

@@ -617,7 +617,7 @@ class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
onToggle: (bool v) async { onToggle: (bool v) async {
await mainSetLocalBoolOption(kOptionEnableShowTerminalExtraKeys, v); await mainSetLocalBoolOption(kOptionEnableShowTerminalExtraKeys, v);
final newValue = final newValue =
mainGetLocalBoolOptionSync(kOptionEnableShowTerminalExtraKeys); mainGetLocalBoolOptionSync(kOptionEnableShowTerminalExtraKeys);
setState(() { setState(() {
_showTerminalExtraKeys = newValue; _showTerminalExtraKeys = newValue;
}); });
@@ -689,17 +689,7 @@ class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
title: Obx(() => Text(gFFI.userModel.userName.value.isEmpty title: Obx(() => Text(gFFI.userModel.userName.value.isEmpty
? translate('Login') ? translate('Login')
: '${translate('Logout')} (${gFFI.userModel.accountLabelWithHandle})')), : '${translate('Logout')} (${gFFI.userModel.accountLabelWithHandle})')),
leading: Obx(() { leading: Icon(Icons.person),
final avatar = bind.mainResolveAvatarUrl(
avatar: gFFI.userModel.avatar.value);
return buildAvatarWidget(
avatar: avatar,
size: 28,
borderRadius: null,
fallback: Icon(Icons.person),
) ??
Icon(Icons.person);
}),
onPressed: (context) { onPressed: (context) {
if (gFFI.userModel.userName.value.isEmpty) { if (gFFI.userModel.userName.value.isEmpty) {
loginDialog(); loginDialog();
@@ -839,12 +829,10 @@ class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
), ),
if (!incomingOnly) if (!incomingOnly)
SettingsTile.switchTile( SettingsTile.switchTile(
title: title: Text(translate('keep-awake-during-outgoing-sessions-label')),
Text(translate('keep-awake-during-outgoing-sessions-label')),
initialValue: _preventSleepWhileConnected, initialValue: _preventSleepWhileConnected,
onToggle: (v) async { onToggle: (v) async {
await mainSetLocalBoolOption( await mainSetLocalBoolOption(kOptionKeepAwakeDuringOutgoingSessions, v);
kOptionKeepAwakeDuringOutgoingSessions, v);
setState(() { setState(() {
_preventSleepWhileConnected = v; _preventSleepWhileConnected = v;
}); });

View File

@@ -348,12 +348,6 @@ class InputModel {
final _trackpadAdjustPeerLinux = 0.06; final _trackpadAdjustPeerLinux = 0.06;
// This is an experience value. // This is an experience value.
final _trackpadAdjustMacToWin = 2.50; final _trackpadAdjustMacToWin = 2.50;
// Ignore directional locking for very small deltas on both axes (including
// tiny single-axis movement) to avoid over-filtering near zero.
static const double _trackpadAxisNoiseThreshold = 0.2;
// Lock to dominant axis only when one axis is clearly stronger.
// 1.6 means the dominant axis must be >= 60% larger than the other.
static const double _trackpadAxisLockRatio = 1.6;
int _trackpadSpeed = kDefaultTrackpadSpeed; int _trackpadSpeed = kDefaultTrackpadSpeed;
double _trackpadSpeedInner = kDefaultTrackpadSpeed / 100.0; double _trackpadSpeedInner = kDefaultTrackpadSpeed / 100.0;
var _trackpadScrollUnsent = Offset.zero; var _trackpadScrollUnsent = Offset.zero;
@@ -1178,7 +1172,6 @@ class InputModel {
if (isMacOS && peerPlatform == kPeerPlatformWindows) { if (isMacOS && peerPlatform == kPeerPlatformWindows) {
delta *= _trackpadAdjustMacToWin; delta *= _trackpadAdjustMacToWin;
} }
delta = _filterTrackpadDeltaAxis(delta);
_trackpadLastDelta = delta; _trackpadLastDelta = delta;
var x = delta.dx.toInt(); var x = delta.dx.toInt();
@@ -1211,24 +1204,6 @@ class InputModel {
} }
} }
Offset _filterTrackpadDeltaAxis(Offset delta) {
final absDx = delta.dx.abs();
final absDy = delta.dy.abs();
// Keep diagonal intent when movement is tiny on both axes.
if (absDx < _trackpadAxisNoiseThreshold &&
absDy < _trackpadAxisNoiseThreshold) {
return delta;
}
// Dominant-axis lock to reduce accidental cross-axis scrolling noise.
if (absDy >= absDx * _trackpadAxisLockRatio) {
return Offset(0, delta.dy);
}
if (absDx >= absDy * _trackpadAxisLockRatio) {
return Offset(delta.dx, 0);
}
return delta;
}
void _scheduleFling(double x, double y, int delay) { void _scheduleFling(double x, double y, int delay) {
if (isViewCamera) return; if (isViewCamera) return;
if ((x == 0 && y == 0) || _stopFling) { if ((x == 0 && y == 0) || _stopFling) {

View File

@@ -1016,31 +1016,19 @@ class FfiModel with ChangeNotifier {
showMsgBox(SessionID sessionId, String type, String title, String text, showMsgBox(SessionID sessionId, String type, String title, String text,
String link, bool hasRetry, OverlayDialogManager dialogManager, String link, bool hasRetry, OverlayDialogManager dialogManager,
{bool? hasCancel}) async { {bool? hasCancel}) async {
final noteAllowed = parent.target != null && final showNoteEdit = parent.target != null &&
allowAskForNoteAtEndOfConnection(parent.target, false) && allowAskForNoteAtEndOfConnection(parent.target, false) &&
(title == "Connection Error" || type == "restarting"); (title == "Connection Error" || type == "restarting") &&
final showNoteEdit = noteAllowed && !hasRetry; !hasRetry;
if (showNoteEdit) { if (showNoteEdit) {
await showConnEndAuditDialogCloseCanceled( await showConnEndAuditDialogCloseCanceled(
ffi: parent.target!, type: type, title: title, text: text); ffi: parent.target!, type: type, title: title, text: text);
closeConnection(); closeConnection();
} else { } else {
VoidCallback? onSubmit;
if (noteAllowed && hasRetry) {
final ffi = parent.target!;
onSubmit = () async {
_timer?.cancel();
_timer = null;
await showConnEndAuditDialogCloseCanceled(
ffi: ffi, type: type, title: title, text: text);
closeConnection();
};
}
msgBox(sessionId, type, title, text, link, dialogManager, msgBox(sessionId, type, title, text, link, dialogManager,
hasCancel: hasCancel, hasCancel: hasCancel,
reconnect: hasRetry ? reconnect : null, reconnect: hasRetry ? reconnect : null,
reconnectTimeout: hasRetry ? _reconnects : null, reconnectTimeout: hasRetry ? _reconnects : null);
onSubmit: onSubmit);
} }
_timer?.cancel(); _timer?.cancel();
if (hasRetry) { if (hasRetry) {
@@ -2164,9 +2152,6 @@ class CanvasModel with ChangeNotifier {
ViewStyle _lastViewStyle = ViewStyle.defaultViewStyle(); ViewStyle _lastViewStyle = ViewStyle.defaultViewStyle();
Timer? _timerMobileFocusCanvasCursor; Timer? _timerMobileFocusCanvasCursor;
Timer? _timerMobileRestoreCanvasOffset;
Offset? _offsetBeforeMobileSoftKeyboard;
double? _scaleBeforeMobileSoftKeyboard;
// `isMobileCanvasChanged` is used to avoid canvas reset when changing the input method // `isMobileCanvasChanged` is used to avoid canvas reset when changing the input method
// after showing the soft keyboard. // after showing the soft keyboard.
@@ -2654,9 +2639,6 @@ class CanvasModel with ChangeNotifier {
_scale = 1.0; _scale = 1.0;
_lastViewStyle = ViewStyle.defaultViewStyle(); _lastViewStyle = ViewStyle.defaultViewStyle();
_timerMobileFocusCanvasCursor?.cancel(); _timerMobileFocusCanvasCursor?.cancel();
_timerMobileRestoreCanvasOffset?.cancel();
_offsetBeforeMobileSoftKeyboard = null;
_scaleBeforeMobileSoftKeyboard = null;
} }
updateScrollPercent() { updateScrollPercent() {
@@ -2685,31 +2667,6 @@ class CanvasModel with ChangeNotifier {
}); });
} }
void saveMobileOffsetBeforeSoftKeyboard() {
_timerMobileRestoreCanvasOffset?.cancel();
_offsetBeforeMobileSoftKeyboard = Offset(_x, _y);
_scaleBeforeMobileSoftKeyboard = _scale;
}
void restoreMobileOffsetAfterSoftKeyboard() {
_timerMobileRestoreCanvasOffset?.cancel();
_timerMobileFocusCanvasCursor?.cancel();
final targetOffset = _offsetBeforeMobileSoftKeyboard;
final targetScale = _scaleBeforeMobileSoftKeyboard;
if (targetOffset == null || targetScale == null) {
return;
}
_timerMobileRestoreCanvasOffset = Timer(Duration(milliseconds: 100), () {
updateSize();
_x = targetOffset.dx;
_y = targetOffset.dy;
_scale = targetScale;
_offsetBeforeMobileSoftKeyboard = null;
_scaleBeforeMobileSoftKeyboard = null;
notifyListeners();
});
}
// mobile only // mobile only
// Move the canvas to make the cursor visible(center) on the screen. // Move the canvas to make the cursor visible(center) on the screen.
void _moveToCenterCursor() { void _moveToCenterCursor() {
@@ -2962,13 +2919,8 @@ class CursorModel with ChangeNotifier {
_lastIsBlocked = true; _lastIsBlocked = true;
} }
if (isMobile && _lastKeyboardIsVisible != keyboardIsVisible) { if (isMobile && _lastKeyboardIsVisible != keyboardIsVisible) {
if (keyboardIsVisible) { parent.target?.canvasModel.mobileFocusCanvasCursor();
parent.target?.canvasModel.saveMobileOffsetBeforeSoftKeyboard(); parent.target?.canvasModel.isMobileCanvasChanged = false;
parent.target?.canvasModel.mobileFocusCanvasCursor();
parent.target?.canvasModel.isMobileCanvasChanged = false;
} else {
parent.target?.canvasModel.restoreMobileOffsetAfterSoftKeyboard();
}
} }
_lastKeyboardIsVisible = keyboardIsVisible; _lastKeyboardIsVisible = keyboardIsVisible;
} }

View File

@@ -820,7 +820,6 @@ class Client {
bool isTerminal = false; bool isTerminal = false;
String portForward = ""; String portForward = "";
String name = ""; String name = "";
String avatar = "";
String peerId = ""; // peer user's id,show at app String peerId = ""; // peer user's id,show at app
bool keyboard = false; bool keyboard = false;
bool clipboard = false; bool clipboard = false;
@@ -848,7 +847,6 @@ class Client {
isTerminal = json['is_terminal'] ?? false; isTerminal = json['is_terminal'] ?? false;
portForward = json['port_forward']; portForward = json['port_forward'];
name = json['name']; name = json['name'];
avatar = json['avatar'] ?? '';
peerId = json['peer_id']; peerId = json['peer_id'];
keyboard = json['keyboard']; keyboard = json['keyboard'];
clipboard = json['clipboard']; clipboard = json['clipboard'];
@@ -872,7 +870,6 @@ class Client {
data['is_terminal'] = isTerminal; data['is_terminal'] = isTerminal;
data['port_forward'] = portForward; data['port_forward'] = portForward;
data['name'] = name; data['name'] = name;
data['avatar'] = avatar;
data['peer_id'] = peerId; data['peer_id'] = peerId;
data['keyboard'] = keyboard; data['keyboard'] = keyboard;
data['clipboard'] = clipboard; data['clipboard'] = clipboard;

View File

@@ -17,7 +17,6 @@ bool refreshingUser = false;
class UserModel { class UserModel {
final RxString userName = ''.obs; final RxString userName = ''.obs;
final RxString displayName = ''.obs; final RxString displayName = ''.obs;
final RxString avatar = ''.obs;
final RxBool isAdmin = false.obs; final RxBool isAdmin = false.obs;
final RxString networkError = ''.obs; final RxString networkError = ''.obs;
bool get isLogin => userName.isNotEmpty; bool get isLogin => userName.isNotEmpty;
@@ -34,7 +33,6 @@ class UserModel {
} }
return '$preferred (@$username)'; return '$preferred (@$username)';
} }
WeakReference<FFI> parent; WeakReference<FFI> parent;
UserModel(this.parent) { UserModel(this.parent) {
@@ -116,7 +114,6 @@ class UserModel {
if (userInfo != null) { if (userInfo != null) {
userName.value = (userInfo['name'] ?? '').toString(); userName.value = (userInfo['name'] ?? '').toString();
displayName.value = (userInfo['display_name'] ?? '').toString(); displayName.value = (userInfo['display_name'] ?? '').toString();
avatar.value = (userInfo['avatar'] ?? '').toString();
} }
} }
@@ -129,13 +126,11 @@ class UserModel {
} }
userName.value = ''; userName.value = '';
displayName.value = ''; displayName.value = '';
avatar.value = '';
} }
_parseAndUpdateUser(UserPayload user) { _parseAndUpdateUser(UserPayload user) {
userName.value = user.name; userName.value = user.name;
displayName.value = user.displayName; displayName.value = user.displayName;
avatar.value = user.avatar;
isAdmin.value = user.isAdmin; isAdmin.value = user.isAdmin;
bind.mainSetLocalOption(key: 'user_info', value: jsonEncode(user)); bind.mainSetLocalOption(key: 'user_info', value: jsonEncode(user));
if (isWeb) { if (isWeb) {

View File

@@ -2034,9 +2034,5 @@ class RustdeskImpl {
return false; return false;
} }
String mainResolveAvatarUrl({required String avatar, dynamic hint}) {
return js.context.callMethod('getByName', ['resolve_avatar_url', avatar])?.toString() ?? avatar;
}
void dispose() {} void dispose() {}
} }

View File

@@ -33,7 +33,7 @@ use crate::{
create_symmetric_key_msg, decode_id_pk, get_rs_pk, is_keyboard_mode_supported, create_symmetric_key_msg, decode_id_pk, get_rs_pk, is_keyboard_mode_supported,
kcp_stream::KcpStream, kcp_stream::KcpStream,
secure_tcp, secure_tcp,
ui_interface::{get_builtin_option, resolve_avatar_url, use_texture_render}, ui_interface::{get_builtin_option, use_texture_render},
ui_session_interface::{InvokeUiSession, Session}, ui_session_interface::{InvokeUiSession, Session},
}; };
#[cfg(feature = "unix-file-copy-paste")] #[cfg(feature = "unix-file-copy-paste")]
@@ -2625,20 +2625,6 @@ impl LoginConfigHandler {
} else { } else {
(my_id, self.id.clone()) (my_id, self.id.clone())
}; };
let mut avatar = get_builtin_option(keys::OPTION_AVATAR);
if avatar.is_empty() {
avatar = serde_json::from_str::<serde_json::Value>(&LocalConfig::get_option(
"user_info",
))
.ok()
.and_then(|x| {
x.get("avatar")
.and_then(|x| x.as_str())
.map(|x| x.trim().to_owned())
})
.unwrap_or_default();
}
avatar = resolve_avatar_url(avatar);
let mut display_name = get_builtin_option(keys::OPTION_DISPLAY_NAME); let mut display_name = get_builtin_option(keys::OPTION_DISPLAY_NAME);
if display_name.is_empty() { if display_name.is_empty() {
display_name = display_name =
@@ -2698,7 +2684,6 @@ impl LoginConfigHandler {
}) })
.into(), .into(),
hwid, hwid,
avatar,
..Default::default() ..Default::default()
}; };
match self.conn_type { match self.conn_type {

View File

@@ -1101,10 +1101,6 @@ pub fn main_get_api_server() -> String {
get_api_server() get_api_server()
} }
pub fn main_resolve_avatar_url(avatar: String) -> SyncReturn<String> {
SyncReturn(resolve_avatar_url(avatar))
}
pub fn main_http_request(url: String, method: String, body: Option<String>, header: String) { pub fn main_http_request(url: String, method: String, body: Option<String>, header: String) {
http_request(url, method, body, header) http_request(url, method, body, header)
} }

View File

@@ -17,7 +17,6 @@ lazy_static::lazy_static! {
const QUERY_INTERVAL_SECS: f32 = 1.0; const QUERY_INTERVAL_SECS: f32 = 1.0;
const QUERY_TIMEOUT_SECS: u64 = 60 * 3; const QUERY_TIMEOUT_SECS: u64 = 60 * 3;
const REQUESTING_ACCOUNT_AUTH: &str = "Requesting account auth"; const REQUESTING_ACCOUNT_AUTH: &str = "Requesting account auth";
const WAITING_ACCOUNT_AUTH: &str = "Waiting account auth"; const WAITING_ACCOUNT_AUTH: &str = "Waiting account auth";
const LOGIN_ACCOUNT_AUTH: &str = "Login account auth"; const LOGIN_ACCOUNT_AUTH: &str = "Login account auth";
@@ -83,8 +82,6 @@ pub struct UserPayload {
#[serde(default)] #[serde(default)]
pub display_name: Option<String>, pub display_name: Option<String>,
#[serde(default)] #[serde(default)]
pub avatar: Option<String>,
#[serde(default)]
pub email: Option<String>, pub email: Option<String>,
#[serde(default)] #[serde(default)]
pub note: Option<String>, pub note: Option<String>,
@@ -276,7 +273,6 @@ impl OidcSession {
serde_json::json!({ serde_json::json!({
"name": auth_body.user.name, "name": auth_body.user.name,
"display_name": auth_body.user.display_name, "display_name": auth_body.user.display_name,
"avatar": auth_body.user.avatar,
"status": auth_body.user.status "status": auth_body.user.status
}) })
.to_string(), .to_string(),

View File

@@ -286,14 +286,10 @@ fn heartbeat_url() -> String {
fn handle_config_options(config_options: HashMap<String, String>) { fn handle_config_options(config_options: HashMap<String, String>) {
let mut options = Config::get_options(); let mut options = Config::get_options();
let default_settings = config::DEFAULT_SETTINGS.read().unwrap().clone();
config_options config_options
.iter() .iter()
.map(|(k, v)| { .map(|(k, v)| {
// Priority: user config > default advanced options. if v.is_empty() {
// Only when default advanced options are also empty, remove user option (fallback to built-in default);
// otherwise insert an empty value so user config remains present.
if v.is_empty() && default_settings.get(k).map_or("", |v| v).is_empty() {
options.remove(k); options.remove(k);
} else { } else {
options.insert(k.to_string(), v.to_string()); options.insert(k.to_string(), v.to_string());

View File

@@ -226,7 +226,6 @@ pub enum Data {
is_terminal: bool, is_terminal: bool,
peer_id: String, peer_id: String,
name: String, name: String,
avatar: String,
authorized: bool, authorized: bool,
port_forward: String, port_forward: String,
keyboard: bool, keyboard: bool,
@@ -1584,6 +1583,6 @@ mod test {
#[test] #[test]
fn verify_ffi_enum_data_size() { fn verify_ffi_enum_data_size() {
println!("{}", std::mem::size_of::<Data>()); println!("{}", std::mem::size_of::<Data>());
assert!(std::mem::size_of::<Data>() <= 120); assert!(std::mem::size_of::<Data>() <= 96);
} }
} }

View File

@@ -738,7 +738,5 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Changelog", "Változáslista"), ("Changelog", "Változáslista"),
("keep-awake-during-outgoing-sessions-label", "Képernyő aktív állapotban tartása a kimenő munkamenetek során"), ("keep-awake-during-outgoing-sessions-label", "Képernyő aktív állapotban tartása a kimenő munkamenetek során"),
("keep-awake-during-incoming-sessions-label", "Képernyő aktív állapotban tartása a bejövő munkamenetek során"), ("keep-awake-during-incoming-sessions-label", "Képernyő aktív állapotban tartása a bejövő munkamenetek során"),
("Continue with {}", "Folytatás ezzel: {}"),
("Display Name", "Kijelző név"),
].iter().cloned().collect(); ].iter().cloned().collect();
} }

View File

@@ -859,10 +859,9 @@ on run {app_name, cur_pid, app_dir, user_name}
set app_dir_q to quoted form of app_dir set app_dir_q to quoted form of app_dir
set user_name_q to quoted form of user_name set user_name_q to quoted form of user_name
set check_source to "test -d " & app_dir_q & " || exit 1;"
set kill_others to "pids=$(pgrep -x '" & app_name & "' | grep -vx " & cur_pid & " || true); if [ -n \"$pids\" ]; then echo \"$pids\" | xargs kill -9 || true; fi;" set kill_others to "pids=$(pgrep -x '" & app_name & "' | grep -vx " & cur_pid & " || true); if [ -n \"$pids\" ]; then echo \"$pids\" | xargs kill -9 || true; fi;"
set copy_files to "rm -rf " & app_bundle_q & " && ditto " & app_dir_q & " " & app_bundle_q & " && chown -R " & user_name_q & ":staff " & app_bundle_q & " && (xattr -r -d com.apple.quarantine " & app_bundle_q & " || true);" set copy_files to "rm -rf " & app_bundle_q & " && ditto " & app_dir_q & " " & app_bundle_q & " && chown -R " & user_name_q & ":staff " & app_bundle_q & " && (xattr -r -d com.apple.quarantine " & app_bundle_q & " || true);"
set sh to "set -e;" & check_source & kill_others & copy_files set sh to "set -e;" & kill_others & copy_files
do shell script sh with prompt app_name & " wants to update itself" with administrator privileges do shell script sh with prompt app_name & " wants to update itself" with administrator privileges
end run end run

View File

@@ -4,7 +4,6 @@ on run {daemon_file, agent_file, user, cur_pid, source_dir}
set daemon_plist to "/Library/LaunchDaemons/com.carriez.RustDesk_service.plist" set daemon_plist to "/Library/LaunchDaemons/com.carriez.RustDesk_service.plist"
set app_bundle to "/Applications/RustDesk.app" set app_bundle to "/Applications/RustDesk.app"
set check_source to "test -d " & quoted form of source_dir & " || exit 1;"
set resolve_uid to "uid=$(id -u " & quoted form of user & " 2>/dev/null || true);" set resolve_uid to "uid=$(id -u " & quoted form of user & " 2>/dev/null || true);"
set unload_agent to "if [ -n \"$uid\" ]; then launchctl bootout gui/$uid " & quoted form of agent_plist & " 2>/dev/null || launchctl bootout user/$uid " & quoted form of agent_plist & " 2>/dev/null || launchctl unload -w " & quoted form of agent_plist & " || true; else launchctl unload -w " & quoted form of agent_plist & " || true; fi;" set unload_agent to "if [ -n \"$uid\" ]; then launchctl bootout gui/$uid " & quoted form of agent_plist & " 2>/dev/null || launchctl bootout user/$uid " & quoted form of agent_plist & " 2>/dev/null || launchctl unload -w " & quoted form of agent_plist & " || true; else launchctl unload -w " & quoted form of agent_plist & " || true; fi;"
set unload_service to "launchctl unload -w " & daemon_plist & " || true;" set unload_service to "launchctl unload -w " & daemon_plist & " || true;"
@@ -20,7 +19,7 @@ on run {daemon_file, agent_file, user, cur_pid, source_dir}
set kickstart_agent to "if [ -n \"$uid\" ]; then launchctl kickstart -k gui/$uid/$agent_label 2>/dev/null || launchctl kickstart -k user/$uid/$agent_label 2>/dev/null || true; fi;" set kickstart_agent to "if [ -n \"$uid\" ]; then launchctl kickstart -k gui/$uid/$agent_label 2>/dev/null || launchctl kickstart -k user/$uid/$agent_label 2>/dev/null || true; fi;"
set load_agent to agent_label_cmd & bootstrap_agent & kickstart_agent set load_agent to agent_label_cmd & bootstrap_agent & kickstart_agent
set sh to "set -e;" & check_source & resolve_uid & unload_agent & unload_service & kill_others & copy_files & write_daemon_plist & write_agent_plist & load_service & load_agent set sh to "set -e;" & resolve_uid & unload_agent & unload_service & kill_others & copy_files & write_daemon_plist & write_agent_plist & load_service & load_agent
do shell script sh with prompt "RustDesk wants to update itself" with administrator privileges do shell script sh with prompt "RustDesk wants to update itself" with administrator privileges
end run end run

View File

@@ -560,9 +560,10 @@ impl Connection {
match data { match data {
ipc::Data::Authorize => { ipc::Data::Authorize => {
conn.require_2fa.take(); conn.require_2fa.take();
if !conn.send_logon_response_and_keep_alive().await { if !conn.connect_port_forward_if_needed().await {
break; break;
} }
conn.send_logon_response().await;
if conn.port_forward_socket.is_some() { if conn.port_forward_socket.is_some() {
break; break;
} }
@@ -1357,10 +1358,9 @@ impl Connection {
if self.port_forward_socket.is_some() { if self.port_forward_socket.is_some() {
return true; return true;
} }
let Some(login_request::Union::PortForward(pf)) = self.lr.union.as_ref() else { let Some(login_request::Union::PortForward(mut pf)) = self.lr.union.clone() else {
return true; return true;
}; };
let mut pf = pf.clone();
let (mut addr, is_rdp) = Self::normalize_port_forward_target(&mut pf); let (mut addr, is_rdp) = Self::normalize_port_forward_target(&mut pf);
self.port_forward_address = addr.clone(); self.port_forward_address = addr.clone();
match timeout(3000, TcpStream::connect(&addr)).await { match timeout(3000, TcpStream::connect(&addr)).await {
@@ -1368,25 +1368,12 @@ impl Connection {
self.port_forward_socket = Some(Framed::new(sock, BytesCodec::new())); self.port_forward_socket = Some(Framed::new(sock, BytesCodec::new()));
true true
} }
Ok(Err(e)) => { _ => {
log::warn!("Port forward connect failed for {}: {}", addr, e);
if is_rdp { if is_rdp {
addr = "RDP".to_owned(); addr = "RDP".to_owned();
} }
self.send_login_error(format!( self.send_login_error(format!(
"Failed to access remote {}. Please make sure it is reachable/open.", "Failed to access remote {}, please make sure if it is open",
addr
))
.await;
false
}
Err(e) => {
log::warn!("Port forward connect timed out for {}: {}", addr, e);
if is_rdp {
addr = "RDP".to_owned();
}
self.send_login_error(format!(
"Failed to access remote {}. Please make sure it is reachable/open.",
addr addr
)) ))
.await; .await;
@@ -1395,11 +1382,9 @@ impl Connection {
} }
} }
// Returns whether this connection should be kept alive. async fn send_logon_response(&mut self) {
// `true` does not necessarily mean authorization succeeded (e.g. REQUIRE_2FA case).
async fn send_logon_response_and_keep_alive(&mut self) -> bool {
if self.authorized { if self.authorized {
return true; return;
} }
if self.require_2fa.is_some() && !self.is_recent_session(true) && !self.from_switch { if self.require_2fa.is_some() && !self.is_recent_session(true) && !self.from_switch {
self.require_2fa.as_ref().map(|totp| { self.require_2fa.as_ref().map(|totp| {
@@ -1430,11 +1415,7 @@ impl Connection {
} }
}); });
self.send_login_error(crate::client::REQUIRE_2FA).await; self.send_login_error(crate::client::REQUIRE_2FA).await;
// Keep the connection alive so the client can continue with 2FA. return;
return true;
}
if !self.connect_port_forward_if_needed().await {
return false;
} }
self.authorized = true; self.authorized = true;
let (conn_type, auth_conn_type) = if self.file_transfer.is_some() { let (conn_type, auth_conn_type) = if self.file_transfer.is_some() {
@@ -1557,7 +1538,7 @@ impl Connection {
res.set_peer_info(pi); res.set_peer_info(pi);
msg_out.set_login_response(res); msg_out.set_login_response(res);
self.send(msg_out).await; self.send(msg_out).await;
return true; return;
} }
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
if self.is_remote() { if self.is_remote() {
@@ -1580,7 +1561,7 @@ impl Connection {
let mut msg_out = Message::new(); let mut msg_out = Message::new();
msg_out.set_login_response(res); msg_out.set_login_response(res);
self.send(msg_out).await; self.send(msg_out).await;
return true; return;
} }
} }
#[allow(unused_mut)] #[allow(unused_mut)]
@@ -1734,7 +1715,6 @@ impl Connection {
self.try_sub_monitor_services(); self.try_sub_monitor_services();
} }
} }
true
} }
fn try_sub_camera_displays(&mut self) { fn try_sub_camera_displays(&mut self) {
@@ -1877,7 +1857,6 @@ impl Connection {
port_forward: self.port_forward_address.clone(), port_forward: self.port_forward_address.clone(),
peer_id, peer_id,
name, name,
avatar: self.lr.avatar.clone(),
authorized, authorized,
keyboard: self.keyboard, keyboard: self.keyboard,
clipboard: self.clipboard, clipboard: self.clipboard,
@@ -2275,7 +2254,9 @@ impl Connection {
// `is_logon_ui()` is a fallback for logon UI detection on Windows. // `is_logon_ui()` is a fallback for logon UI detection on Windows.
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
let is_logon = || { let is_logon = || {
crate::platform::is_prelogin() || crate::platform::is_locked() || { crate::platform::is_prelogin()
|| crate::platform::is_locked()
|| {
match crate::platform::is_logon_ui() { match crate::platform::is_logon_ui() {
Ok(result) => result, Ok(result) => result,
Err(e) => { Err(e) => {
@@ -2314,9 +2295,10 @@ impl Connection {
if err_msg.is_empty() { if err_msg.is_empty() {
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
self.linux_headless_handle.wait_desktop_cm_ready().await; self.linux_headless_handle.wait_desktop_cm_ready().await;
if !self.send_logon_response_and_keep_alive().await { if !self.connect_port_forward_if_needed().await {
return false; return false;
} }
self.send_logon_response().await;
self.try_start_cm(lr.my_id.clone(), lr.my_name.clone(), self.authorized); self.try_start_cm(lr.my_id.clone(), lr.my_name.clone(), self.authorized);
} else { } else {
self.send_login_error(err_msg).await; self.send_login_error(err_msg).await;
@@ -2352,9 +2334,10 @@ impl Connection {
if err_msg.is_empty() { if err_msg.is_empty() {
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
self.linux_headless_handle.wait_desktop_cm_ready().await; self.linux_headless_handle.wait_desktop_cm_ready().await;
if !self.send_logon_response_and_keep_alive().await { if !self.connect_port_forward_if_needed().await {
return false; return false;
} }
self.send_logon_response().await;
self.try_start_cm(lr.my_id, lr.my_name, self.authorized); self.try_start_cm(lr.my_id, lr.my_name, self.authorized);
} else { } else {
self.send_login_error(err_msg).await; self.send_login_error(err_msg).await;
@@ -2372,9 +2355,10 @@ impl Connection {
self.update_failure(failure, true, 1); self.update_failure(failure, true, 1);
self.require_2fa.take(); self.require_2fa.take();
raii::AuthedConnID::set_session_2fa(self.session_key()); raii::AuthedConnID::set_session_2fa(self.session_key());
if !self.send_logon_response_and_keep_alive().await { if !self.connect_port_forward_if_needed().await {
return false; return false;
} }
self.send_logon_response().await;
self.try_start_cm( self.try_start_cm(
self.lr.my_id.to_owned(), self.lr.my_id.to_owned(),
self.lr.my_name.to_owned(), self.lr.my_name.to_owned(),
@@ -2425,9 +2409,10 @@ impl Connection {
if let Some((_instant, uuid_old)) = uuid_old { if let Some((_instant, uuid_old)) = uuid_old {
if uuid == uuid_old { if uuid == uuid_old {
self.from_switch = true; self.from_switch = true;
if !self.send_logon_response_and_keep_alive().await { if !self.connect_port_forward_if_needed().await {
return false; return false;
} }
self.send_logon_response().await;
self.try_start_cm( self.try_start_cm(
lr.my_id.clone(), lr.my_id.clone(),
lr.my_name.clone(), lr.my_name.clone(),
@@ -5393,8 +5378,9 @@ mod raii {
} }
pub fn check_wake_lock_on_setting_changed() { pub fn check_wake_lock_on_setting_changed() {
let current = let current = config::Config::get_bool_option(
config::Config::get_bool_option(keys::OPTION_KEEP_AWAKE_DURING_INCOMING_SESSIONS); keys::OPTION_KEEP_AWAKE_DURING_INCOMING_SESSIONS,
);
let cached = *WAKELOCK_KEEP_AWAKE_OPTION.lock().unwrap(); let cached = *WAKELOCK_KEEP_AWAKE_OPTION.lock().unwrap();
if cached != Some(current) { if cached != Some(current) {
Self::check_wake_lock(); Self::check_wake_lock();

View File

@@ -57,11 +57,6 @@ div.icon {
font-weight: bold; font-weight: bold;
} }
img.icon {
size: 96px;
border-radius: 8px;
}
div.id { div.id {
@ELLIPSIS; @ELLIPSIS;
color: color(green-blue); color: color(green-blue);

View File

@@ -28,7 +28,6 @@ impl InvokeUiCM for SciterHandler {
client.port_forward.clone(), client.port_forward.clone(),
client.peer_id.clone(), client.peer_id.clone(),
client.name.clone(), client.name.clone(),
client.avatar.clone(),
client.authorized, client.authorized,
client.keyboard, client.keyboard,
client.clipboard, client.clipboard,

View File

@@ -42,11 +42,9 @@ class Body: Reactor.Component
return <div .content style="size:*"> return <div .content style="size:*">
<div .left-panel> <div .left-panel>
<div .icon-and-id> <div .icon-and-id>
{c.avatar ?
<img .icon src={c.avatar} /> :
<div .icon style={"background: " + string2RGB(c.name, 1)}> <div .icon style={"background: " + string2RGB(c.name, 1)}>
{c.name[0].toUpperCase()} {c.name[0].toUpperCase()}
</div>} </div>
<div> <div>
<div .id style="font-weight: bold; font-size: 1.2em;">{c.name}</div> <div .id style="font-weight: bold; font-size: 1.2em;">{c.name}</div>
<div .id>({c.peer_id})</div> <div .id>({c.peer_id})</div>
@@ -368,7 +366,7 @@ function bring_to_top(idx=-1) {
} }
} }
handler.addConnection = function(id, is_file_transfer, is_view_camera, is_terminal, port_forward, peer_id, name, avatar, authorized, keyboard, clipboard, audio, file, restart, recording, block_input) { handler.addConnection = function(id, is_file_transfer, is_view_camera, is_terminal, port_forward, peer_id, name, authorized, keyboard, clipboard, audio, file, restart, recording, block_input) {
stdout.println("new connection #" + id + ": " + peer_id); stdout.println("new connection #" + id + ": " + peer_id);
var conn; var conn;
connections.map(function(c) { connections.map(function(c) {
@@ -387,7 +385,6 @@ handler.addConnection = function(id, is_file_transfer, is_view_camera, is_termin
conn = { conn = {
id: id, is_file_transfer: is_file_transfer, is_view_camera: is_view_camera, is_terminal: is_terminal, peer_id: peer_id, id: id, is_file_transfer: is_file_transfer, is_view_camera: is_view_camera, is_terminal: is_terminal, peer_id: peer_id,
port_forward: port_forward, port_forward: port_forward,
avatar: avatar,
name: name, authorized: authorized, time: new Date(), now: new Date(), name: name, authorized: authorized, time: new Date(), now: new Date(),
keyboard: keyboard, clipboard: clipboard, msgs: [], unreaded: 0, keyboard: keyboard, clipboard: clipboard, msgs: [], unreaded: 0,
audio: audio, file: file, restart: restart, recording: recording, audio: audio, file: file, restart: restart, recording: recording,

View File

@@ -1451,9 +1451,6 @@ function set_local_user_info(user) {
if (user.display_name) { if (user.display_name) {
user_info.display_name = user.display_name; user_info.display_name = user.display_name;
} }
if (user.avatar) {
user_info.avatar = user.avatar;
}
if (user.status) { if (user.status) {
user_info.status = user.status; user_info.status = user.status;
} }

View File

@@ -134,7 +134,6 @@ pub struct Client {
pub is_terminal: bool, pub is_terminal: bool,
pub port_forward: String, pub port_forward: String,
pub name: String, pub name: String,
pub avatar: String,
pub peer_id: String, pub peer_id: String,
pub keyboard: bool, pub keyboard: bool,
pub clipboard: bool, pub clipboard: bool,
@@ -221,7 +220,6 @@ impl<T: InvokeUiCM> ConnectionManager<T> {
port_forward: String, port_forward: String,
peer_id: String, peer_id: String,
name: String, name: String,
avatar: String,
authorized: bool, authorized: bool,
keyboard: bool, keyboard: bool,
clipboard: bool, clipboard: bool,
@@ -242,7 +240,6 @@ impl<T: InvokeUiCM> ConnectionManager<T> {
is_terminal, is_terminal,
port_forward, port_forward,
name: name.clone(), name: name.clone(),
avatar,
peer_id: peer_id.clone(), peer_id: peer_id.clone(),
keyboard, keyboard,
clipboard, clipboard,
@@ -503,9 +500,9 @@ impl<T: InvokeUiCM> IpcTaskRunner<T> {
} }
Ok(Some(data)) => { Ok(Some(data)) => {
match data { match data {
Data::Login{id, is_file_transfer, is_view_camera, is_terminal, port_forward, peer_id, name, avatar, authorized, keyboard, clipboard, audio, file, file_transfer_enabled: _file_transfer_enabled, restart, recording, block_input, from_switch} => { Data::Login{id, is_file_transfer, is_view_camera, is_terminal, port_forward, peer_id, name, authorized, keyboard, clipboard, audio, file, file_transfer_enabled: _file_transfer_enabled, restart, recording, block_input, from_switch} => {
log::debug!("conn_id: {}", id); log::debug!("conn_id: {}", id);
self.cm.add_connection(id, is_file_transfer, is_view_camera, is_terminal, port_forward, peer_id, name, avatar, authorized, keyboard, clipboard, audio, file, restart, recording, block_input, from_switch, self.tx.clone()); self.cm.add_connection(id, is_file_transfer, is_view_camera, is_terminal, port_forward, peer_id, name, authorized, keyboard, clipboard, audio, file, restart, recording, block_input, from_switch, self.tx.clone());
self.conn_id = id; self.conn_id = id;
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
{ {
@@ -826,7 +823,6 @@ pub async fn start_listen<T: InvokeUiCM>(
port_forward, port_forward,
peer_id, peer_id,
name, name,
avatar,
authorized, authorized,
keyboard, keyboard,
clipboard, clipboard,
@@ -847,7 +843,6 @@ pub async fn start_listen<T: InvokeUiCM>(
port_forward, port_forward,
peer_id, peer_id,
name, name,
avatar,
authorized, authorized,
keyboard, keyboard,
clipboard, clipboard,

View File

@@ -245,20 +245,7 @@ pub fn get_builtin_option(key: &str) -> String {
#[inline] #[inline]
pub fn set_local_option(key: String, value: String) { pub fn set_local_option(key: String, value: String) {
LocalConfig::set_option(key.clone(), value); LocalConfig::set_option(key.clone(), value.clone());
}
/// Resolve relative avatar path (e.g. "/avatar/xxx") to absolute URL
/// by prepending the API server address.
pub fn resolve_avatar_url(avatar: String) -> String {
let avatar = avatar.trim().to_owned();
if avatar.starts_with('/') {
let api_server = get_api_server();
if !api_server.is_empty() {
return format!("{}{}", api_server.trim_end_matches('/'), avatar);
}
}
avatar
} }
#[cfg(any(target_os = "android", target_os = "ios", feature = "flutter"))] #[cfg(any(target_os = "android", target_os = "ios", feature = "flutter"))]

View File

@@ -1289,7 +1289,8 @@ impl<T: InvokeUiSession> Session<T> {
drop(connection_round_state_lock); drop(connection_round_state_lock);
let cloned = self.clone(); let cloned = self.clone();
*cloned.audit_guid.lock().unwrap() = String::new();
*cloned.last_audit_note.lock().unwrap() = String::new();
// override only if true // override only if true
if true == force_relay { if true == force_relay {
self.lc.write().unwrap().force_relay = true; self.lc.write().unwrap().force_relay = true;
@@ -1812,9 +1813,6 @@ impl<T: InvokeUiSession> Interface for Session<T> {
); );
} }
self.update_privacy_mode(); self.update_privacy_mode();
// Clear audit_guid when connection is established successfully
*self.audit_guid.lock().unwrap() = String::new();
*self.last_audit_note.lock().unwrap() = String::new();
// Save recent peers, then push event to flutter. So flutter can refresh peer page. // Save recent peers, then push event to flutter. So flutter can refresh peer page.
self.lc.write().unwrap().handle_peer_info(&pi); self.lc.write().unwrap().handle_peer_info(&pi);
self.set_peer_info(&pi); self.set_peer_info(&pi);