diff --git a/flutter/lib/common.dart b/flutter/lib/common.dart
index 21dc427cc..3138dd14d 100644
--- a/flutter/lib/common.dart
+++ b/flutter/lib/common.dart
@@ -112,18 +112,21 @@ class ColorThemeExtension extends ThemeExtension<ColorThemeExtension> {
     required this.border2,
     required this.highlight,
     required this.drag_indicator,
+    required this.shadow,
   });
 
   final Color? border;
   final Color? border2;
   final Color? highlight;
   final Color? drag_indicator;
+  final Color? shadow;
 
   static final light = ColorThemeExtension(
     border: Color(0xFFCCCCCC),
     border2: Color(0xFFBBBBBB),
     highlight: Color(0xFFE5E5E5),
     drag_indicator: Colors.grey[800],
+    shadow: Colors.black,
   );
 
   static final dark = ColorThemeExtension(
@@ -131,19 +134,24 @@ class ColorThemeExtension extends ThemeExtension<ColorThemeExtension> {
     border2: Color(0xFFE5E5E5),
     highlight: Color(0xFF3F3F3F),
     drag_indicator: Colors.grey,
+    shadow: Colors.grey,
   );
 
   @override
-  ThemeExtension<ColorThemeExtension> copyWith(
-      {Color? border,
-      Color? border2,
-      Color? highlight,
-      Color? drag_indicator}) {
+  ThemeExtension<ColorThemeExtension> copyWith({
+    Color? border,
+    Color? border2,
+    Color? highlight,
+    Color? drag_indicator,
+    Color? shadow,
+  }) {
     return ColorThemeExtension(
-        border: border ?? this.border,
-        border2: border2 ?? this.border2,
-        highlight: highlight ?? this.highlight,
-        drag_indicator: drag_indicator ?? this.drag_indicator);
+      border: border ?? this.border,
+      border2: border2 ?? this.border2,
+      highlight: highlight ?? this.highlight,
+      drag_indicator: drag_indicator ?? this.drag_indicator,
+      shadow: shadow ?? this.shadow,
+    );
   }
 
   @override
@@ -157,6 +165,7 @@ class ColorThemeExtension extends ThemeExtension<ColorThemeExtension> {
       border2: Color.lerp(border2, other.border2, t),
       highlight: Color.lerp(highlight, other.highlight, t),
       drag_indicator: Color.lerp(drag_indicator, other.drag_indicator, t),
+      shadow: Color.lerp(shadow, other.shadow, t),
     );
   }
 }
diff --git a/flutter/lib/desktop/widgets/remote_menubar.dart b/flutter/lib/desktop/widgets/remote_menubar.dart
index 6cfbf4675..8a1b2fbd6 100644
--- a/flutter/lib/desktop/widgets/remote_menubar.dart
+++ b/flutter/lib/desktop/widgets/remote_menubar.dart
@@ -372,6 +372,7 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
           offstage: _dragging.isTrue,
           child: Material(
             elevation: _MenubarTheme.elevation,
+            shadowColor: MyTheme.color(context).shadow,
             child: _DraggableShowHide(
               dragging: _dragging,
               fractionX: _fractionX,
@@ -421,6 +422,7 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
       children: [
         Material(
           elevation: _MenubarTheme.elevation,
+          shadowColor: MyTheme.color(context).shadow,
           borderRadius: BorderRadius.all(Radius.circular(4.0)),
           color: Theme.of(context)
               .menuBarTheme
diff --git a/flutter/lib/models/file_model.dart b/flutter/lib/models/file_model.dart
index 4170a5461..ebb3339ef 100644
--- a/flutter/lib/models/file_model.dart
+++ b/flutter/lib/models/file_model.dart
@@ -4,7 +4,7 @@ import 'dart:io';
 
 import 'package:flutter/material.dart';
 import 'package:flutter_hbb/common.dart';
-import 'package:flutter_hbb/consts.dart';
+import 'package:flutter_hbb/utils/event_loop.dart';
 import 'package:get/get.dart';
 import 'package:path/path.dart' as path;
 
@@ -45,6 +45,7 @@ class FileModel {
 
   late final GetSessionID getSessionID;
   String get sessionID => getSessionID();
+  late final FileDialogEventLoop evtLoop;
 
   FileModel(this.parent) {
     getSessionID = () => parent.target?.id ?? "";
@@ -64,14 +65,17 @@ class FileModel {
         jobController: jobController,
         fileFetcher: fileFetcher,
         getOtherSideDirectoryData: () => localController.directoryData());
+    evtLoop = FileDialogEventLoop();
   }
 
   Future<void> onReady() async {
+    await evtLoop.onReady();
     await localController.onReady();
     await remoteController.onReady();
   }
 
   Future<void> close() async {
+    await evtLoop.close();
     parent.target?.dialogManager.dismissAll();
     await localController.close();
     await remoteController.close();
@@ -90,14 +94,26 @@ class FileModel {
     fileFetcher.tryCompleteTask(evt['value'], evt['is_local']);
   }
 
-  void overrideFileConfirm(Map<String, dynamic> evt) async {
-    final resp = await showFileConfirmDialog(
-        translate("Overwrite"), "${evt['read_path']}", true);
+  Future<void> postOverrideFileConfirm(Map<String, dynamic> evt) async {
+    evtLoop.pushEvent(
+        _FileDialogEvent(WeakReference(this), FileDialogType.overwrite, evt));
+  }
+
+  Future<void> overrideFileConfirm(Map<String, dynamic> evt,
+      {bool? overrideConfirm, bool skip = false}) async {
+    // If `skip == true`, it means to skip this file without showing dialog.
+    // Because `resp` may be null after the user operation or the last remembered operation,
+    // and we should distinguish them.
+    final resp = overrideConfirm ??
+        (!skip
+            ? await showFileConfirmDialog(translate("Overwrite"),
+                "${evt['read_path']}", true, evt['is_identical'] == "true")
+            : null);
     final id = int.tryParse(evt['id']) ?? 0;
     if (false == resp) {
       final jobIndex = jobController.getJob(id);
       if (jobIndex != -1) {
-        jobController.cancelJob(id);
+        await jobController.cancelJob(id);
         final job = jobController.jobTable[jobIndex];
         job.state = JobState.done;
         jobController.jobTable.refresh();
@@ -111,7 +127,11 @@ class FileModel {
         // overwrite
         need_override = true;
       }
-      bind.sessionSetConfirmOverrideFile(
+      // Update the loop config.
+      if (fileConfirmCheckboxRemember) {
+        evtLoop.setSkip(!need_override);
+      }
+      await bind.sessionSetConfirmOverrideFile(
           id: sessionID,
           actId: id,
           fileNum: int.parse(evt['file_num']),
@@ -119,12 +139,16 @@ class FileModel {
           remember: fileConfirmCheckboxRemember,
           isUpload: evt['is_upload'] == "true");
     }
+    // Update the loop config.
+    if (fileConfirmCheckboxRemember) {
+      evtLoop.setOverrideConfirm(resp);
+    }
   }
 
   bool fileConfirmCheckboxRemember = false;
 
   Future<bool?> showFileConfirmDialog(
-      String title, String content, bool showCheckbox) async {
+      String title, String content, bool showCheckbox, bool isIdentical) async {
     fileConfirmCheckboxRemember = false;
     return await parent.target?.dialogManager.show<bool?>(
         (setState, Function(bool? v) close) {
@@ -149,6 +173,17 @@ class FileModel {
                   style: const TextStyle(fontWeight: FontWeight.bold)),
               const SizedBox(height: 5),
               Text(content),
+              Offstage(
+                offstage: !isIdentical,
+                child: Column(
+                  mainAxisSize: MainAxisSize.min,
+                  children: [
+                    const SizedBox(height: 12),
+                    Text(translate("identical_file_tip"),
+                        style: const TextStyle(fontWeight: FontWeight.w500))
+                  ],
+                ),
+              ),
               showCheckbox
                   ? CheckboxListTile(
                       contentPadding: const EdgeInsets.all(0),
@@ -377,12 +412,12 @@ class FileController {
           }
           job.totalSize = totalSize;
           job.fileCount = fileCount;
-          debugPrint("update receive details:${fd.path}");
+          debugPrint("update receive details: ${fd.path}");
           jobController.jobTable.refresh();
         }
       } else if (options.value.home.isEmpty) {
         options.value.home = fd.path;
-        debugPrint("init remote home:${fd.path}");
+        debugPrint("init remote home: ${fd.path}");
         directory.value = fd;
       }
     } catch (e) {
@@ -414,7 +449,7 @@ class FileController {
           includeHidden: showHidden,
           isRemote: isRemoteToLocal);
       debugPrint(
-          "path:${from.path}, toPath:$toPath, to:${PathUtil.join(toPath, from.name, isWindows)}");
+          "path: ${from.path}, toPath: $toPath, to: ${PathUtil.join(toPath, from.name, isWindows)}");
     }
   }
 
@@ -639,7 +674,7 @@ class JobController {
         jobTable.refresh();
       }
     } catch (e) {
-      debugPrint("Failed to tryUpdateJobProgress,evt:${evt.toString()}");
+      debugPrint("Failed to tryUpdateJobProgress, evt: ${evt.toString()}");
     }
   }
 
@@ -677,8 +712,8 @@ class JobController {
     debugPrint("jobError $evt");
   }
 
-  void cancelJob(int id) async {
-    bind.sessionCancelJob(id: sessionID, actId: id);
+  Future<void> cancelJob(int id) async {
+    await bind.sessionCancelJob(id: sessionID, actId: id);
   }
 
   void loadLastJob(Map<String, dynamic> evt) {
@@ -806,7 +841,7 @@ class FileFetcher {
     Timer(Duration(seconds: 2), () {
       tasks.remove(path);
       if (c.isCompleted) return;
-      c.completeError("Failed to read dir,timeout");
+      c.completeError("Failed to read dir, timeout");
     });
     return c.future;
   }
@@ -822,7 +857,7 @@ class FileFetcher {
     Timer(Duration(seconds: 2), () {
       tasks.remove(actID);
       if (c.isCompleted) return;
-      c.completeError("Failed to read dir,timeout");
+      c.completeError("Failed to read dir, timeout");
     });
     return c.future;
   }
@@ -846,7 +881,7 @@ class FileFetcher {
         completer?.complete(fd);
       }
     } catch (e) {
-      debugPrint("tryCompleteJob err :$e");
+      debugPrint("tryCompleteJob err: $e");
     }
   }
 
@@ -1167,3 +1202,74 @@ List<Entry> _sortList(List<Entry> list, SortBy sortType, bool ascending) {
   }
   return [];
 }
+
+/// Define a general queue which can accepts different dialog type.
+///
+/// [Visibility]
+/// The `_FileDialogType` and `_DialogEvent` are invisible for other models.
+enum FileDialogType { overwrite, unknown }
+
+class _FileDialogEvent extends BaseEvent<FileDialogType, Map<String, dynamic>> {
+  WeakReference<FileModel> fileModel;
+  bool? _overrideConfirm;
+  bool _skip = false;
+
+  _FileDialogEvent(this.fileModel, super.type, super.data);
+
+  void setOverrideConfirm(bool? confirm) {
+    _overrideConfirm = confirm;
+  }
+
+  void setSkip(bool skip) {
+    _skip = skip;
+  }
+
+  @override
+  EventCallback<Map<String, dynamic>>? findCallback(FileDialogType type) {
+    final model = fileModel.target;
+    if (model == null) {
+      return null;
+    }
+    switch (type) {
+      case FileDialogType.overwrite:
+        return (data) async {
+          return await model.overrideFileConfirm(data,
+              overrideConfirm: _overrideConfirm, skip: _skip);
+        };
+      default:
+        debugPrint("Unknown event type: $type with $data");
+        return null;
+    }
+  }
+}
+
+class FileDialogEventLoop
+    extends BaseEventLoop<FileDialogType, Map<String, dynamic>> {
+  bool? _overrideConfirm;
+  bool _skip = false;
+
+  @override
+  Future<void> onPreConsume(
+      BaseEvent<FileDialogType, Map<String, dynamic>> evt) async {
+    var event = evt as _FileDialogEvent;
+    event.setOverrideConfirm(_overrideConfirm);
+    event.setSkip(_skip);
+    debugPrint(
+        "FileDialogEventLoop: consuming<jobId: ${evt.data['id']} overrideConfirm: $_overrideConfirm, skip: $_skip>");
+  }
+
+  @override
+  Future<void> onEventsClear() {
+    _overrideConfirm = null;
+    _skip = false;
+    return super.onEventsClear();
+  }
+
+  void setOverrideConfirm(bool? confirm) {
+    _overrideConfirm = confirm;
+  }
+
+  void setSkip(bool skip) {
+    _skip = skip;
+  }
+}
diff --git a/flutter/lib/models/model.dart b/flutter/lib/models/model.dart
index 94e28ea21..fd88a5332 100644
--- a/flutter/lib/models/model.dart
+++ b/flutter/lib/models/model.dart
@@ -173,7 +173,7 @@ class FfiModel with ChangeNotifier {
       } else if (name == 'job_error') {
         parent.target?.fileModel.jobController.jobError(evt);
       } else if (name == 'override_file_confirm') {
-        parent.target?.fileModel.overrideFileConfirm(evt);
+        parent.target?.fileModel.postOverrideFileConfirm(evt);
       } else if (name == 'load_last_job') {
         parent.target?.fileModel.jobController.loadLastJob(evt);
       } else if (name == 'update_folder_files') {
diff --git a/flutter/lib/utils/event_loop.dart b/flutter/lib/utils/event_loop.dart
new file mode 100644
index 000000000..a982cf9e2
--- /dev/null
+++ b/flutter/lib/utils/event_loop.dart
@@ -0,0 +1,79 @@
+import 'dart:async';
+
+import 'package:flutter/foundation.dart';
+
+typedef EventCallback<Data> = Future<dynamic> Function(Data data);
+
+abstract class BaseEvent<EventType, Data> {
+  EventType type;
+  Data data;
+
+  /// Constructor.
+  BaseEvent(this.type, this.data);
+
+  /// Consume this event.
+  @visibleForTesting
+  Future<dynamic> consume() async {
+    final cb = findCallback(type);
+    if (cb == null) {
+      return null;
+    } else {
+      return cb(data);
+    }
+  }
+
+  EventCallback<Data>? findCallback(EventType type);
+}
+
+abstract class BaseEventLoop<EventType, Data> {
+  final List<BaseEvent<EventType, Data>> _evts = [];
+  Timer? _timer;
+
+  List<BaseEvent<EventType, Data>> get evts => _evts;
+
+  Future<void> onReady() async {
+    // Poll every 100ms.
+    _timer = Timer.periodic(Duration(milliseconds: 100), _handleTimer);
+  }
+
+  /// An Event is about to be consumed.
+  Future<void> onPreConsume(BaseEvent<EventType, Data> evt) async {}
+  /// An Event was consumed.
+  Future<void> onPostConsume(BaseEvent<EventType, Data> evt) async {}
+  /// Events are all handled and cleared.
+  Future<void> onEventsClear() async {}
+  /// Events start to consume.
+  Future<void> onEventsStartConsuming() async {}
+
+  Future<void> _handleTimer(Timer timer) async {
+      if (_evts.isEmpty) {
+        return;
+      }
+      timer.cancel();
+      _timer = null;
+      // Handle the logic.
+      await onEventsStartConsuming();
+      while (_evts.isNotEmpty) {
+        final evt = _evts.first;
+        _evts.remove(evt);
+        await onPreConsume(evt);
+        await evt.consume();
+        await onPostConsume(evt);
+      }
+      await onEventsClear();
+      // Now events are all processed.
+      _timer = Timer.periodic(Duration(milliseconds: 100), _handleTimer);
+  }
+
+  Future<void> close() async {
+    _timer?.cancel();
+  }
+
+  void pushEvent(BaseEvent<EventType, Data> evt) {
+    _evts.add(evt);
+  }
+
+  void clear() {
+    _evts.clear();
+  }
+}
diff --git a/libs/hbb_common/protos/message.proto b/libs/hbb_common/protos/message.proto
index be3a1e51e..0c29a5f19 100644
--- a/libs/hbb_common/protos/message.proto
+++ b/libs/hbb_common/protos/message.proto
@@ -301,6 +301,7 @@ message FileTransferDigest {
   uint64 last_modified = 3;
   uint64 file_size = 4;
   bool is_upload = 5;
+  bool is_identical = 6;
 }
 
 message FileTransferBlock {
diff --git a/libs/hbb_common/protos/rendezvous.proto b/libs/hbb_common/protos/rendezvous.proto
index 1ac60f3f3..d86226290 100644
--- a/libs/hbb_common/protos/rendezvous.proto
+++ b/libs/hbb_common/protos/rendezvous.proto
@@ -27,6 +27,7 @@ message PunchHole {
   bytes socket_addr = 1;
   string relay_server = 2;
   NatType nat_type = 3;
+  string request_region = 4;
 }
 
 message TestNatRequest {
@@ -51,6 +52,7 @@ message PunchHoleSent {
   string relay_server = 3;
   NatType nat_type = 4;
   string version = 5;
+  string request_region = 6;
 }
 
 message RegisterPk {
@@ -105,6 +107,7 @@ message RequestRelay {
   string licence_key = 6;
   ConnType conn_type = 7;
   string token = 8;
+  string request_region = 9;
 }
 
 message RelayResponse {
@@ -117,6 +120,7 @@ message RelayResponse {
   }
   string refuse_reason = 6;
   string version = 7;
+  string request_region = 8;
 }
 
 message SoftwareUpdate { string url = 1; }
@@ -128,6 +132,7 @@ message SoftwareUpdate { string url = 1; }
 message FetchLocalAddr { 
   bytes socket_addr = 1; 
   string relay_server = 2;
+  string request_region = 3;
 }
 
 message LocalAddr {
@@ -136,6 +141,7 @@ message LocalAddr {
   string relay_server = 3;
   string id = 4;
   string version = 5;
+  string request_region = 6;
 }
 
 message PeerDiscovery {
diff --git a/libs/hbb_common/src/config.rs b/libs/hbb_common/src/config.rs
index 4ce2cf07e..4c4f065ab 100644
--- a/libs/hbb_common/src/config.rs
+++ b/libs/hbb_common/src/config.rs
@@ -43,6 +43,7 @@ lazy_static::lazy_static! {
     static ref CONFIG: Arc<RwLock<Config>> = Arc::new(RwLock::new(Config::load()));
     static ref CONFIG2: Arc<RwLock<Config2>> = Arc::new(RwLock::new(Config2::load()));
     static ref LOCAL_CONFIG: Arc<RwLock<LocalConfig>> = Arc::new(RwLock::new(LocalConfig::load()));
+    pub static ref CONFIG_OIDC: Arc<RwLock<ConfigOidc>> = Arc::new(RwLock::new(ConfigOidc::load()));
     pub static ref ONLINE: Arc<Mutex<HashMap<String, i64>>> = Default::default();
     pub static ref PROD_RENDEZVOUS_SERVER: Arc<RwLock<String>> = Arc::new(RwLock::new(match option_env!("RENDEZVOUS_SERVER") {
         Some(key) if !key.is_empty() => key,
@@ -257,6 +258,35 @@ pub struct PeerInfoSerde {
     pub platform: String,
 }
 
+#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
+pub struct ConfigOidc {
+    #[serde(default)]
+    pub max_auth_count: usize,
+    #[serde(default)]
+    pub callback_url: String,
+    #[serde(default)]
+    pub providers: HashMap<String, ConfigOidcProvider>,
+}
+
+#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
+pub struct ConfigOidcProvider {
+    // seconds. 0 means never expires
+    #[serde(default)]
+    pub refresh_token_expires_in: u32,
+    #[serde(default)]
+    pub client_id: String,
+    #[serde(default)]
+    pub client_secret: String,
+    #[serde(default)]
+    pub issuer: Option<String>,
+    #[serde(default)]
+    pub authorization_endpoint: Option<String>,
+    #[serde(default)]
+    pub token_endpoint: Option<String>,
+    #[serde(default)]
+    pub userinfo_endpoint: Option<String>,
+}
+
 #[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
 pub struct TransferSerde {
     #[serde(default)]
@@ -1366,6 +1396,29 @@ impl UserDefaultConfig {
     }
 }
 
+impl ConfigOidc {
+    fn suffix() -> &'static str {
+        "_oidc"
+    }
+
+    fn load() -> Self {
+        Config::load_::<Self>(Self::suffix())._load_env()
+    }
+
+    fn _load_env(mut self) -> Self {
+        use std::env;
+        for (k, mut v) in &mut self.providers {
+            if let Ok(client_id) = env::var(format!("OIDC-{}-CLIENT-ID", k.to_uppercase())) {
+                v.client_id = client_id;
+            }
+            if let Ok(client_secret) = env::var(format!("OIDC-{}-CLIENT-SECRET", k.to_uppercase())) {
+                v.client_secret = client_secret;
+            }
+        }
+        self
+    }
+}
+
 #[cfg(test)]
 mod tests {
     use super::*;
diff --git a/libs/hbb_common/src/fs.rs b/libs/hbb_common/src/fs.rs
index ea54e113a..41160f49d 100644
--- a/libs/hbb_common/src/fs.rs
+++ b/libs/hbb_common/src/fs.rs
@@ -823,14 +823,19 @@ pub fn is_write_need_confirmation(
         let modified_time = metadata.modified()?;
         let remote_mt = Duration::from_secs(digest.last_modified);
         let local_mt = modified_time.duration_since(UNIX_EPOCH)?;
+        // [Note]
+        // We decide to give the decision whether to override the existing file to users,
+        // which obey the behavior of the file manager in our system.
+        let mut is_identical = false;
         if remote_mt == local_mt && digest.file_size == metadata.len() {
-            return Ok(DigestCheckResult::IsSame);
+            is_identical = true;
         }
         Ok(DigestCheckResult::NeedConfirm(FileTransferDigest {
             id: digest.id,
             file_num: digest.file_num,
             last_modified: local_mt.as_secs(),
             file_size: metadata.len(),
+            is_identical,
             ..Default::default()
         }))
     } else {
diff --git a/src/client/io_loop.rs b/src/client/io_loop.rs
index 1c7788193..c37f235c0 100644
--- a/src/client/io_loop.rs
+++ b/src/client/io_loop.rs
@@ -954,6 +954,7 @@ impl<T: InvokeUiSession> Remote<T> {
                                                 digest.file_num,
                                                 read_path,
                                                 true,
+                                                digest.is_identical
                                             );
                                         }
                                     }
@@ -997,6 +998,7 @@ impl<T: InvokeUiSession> Remote<T> {
                                                             digest.file_num,
                                                             write_path,
                                                             false,
+                                                            digest.is_identical
                                                         );
                                                     }
                                                 }
diff --git a/src/flutter.rs b/src/flutter.rs
index 354e418eb..2262df660 100644
--- a/src/flutter.rs
+++ b/src/flutter.rs
@@ -420,7 +420,7 @@ impl InvokeUiSession for FlutterHandler {
     // unused in flutter // TEST flutter
     fn confirm_delete_files(&self, _id: i32, _i: i32, _name: String) {}
 
-    fn override_file_confirm(&self, id: i32, file_num: i32, to: String, is_upload: bool) {
+    fn override_file_confirm(&self, id: i32, file_num: i32, to: String, is_upload: bool, is_identical: bool) {
         self.push_event(
             "override_file_confirm",
             vec![
@@ -428,6 +428,7 @@ impl InvokeUiSession for FlutterHandler {
                 ("file_num", &file_num.to_string()),
                 ("read_path", &to),
                 ("is_upload", &is_upload.to_string()),
+                ("is_identical", &is_identical.to_string())
             ],
         );
     }
diff --git a/src/lang/ca.rs b/src/lang/ca.rs
index b086cbb01..585b784a6 100644
--- a/src/lang/ca.rs
+++ b/src/lang/ca.rs
@@ -477,6 +477,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
         ("Empty Username", ""),
         ("Empty Password", ""),
         ("Me", ""),
+        ("identical_file_tip", ""),
         ("Show monitors in menu bar", ""),
     ].iter().cloned().collect();
 }
diff --git a/src/lang/cn.rs b/src/lang/cn.rs
index aee739f84..ef2d6f50b 100644
--- a/src/lang/cn.rs
+++ b/src/lang/cn.rs
@@ -477,6 +477,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
         ("Empty Username", ""),
         ("Empty Password", ""),
         ("Me", ""),
+        ("identical_file_tip", "此文件与对方的一致"),
         ("Show monitors in menu bar", ""),
     ].iter().cloned().collect();
 }
diff --git a/src/lang/cs.rs b/src/lang/cs.rs
index 9ebe4cff9..ac0e9e6bb 100644
--- a/src/lang/cs.rs
+++ b/src/lang/cs.rs
@@ -477,6 +477,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
         ("Empty Username", ""),
         ("Empty Password", ""),
         ("Me", ""),
+        ("identical_file_tip", ""),
         ("Show monitors in menu bar", ""),
     ].iter().cloned().collect();
 }
diff --git a/src/lang/da.rs b/src/lang/da.rs
index 478d7b925..14bcc5158 100644
--- a/src/lang/da.rs
+++ b/src/lang/da.rs
@@ -477,6 +477,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
         ("Empty Username", "Tom brugernavn"),
         ("Empty Password", "Tom adgangskode"),
         ("Me", "Mig"),
+        ("identical_file_tip", ""),
         ("Show monitors in menu bar", ""),
     ].iter().cloned().collect();
 }
diff --git a/src/lang/de.rs b/src/lang/de.rs
index 32f720f4b..94e9832f1 100644
--- a/src/lang/de.rs
+++ b/src/lang/de.rs
@@ -477,6 +477,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
         ("Empty Username", "Leerer Benutzername"),
         ("Empty Password", "Leeres Passwort"),
         ("Me", "Ich"),
+        ("identical_file_tip", ""),
         ("Show monitors in menu bar", ""),
     ].iter().cloned().collect();
 }
diff --git a/src/lang/el.rs b/src/lang/el.rs
index f62912cd4..6ab774b3c 100644
--- a/src/lang/el.rs
+++ b/src/lang/el.rs
@@ -477,6 +477,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
         ("Empty Username", "Κενό όνομα χρήστη"),
         ("Empty Password", "Κενός κωδικός πρόσβασης"),
         ("Me", ""),
+        ("identical_file_tip", ""),
         ("Show monitors in menu bar", ""),
     ].iter().cloned().collect();
 }
diff --git a/src/lang/en.rs b/src/lang/en.rs
index b44ff2e40..389909245 100644
--- a/src/lang/en.rs
+++ b/src/lang/en.rs
@@ -52,5 +52,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
         ("empty_favorite_tip", "No favorite peers yet?\nLet's find someone to connect with and add it to your favorites!"),
         ("empty_lan_tip", "Oh no, it looks like we haven't discovered any peers yet."),
         ("empty_address_book_tip", "Oh dear, it appears that there are currently no peers listed in your address book."),
+        ("identical_file_tip", "This file is identical with the peer's one."),
         ].iter().cloned().collect();
 }
diff --git a/src/lang/eo.rs b/src/lang/eo.rs
index 5403c8883..0a5930412 100644
--- a/src/lang/eo.rs
+++ b/src/lang/eo.rs
@@ -477,6 +477,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
         ("Empty Username", ""),
         ("Empty Password", ""),
         ("Me", ""),
+        ("identical_file_tip", ""),
         ("Show monitors in menu bar", ""),
     ].iter().cloned().collect();
 }
diff --git a/src/lang/es.rs b/src/lang/es.rs
index d7c753096..b144d6a8c 100644
--- a/src/lang/es.rs
+++ b/src/lang/es.rs
@@ -477,6 +477,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
         ("Empty Username", "Nombre de usuario vacío"),
         ("Empty Password", "Contraseña vacía"),
         ("Me", "Yo"),
+        ("identical_file_tip", ""),
         ("Show monitors in menu bar", ""),
     ].iter().cloned().collect();
 }
diff --git a/src/lang/fa.rs b/src/lang/fa.rs
index f3da03d21..3a9f58749 100644
--- a/src/lang/fa.rs
+++ b/src/lang/fa.rs
@@ -477,6 +477,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
         ("Empty Username", ""),
         ("Empty Password", ""),
         ("Me", ""),
+        ("identical_file_tip", ""),
         ("Show monitors in menu bar", ""),
     ].iter().cloned().collect();
 }
diff --git a/src/lang/fr.rs b/src/lang/fr.rs
index 06bc82bfe..3a28bbb9d 100644
--- a/src/lang/fr.rs
+++ b/src/lang/fr.rs
@@ -477,6 +477,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
         ("Empty Username", ""),
         ("Empty Password", ""),
         ("Me", ""),
+        ("identical_file_tip", ""),
         ("Show monitors in menu bar", ""),
     ].iter().cloned().collect();
 }
diff --git a/src/lang/hu.rs b/src/lang/hu.rs
index 73e3c41a7..2aa1558e5 100644
--- a/src/lang/hu.rs
+++ b/src/lang/hu.rs
@@ -477,6 +477,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
         ("Empty Username", ""),
         ("Empty Password", ""),
         ("Me", ""),
+        ("identical_file_tip", ""),
         ("Show monitors in menu bar", ""),
     ].iter().cloned().collect();
 }
diff --git a/src/lang/id.rs b/src/lang/id.rs
index 4c0fefd82..1927d582c 100644
--- a/src/lang/id.rs
+++ b/src/lang/id.rs
@@ -477,6 +477,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
         ("Empty Username", ""),
         ("Empty Password", ""),
         ("Me", ""),
+        ("identical_file_tip", ""),
         ("Show monitors in menu bar", ""),
     ].iter().cloned().collect();
 }
diff --git a/src/lang/it.rs b/src/lang/it.rs
index b0728ddfb..4ca577823 100644
--- a/src/lang/it.rs
+++ b/src/lang/it.rs
@@ -477,6 +477,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
         ("Empty Username", "Nome Utente Vuoto"),
         ("Empty Password", "Password Vuota"),
         ("Me", "Io"),
+        ("identical_file_tip", "Questo file è identico a quello del peer."),
         ("Show monitors in menu bar", "Mostra schermi nella barra di menù"),
     ].iter().cloned().collect();
 }
diff --git a/src/lang/ja.rs b/src/lang/ja.rs
index be6f337b0..31fa936ce 100644
--- a/src/lang/ja.rs
+++ b/src/lang/ja.rs
@@ -477,6 +477,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
         ("Empty Username", ""),
         ("Empty Password", ""),
         ("Me", ""),
+        ("identical_file_tip", ""),
         ("Show monitors in menu bar", ""),
     ].iter().cloned().collect();
 }
diff --git a/src/lang/ko.rs b/src/lang/ko.rs
index c0e729ba0..a857dd56f 100644
--- a/src/lang/ko.rs
+++ b/src/lang/ko.rs
@@ -477,6 +477,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
         ("Empty Username", ""),
         ("Empty Password", ""),
         ("Me", ""),
+        ("identical_file_tip", ""),
         ("Show monitors in menu bar", ""),
     ].iter().cloned().collect();
 }
diff --git a/src/lang/kz.rs b/src/lang/kz.rs
index 18fee1e00..4bcd4949d 100644
--- a/src/lang/kz.rs
+++ b/src/lang/kz.rs
@@ -477,6 +477,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
         ("Empty Username", ""),
         ("Empty Password", ""),
         ("Me", ""),
+        ("identical_file_tip", ""),
         ("Show monitors in menu bar", ""),
     ].iter().cloned().collect();
 }
diff --git a/src/lang/nl.rs b/src/lang/nl.rs
index 033e7b8d4..0dc7e6bb0 100644
--- a/src/lang/nl.rs
+++ b/src/lang/nl.rs
@@ -477,6 +477,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
         ("Empty Username", "Gebruikersnaam Leeg"),
         ("Empty Password", "Wachtwoord Leeg"),
         ("Me", ""),
+        ("identical_file_tip", ""),
         ("Show monitors in menu bar", ""),
     ].iter().cloned().collect();
 }
diff --git a/src/lang/pl.rs b/src/lang/pl.rs
index 8b256a4c2..8a8f51abf 100644
--- a/src/lang/pl.rs
+++ b/src/lang/pl.rs
@@ -477,6 +477,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
         ("Empty Username", ""),
         ("Empty Password", ""),
         ("Me", ""),
+        ("identical_file_tip", ""),
         ("Show monitors in menu bar", ""),
     ].iter().cloned().collect();
 }
diff --git a/src/lang/pt_PT.rs b/src/lang/pt_PT.rs
index d583daa9e..603dd0d59 100644
--- a/src/lang/pt_PT.rs
+++ b/src/lang/pt_PT.rs
@@ -477,6 +477,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
         ("Empty Username", ""),
         ("Empty Password", ""),
         ("Me", ""),
+        ("identical_file_tip", ""),
         ("Show monitors in menu bar", ""),
     ].iter().cloned().collect();
 }
diff --git a/src/lang/ptbr.rs b/src/lang/ptbr.rs
index 43ceb9a25..7eaaabd70 100644
--- a/src/lang/ptbr.rs
+++ b/src/lang/ptbr.rs
@@ -477,6 +477,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
         ("Empty Username", ""),
         ("Empty Password", ""),
         ("Me", ""),
+        ("identical_file_tip", ""),
         ("Show monitors in menu bar", ""),
     ].iter().cloned().collect();
 }
diff --git a/src/lang/ro.rs b/src/lang/ro.rs
index a9b82e788..0b26f382d 100644
--- a/src/lang/ro.rs
+++ b/src/lang/ro.rs
@@ -477,6 +477,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
         ("Empty Username", ""),
         ("Empty Password", ""),
         ("Me", ""),
+        ("identical_file_tip", ""),
         ("Show monitors in menu bar", ""),
     ].iter().cloned().collect();
 }
diff --git a/src/lang/ru.rs b/src/lang/ru.rs
index 1f5328638..7c17c2b46 100644
--- a/src/lang/ru.rs
+++ b/src/lang/ru.rs
@@ -477,6 +477,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
         ("Empty Username", ""),
         ("Empty Password", ""),
         ("Me", ""),
+        ("identical_file_tip", ""),
         ("Show monitors in menu bar", ""),
     ].iter().cloned().collect();
 }
diff --git a/src/lang/sk.rs b/src/lang/sk.rs
index c1ea44554..16c70f7c9 100644
--- a/src/lang/sk.rs
+++ b/src/lang/sk.rs
@@ -477,6 +477,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
         ("Empty Username", ""),
         ("Empty Password", ""),
         ("Me", ""),
+        ("identical_file_tip", ""),
         ("Show monitors in menu bar", ""),
     ].iter().cloned().collect();
 }
diff --git a/src/lang/sl.rs b/src/lang/sl.rs
index dd6050e57..90a6ee459 100755
--- a/src/lang/sl.rs
+++ b/src/lang/sl.rs
@@ -477,6 +477,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
         ("Empty Username", ""),
         ("Empty Password", ""),
         ("Me", ""),
+        ("identical_file_tip", ""),
         ("Show monitors in menu bar", ""),
     ].iter().cloned().collect();
 }
diff --git a/src/lang/sq.rs b/src/lang/sq.rs
index 1eedfdfad..25dd55f8d 100644
--- a/src/lang/sq.rs
+++ b/src/lang/sq.rs
@@ -477,6 +477,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
         ("Empty Username", ""),
         ("Empty Password", ""),
         ("Me", ""),
+        ("identical_file_tip", ""),
         ("Show monitors in menu bar", ""),
     ].iter().cloned().collect();
 }
diff --git a/src/lang/sr.rs b/src/lang/sr.rs
index d7c932cf0..b07cca46b 100644
--- a/src/lang/sr.rs
+++ b/src/lang/sr.rs
@@ -477,6 +477,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
         ("Empty Username", ""),
         ("Empty Password", ""),
         ("Me", ""),
+        ("identical_file_tip", ""),
         ("Show monitors in menu bar", ""),
     ].iter().cloned().collect();
 }
diff --git a/src/lang/sv.rs b/src/lang/sv.rs
index 49be9ef16..abc344820 100644
--- a/src/lang/sv.rs
+++ b/src/lang/sv.rs
@@ -477,6 +477,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
         ("Empty Username", ""),
         ("Empty Password", ""),
         ("Me", ""),
+        ("identical_file_tip", ""),
         ("Show monitors in menu bar", ""),
     ].iter().cloned().collect();
 }
diff --git a/src/lang/template.rs b/src/lang/template.rs
index 771ca4fcb..c0adc7a2a 100644
--- a/src/lang/template.rs
+++ b/src/lang/template.rs
@@ -477,6 +477,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
         ("Empty Username", ""),
         ("Empty Password", ""),
         ("Me", ""),
+        ("identical_file_tip", ""),
         ("Show monitors in menu bar", ""),
     ].iter().cloned().collect();
 }
diff --git a/src/lang/th.rs b/src/lang/th.rs
index c1c55927a..63456778b 100644
--- a/src/lang/th.rs
+++ b/src/lang/th.rs
@@ -477,6 +477,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
         ("Empty Username", ""),
         ("Empty Password", ""),
         ("Me", ""),
+        ("identical_file_tip", ""),
         ("Show monitors in menu bar", ""),
     ].iter().cloned().collect();
 }
diff --git a/src/lang/tr.rs b/src/lang/tr.rs
index c31789ee5..b68e2f3e8 100644
--- a/src/lang/tr.rs
+++ b/src/lang/tr.rs
@@ -477,6 +477,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
         ("Empty Username", ""),
         ("Empty Password", ""),
         ("Me", ""),
+        ("identical_file_tip", ""),
         ("Show monitors in menu bar", ""),
     ].iter().cloned().collect();
 }
diff --git a/src/lang/tw.rs b/src/lang/tw.rs
index b123c73bc..d314f5f6f 100644
--- a/src/lang/tw.rs
+++ b/src/lang/tw.rs
@@ -477,6 +477,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
         ("Empty Username", ""),
         ("Empty Password", ""),
         ("Me", ""),
+        ("identical_file_tip", ""),
         ("Show monitors in menu bar", ""),
     ].iter().cloned().collect();
 }
diff --git a/src/lang/ua.rs b/src/lang/ua.rs
index 5779617ba..62a7382ed 100644
--- a/src/lang/ua.rs
+++ b/src/lang/ua.rs
@@ -477,6 +477,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
         ("Empty Username", ""),
         ("Empty Password", ""),
         ("Me", ""),
+        ("identical_file_tip", ""),
         ("Show monitors in menu bar", ""),
     ].iter().cloned().collect();
 }
diff --git a/src/lang/vn.rs b/src/lang/vn.rs
index 55e294831..4f94c5a38 100644
--- a/src/lang/vn.rs
+++ b/src/lang/vn.rs
@@ -477,6 +477,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
         ("Empty Username", ""),
         ("Empty Password", ""),
         ("Me", ""),
+        ("identical_file_tip", ""),
         ("Show monitors in menu bar", ""),
     ].iter().cloned().collect();
 }
diff --git a/src/ui/file_transfer.tis b/src/ui/file_transfer.tis
index f69f6d323..6c741b31f 100644
--- a/src/ui/file_transfer.tis
+++ b/src/ui/file_transfer.tis
@@ -778,12 +778,14 @@ handler.confirmDeleteFiles = function(id, i, name) {
     });
 }
 
-handler.overrideFileConfirm = function(id, file_num, to, is_upload) {
+handler.overrideFileConfirm = function(id, file_num, to, is_upload, is_identical) {
   var jt = file_transfer.job_table;
+  var identical_msg = is_identical ? translate("identical_file_tip"): "";
   msgbox("custom-skip", "Confirm Write Strategy", "<div .form> \
-        <div>" + translate('Overwrite') + translate('files') + ".</div> \
+        <div>" + translate('Overwrite') + " " + translate('files') + ".</div> \
         <div>" + translate('This file exists, skip or overwrite this file?') + "</div> \
         <div.ellipsis style=\"font-weight: bold;\" .text>" + to + "</div> \
+        <div>" + identical_msg + "</div> \
         <div><button|checkbox(remember) {ts}>" + translate('Do this for all conflicts') + "</button></div> \
     </div>", "", function(res=null) {
       if (!res) {
diff --git a/src/ui/remote.rs b/src/ui/remote.rs
index ed16f1e0e..68decf955 100644
--- a/src/ui/remote.rs
+++ b/src/ui/remote.rs
@@ -197,10 +197,10 @@ impl InvokeUiSession for SciterHandler {
         self.call("confirmDeleteFiles", &make_args!(id, i, name));
     }
 
-    fn override_file_confirm(&self, id: i32, file_num: i32, to: String, is_upload: bool) {
+    fn override_file_confirm(&self, id: i32, file_num: i32, to: String, is_upload: bool, is_identical: bool) {
         self.call(
             "overrideFileConfirm",
-            &make_args!(id, file_num, to, is_upload),
+            &make_args!(id, file_num, to, is_upload, is_identical),
         );
     }
 
diff --git a/src/ui_session_interface.rs b/src/ui_session_interface.rs
index 11bcff925..b90f5fbea 100644
--- a/src/ui_session_interface.rs
+++ b/src/ui_session_interface.rs
@@ -872,7 +872,7 @@ pub trait InvokeUiSession: Send + Sync + Clone + 'static + Sized + Default {
         only_count: bool,
     );
     fn confirm_delete_files(&self, id: i32, i: i32, name: String);
-    fn override_file_confirm(&self, id: i32, file_num: i32, to: String, is_upload: bool);
+    fn override_file_confirm(&self, id: i32, file_num: i32, to: String, is_upload: bool, is_identical: bool);
     fn update_block_input_state(&self, on: bool);
     fn job_progress(&self, id: i32, file_num: i32, speed: f64, finished_size: f64);
     fn adapt_size(&self);