mirror of
https://github.com/rustdesk/rustdesk.git
synced 2025-01-18 07:43:01 +08:00
Merge translate mode
This commit is contained in:
commit
3b8032e5ec
16
Cargo.lock
generated
16
Cargo.lock
generated
@ -319,9 +319,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitvec"
|
name = "bitvec"
|
||||||
version = "1.0.0"
|
version = "1.0.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1489fcb93a5bb47da0462ca93ad252ad6af2145cce58d10d46a83931ba9f016b"
|
checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"funty",
|
"funty",
|
||||||
"radium",
|
"radium",
|
||||||
@ -2233,7 +2233,7 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "hwcodec"
|
name = "hwcodec"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/21pages/hwcodec#890204e0703a3d361fc7a45f035fe75c0575bb1d"
|
source = "git+https://github.com/21pages/hwcodec#91d1cd327c88490f917457072aeef0676ddb2be7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bindgen",
|
"bindgen",
|
||||||
"cc",
|
"cc",
|
||||||
@ -4039,6 +4039,7 @@ dependencies = [
|
|||||||
"simple_rc",
|
"simple_rc",
|
||||||
"sys-locale",
|
"sys-locale",
|
||||||
"sysinfo",
|
"sysinfo",
|
||||||
|
"system_shutdown",
|
||||||
"tray-item",
|
"tray-item",
|
||||||
"trayicon",
|
"trayicon",
|
||||||
"uuid",
|
"uuid",
|
||||||
@ -4659,6 +4660,15 @@ dependencies = [
|
|||||||
"version-compare 0.1.0",
|
"version-compare 0.1.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "system_shutdown"
|
||||||
|
version = "3.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "035e081d603551d8d78db27d2232913269c749ea67648c369100049820406a14"
|
||||||
|
dependencies = [
|
||||||
|
"winapi 0.3.9",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tap"
|
name = "tap"
|
||||||
version = "1.0.1"
|
version = "1.0.1"
|
||||||
|
@ -76,6 +76,7 @@ rdev = { git = "https://github.com/asur4s/rdev" }
|
|||||||
ctrlc = "3.2"
|
ctrlc = "3.2"
|
||||||
arboard = "2.0"
|
arboard = "2.0"
|
||||||
#minreq = { version = "2.4", features = ["punycode", "https-native"] }
|
#minreq = { version = "2.4", features = ["punycode", "https-native"] }
|
||||||
|
system_shutdown = "3.0.0"
|
||||||
|
|
||||||
[target.'cfg(target_os = "windows")'.dependencies]
|
[target.'cfg(target_os = "windows")'.dependencies]
|
||||||
#systray = { git = "https://github.com/open-trade/systray-rs" }
|
#systray = { git = "https://github.com/open-trade/systray-rs" }
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
<a href="#how-to-build-with-docker">Docker</a> •
|
<a href="#how-to-build-with-docker">Docker</a> •
|
||||||
<a href="#file-structure">Structure</a> •
|
<a href="#file-structure">Structure</a> •
|
||||||
<a href="#snapshot">Snapshot</a><br>
|
<a href="#snapshot">Snapshot</a><br>
|
||||||
[<a href="README-CS.md">česky</a>] | [<a href="README-ZH.md">中文</a>] | | [<a href="README-HU.md">Magyar</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-DE.md">Deutsch</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-ID.md">Indonesian</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-JP.md">日本語</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-IT.md">Italiano</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-AR.md">العربي</a>]<br>
|
[<a href="README-CS.md">česky</a>] | [<a href="README-ZH.md">中文</a>] | | [<a href="README-HU.md">Magyar</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-DE.md">Deutsch</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-ID.md">Indonesian</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-JP.md">日本語</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-IT.md">Italiano</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-AR.md">العربي</a>] | [<a href="README-VN.md">Tiếng Việt</a>]<br>
|
||||||
<b> لغتك الأم, <a href="https://github.com/rustdesk/doc.rustdesk.com">Doc</a> و <a href="https://github.com/rustdesk/rustdesk/tree/master/src/lang">RustDesk UI</a>, README نحن بحاجة إلى مساعدتك لترجمة هذا </b>
|
<b> لغتك الأم, <a href="https://github.com/rustdesk/doc.rustdesk.com">Doc</a> و <a href="https://github.com/rustdesk/rustdesk/tree/master/src/lang">RustDesk UI</a>, README نحن بحاجة إلى مساعدتك لترجمة هذا </b>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
<a href="#how-to-build-with-docker">Docker</a> •
|
<a href="#how-to-build-with-docker">Docker</a> •
|
||||||
<a href="#file-structure">Struktura</a> •
|
<a href="#file-structure">Struktura</a> •
|
||||||
<a href="#snapshot">Ukázky</a><br>
|
<a href="#snapshot">Ukázky</a><br>
|
||||||
[<a href="README-CS.md">česky</a>] | [<a href="README-ZH.md">中文</a>] | | [<a href="README-HU.md">Magyar</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-DE.md">Deutsch</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-ID.md">Indonesian</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-JP.md">日本語</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-IT.md">Italiano</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-AR.md">العربي</a>]<br>
|
[<a href="README-CS.md">česky</a>] | [<a href="README-ZH.md">中文</a>] | | [<a href="README-HU.md">Magyar</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-DE.md">Deutsch</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-ID.md">Indonesian</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-JP.md">日本語</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-IT.md">Italiano</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-AR.md">العربي</a>] | [<a href="README-VN.md">Tiếng Việt</a>]<br>
|
||||||
<b>Potřebujeme Vaši pomoc s překláním textů tohoto ČTIMNE, <a href="https://github.com/rustdesk/rustdesk/tree/master/src/lang">uživatelského rozhraní aplikace RustDesk</a> a <a href="https://github.com/rustdesk/doc.rustdesk.com">dokumentace k ní</a> do vašeho jazyka</b>
|
<b>Potřebujeme Vaši pomoc s překláním textů tohoto ČTIMNE, <a href="https://github.com/rustdesk/rustdesk/tree/master/src/lang">uživatelského rozhraní aplikace RustDesk</a> a <a href="https://github.com/rustdesk/doc.rustdesk.com">dokumentace k ní</a> do vašeho jazyka</b>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
<a href="#auf-docker-kompilieren">Docker</a> •
|
<a href="#auf-docker-kompilieren">Docker</a> •
|
||||||
<a href="#dateistruktur">Dateistruktur</a> •
|
<a href="#dateistruktur">Dateistruktur</a> •
|
||||||
<a href="#screenshots">Screenshots</a><br>
|
<a href="#screenshots">Screenshots</a><br>
|
||||||
[<a href="README-CS.md">česky</a>] | [<a href="README-ZH.md">中文</a>] | | [<a href="README-HU.md">Magyar</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-DE.md">Deutsch</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-ID.md">Indonesian</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-JP.md">日本語</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-IT.md">Italiano</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-AR.md">العربي</a>]<br>
|
[<a href="README-CS.md">česky</a>] | [<a href="README-ZH.md">中文</a>] | | [<a href="README-HU.md">Magyar</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-DE.md">Deutsch</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-ID.md">Indonesian</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-JP.md">日本語</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-IT.md">Italiano</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-AR.md">العربي</a>] | [<a href="README-VN.md">Tiếng Việt</a>]<br>
|
||||||
<b>Wir brauchen deine Hilfe um diese README Datei zu verbessern und aktualisieren</b>
|
<b>Wir brauchen deine Hilfe um diese README Datei zu verbessern und aktualisieren</b>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
<a href="#kiel-kompili-kun-docker">Docker</a> •
|
<a href="#kiel-kompili-kun-docker">Docker</a> •
|
||||||
<a href="#dosierstrukturo">Strukturo</a> •
|
<a href="#dosierstrukturo">Strukturo</a> •
|
||||||
<a href="#ekrankopio">Ekrankopio</a><br>
|
<a href="#ekrankopio">Ekrankopio</a><br>
|
||||||
[<a href="README-CS.md">česky</a>] | [<a href="README-ZH.md">中文</a>] | | [<a href="README-HU.md">Magyar</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-DE.md">Deutsch</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-ID.md">Indonesian</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-JP.md">日本語</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-IT.md">Italiano</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-AR.md">العربي</a>]<br>
|
[<a href="README-CS.md">česky</a>] | [<a href="README-ZH.md">中文</a>] | | [<a href="README-HU.md">Magyar</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-DE.md">Deutsch</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-ID.md">Indonesian</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-JP.md">日本語</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-IT.md">Italiano</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-AR.md">العربي</a>] | [<a href="README-VN.md">Tiếng Việt</a>]<br>
|
||||||
<b>Ni bezonas helpon traduki tiun README kaj <a href="https://github.com/rustdesk/rustdesk/tree/master/src/lang">la interfacon</a> al via denaska lingvo</b>
|
<b>Ni bezonas helpon traduki tiun README kaj <a href="https://github.com/rustdesk/rustdesk/tree/master/src/lang">la interfacon</a> al via denaska lingvo</b>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
<a href="#como-compilar-con-docker">Docker</a> •
|
<a href="#como-compilar-con-docker">Docker</a> •
|
||||||
<a href="#estructura-de-archivos">Estructura</a> •
|
<a href="#estructura-de-archivos">Estructura</a> •
|
||||||
<a href="#captura-de-pantalla">Captura de pantalla</a><br>
|
<a href="#captura-de-pantalla">Captura de pantalla</a><br>
|
||||||
[<a href="README-CS.md">česky</a>] | [<a href="README-ZH.md">中文</a>] | | [<a href="README-HU.md">Magyar</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-DE.md">Deutsch</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-ID.md">Indonesian</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-JP.md">日本語</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-IT.md">Italiano</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-AR.md">العربي</a>]<br>
|
[<a href="README-CS.md">česky</a>] | [<a href="README-ZH.md">中文</a>] | | [<a href="README-HU.md">Magyar</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-DE.md">Deutsch</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-ID.md">Indonesian</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-JP.md">日本語</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-IT.md">Italiano</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-AR.md">العربي</a>] | [<a href="README-VN.md">Tiếng Việt</a>]<br>
|
||||||
<b>Necesitamos tu ayuda para traducir este README a tu idioma</b>
|
<b>Necesitamos tu ayuda para traducir este README a tu idioma</b>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
<a dir="rtl" href="#نحوه-ساخت-با-داکر">داکر</a> •
|
<a dir="rtl" href="#نحوه-ساخت-با-داکر">داکر</a> •
|
||||||
<a dir="rtl" href="#ساخت">ساخت</a> •
|
<a dir="rtl" href="#ساخت">ساخت</a> •
|
||||||
<a dir="rtl" href="#سرورهای-عمومی-رایگان">سرور</a><br>
|
<a dir="rtl" href="#سرورهای-عمومی-رایگان">سرور</a><br>
|
||||||
[<a href="README-CS.md">česky</a>] | [<a href="README-ZH.md">中文</a>] | | [<a href="README-HU.md">Magyar</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-DE.md">Deutsch</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-ID.md">Indonesian</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-JP.md">日本語</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-IT.md">Italiano</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-AR.md">العربي</a>]<br>
|
[<a href="README-CS.md">česky</a>] | [<a href="README-ZH.md">中文</a>] | | [<a href="README-HU.md">Magyar</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-DE.md">Deutsch</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-ID.md">Indonesian</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-JP.md">日本語</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-IT.md">Italiano</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-AR.md">العربي</a>] | [<a href="README-VN.md">Tiếng Việt</a>]<br>
|
||||||
‫<b>برای ترجمه این <a href="https://github.com/rustdesk/rustdesk/tree/master/src/lang"> RustDesk UI</a> ،README و <a href="https://github.com/rustdesk/doc.rustdesk.com">Doc</a> به زبان مادری شما به کمکتون نیاز داریم
|
‫<b>برای ترجمه این <a href="https://github.com/rustdesk/rustdesk/tree/master/src/lang"> RustDesk UI</a> ،README و <a href="https://github.com/rustdesk/doc.rustdesk.com">Doc</a> به زبان مادری شما به کمکتون نیاز داریم
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
16
README-FI.md
16
README-FI.md
@ -5,7 +5,7 @@
|
|||||||
<a href="#how-to-build-with-docker">Docker</a> •
|
<a href="#how-to-build-with-docker">Docker</a> •
|
||||||
<a href="#file-structure">Rakenne</a> •
|
<a href="#file-structure">Rakenne</a> •
|
||||||
<a href="#snapshot">Tilannevedos</a><br>
|
<a href="#snapshot">Tilannevedos</a><br>
|
||||||
[<a href="README-CS.md">česky</a>] | [<a href="README-ZH.md">中文</a>] | | [<a href="README-HU.md">Magyar</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-DE.md">Deutsch</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-ID.md">Indonesian</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-JP.md">日本語</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-IT.md">Italiano</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-AR.md">العربي</a>]<br>
|
[<a href="README-CS.md">česky</a>] | [<a href="README-ZH.md">中文</a>] | | [<a href="README-HU.md">Magyar</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-DE.md">Deutsch</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-ID.md">Indonesian</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-JP.md">日本語</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-IT.md">Italiano</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-AR.md">العربي</a>] | [<a href="README-VN.md">Tiếng Việt</a>]<br>
|
||||||
<b>Tarvitsemme apua tämän README-tiedoston kääntämiseksi äidinkielellesi</b>
|
<b>Tarvitsemme apua tämän README-tiedoston kääntämiseksi äidinkielellesi</b>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
@ -13,7 +13,7 @@ Juttele meidän kanssa: [Discord](https://discord.gg/nDceKgxnkV) | [Twitter](htt
|
|||||||
|
|
||||||
[![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/I2I04VU09)
|
[![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/I2I04VU09)
|
||||||
|
|
||||||
Vielä yksi etätyöpöytäohjelmisto, ohjelmoitu Rust-kielellä. Toimii suoraan pakkauksesta, ei tarvitse asetuksia. Hallitset täysin tietojasi, ei tarvitse murehtia turvallisuutta. Voit käyttää meidän rendezvous/relay-palvelinta, [aseta omasi](https://rustdesk.com/server), tai [kirjoita oma rendezvous/relay-palvelin](https://github.com/rustdesk/rustdesk-server-demo).
|
Vielä yksi etätyöpöytäohjelmisto, ohjelmoitu Rust-kielellä. Toimii suoraan pakkauksesta, ei tarvitse asetusta. Hallitset täysin tietojasi, ei tarvitse murehtia turvallisuutta. Voit käyttää meidän rendezvous/relay-palvelinta, [aseta omasi](https://rustdesk.com/server), tai [kirjoittaa oma rendezvous/relay-palvelin](https://github.com/rustdesk/rustdesk-server-demo).
|
||||||
|
|
||||||
RustDesk toivottaa avustukset tervetulleiksi kaikilta. Katso lisätietoja [`CONTRIBUTING.md`](CONTRIBUTING.md) avun saamiseksi.
|
RustDesk toivottaa avustukset tervetulleiksi kaikilta. Katso lisätietoja [`CONTRIBUTING.md`](CONTRIBUTING.md) avun saamiseksi.
|
||||||
|
|
||||||
@ -45,9 +45,9 @@ Desktop-versiot käyttävät [sciter](https://sciter.com/) graafisena käyttöli
|
|||||||
- Windows: vcpkg install libvpx:x64-windows-static libyuv:x64-windows-static opus:x64-windows-static
|
- Windows: vcpkg install libvpx:x64-windows-static libyuv:x64-windows-static opus:x64-windows-static
|
||||||
- Linux/MacOS: vcpkg install libvpx libyuv opus
|
- Linux/MacOS: vcpkg install libvpx libyuv opus
|
||||||
|
|
||||||
- aja `cargo run`
|
- suorita `cargo run`
|
||||||
|
|
||||||
## Kuinka rakentaa Linuxissa
|
## Kuinka rakentaa Linux:issa
|
||||||
|
|
||||||
### Ubuntu 18 (Debian 10)
|
### Ubuntu 18 (Debian 10)
|
||||||
|
|
||||||
@ -79,7 +79,7 @@ export VCPKG_ROOT=$HOME/vcpkg
|
|||||||
vcpkg/vcpkg install libvpx libyuv opus
|
vcpkg/vcpkg install libvpx libyuv opus
|
||||||
```
|
```
|
||||||
|
|
||||||
### Korjaa libvpx (Fedora-linux-versiota varten)
|
### Korjaa libvpx (Fedora)
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
cd vcpkg/buildtrees/libvpx/src
|
cd vcpkg/buildtrees/libvpx/src
|
||||||
@ -107,7 +107,7 @@ VCPKG_ROOT=$HOME/vcpkg cargo run
|
|||||||
|
|
||||||
### Vaihda Wayland-ympäristö X11 (Xorg)-ympäristöön
|
### Vaihda Wayland-ympäristö X11 (Xorg)-ympäristöön
|
||||||
|
|
||||||
RustDesk ei tue Waylandia. Tarkista [tämä](https://docs.fedoraproject.org/en-US/quick-docs/configuring-xorg-as-default-gnome-session/) asettamaan Xorg oletus GNOME-istuntona.
|
RustDesk ei tue Waylandia. Tarkista [tämä](https://docs.fedoraproject.org/en-US/quick-docs/configuring-xorg-as-default-gnome-session/) asettamalla Xorg oletus GNOME-istuntoon.
|
||||||
|
|
||||||
## Kuinka rakennetaan Dockerin kanssa
|
## Kuinka rakennetaan Dockerin kanssa
|
||||||
|
|
||||||
@ -119,13 +119,13 @@ cd rustdesk
|
|||||||
docker build -t "rustdesk-builder" .
|
docker build -t "rustdesk-builder" .
|
||||||
```
|
```
|
||||||
|
|
||||||
Sitten, joka kerta kun sinun on rakennettava sovellus, aja seuraava komento:
|
Sitten, joka kerta kun sinun on rakennettava sovellus, suorita seuraava komento:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
docker run --rm -it -v $PWD:/home/user/rustdesk -v rustdesk-git-cache:/home/user/.cargo/git -v rustdesk-registry-cache:/home/user/.cargo/registry -e PUID="$(id -u)" -e PGID="$(id -g)" rustdesk-builder
|
docker run --rm -it -v $PWD:/home/user/rustdesk -v rustdesk-git-cache:/home/user/.cargo/git -v rustdesk-registry-cache:/home/user/.cargo/registry -e PUID="$(id -u)" -e PGID="$(id -g)" rustdesk-builder
|
||||||
```
|
```
|
||||||
|
|
||||||
Huomaa, että ensimmäinen rakentaminen saattaa kestää pitempään ennen kuin riippuvuudet on siirretty välimuistiin, seuraavat rakentamiset ovat nopeampia. Lisäksi, jos sinun on määritettävä eri argumentteja rakentamiskomennolle, saatat tehdä sen niin, että komennon lopussa <OPTIONAL-ARGS>`-kohdassa. Esimerkiksi, jos haluat rakentaa optimoidun julkaisuversion, sinun on ajettava komento yllä siten, että sitä seuraa argumentti`--release`. Suoritettava tiedosto on saatavilla järjestelmäsi kohdehakemistossa, ja se voidaan suorittaa seuraavan kera:
|
Huomaa, että ensimmäinen rakentaminen saattaa kestää pitempään ennen kuin riippuvuudet on siirretty välimuistiin, seuraavat rakentamiset ovat nopeampia. Lisäksi, jos sinun on määritettävä eri väittämiä rakentamiskomennolle, saatat tehdä sen niin, että komennon lopussa <OPTIONAL-ARGS>`-kohdassa. Esimerkiksi, jos haluat rakentaa optimoidun julkaisuversion, sinun on ajettava komento yllä siten, että sitä seuraa väittämä`--release`. Suoritettava tiedosto on saatavilla järjestelmäsi kohdehakemistossa, ja se voidaan suorittaa seuraavan kera:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
target/debug/rustdesk
|
target/debug/rustdesk
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
<a href="#comment-construire-avec-docker">Docker</a> -
|
<a href="#comment-construire-avec-docker">Docker</a> -
|
||||||
<a href="#structure-du-projet">Structure</a> -
|
<a href="#structure-du-projet">Structure</a> -
|
||||||
<a href="#images">Images</a><br>
|
<a href="#images">Images</a><br>
|
||||||
[<a href="README-CS.md">česky</a>] | [<a href="README-ZH.md">中文</a>] | | [<a href="README-HU.md">Magyar</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-DE.md">Deutsch</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-ID.md">Indonesian</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-JP.md">日本語</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-IT.md">Italiano</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-AR.md">العربي</a>]<br>
|
[<a href="README-CS.md">česky</a>] | [<a href="README-ZH.md">中文</a>] | | [<a href="README-HU.md">Magyar</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-DE.md">Deutsch</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-ID.md">Indonesian</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-JP.md">日本語</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-IT.md">Italiano</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-AR.md">العربي</a>] | [<a href="README-VN.md">Tiếng Việt</a>]<br>
|
||||||
<b>Nous avons besoin de votre aide pour traduire ce README dans votre langue maternelle</b>.
|
<b>Nous avons besoin de votre aide pour traduire ce README dans votre langue maternelle</b>.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
<a href="#hogyan-éptís-dockerrel">Docker</a> •
|
<a href="#hogyan-éptís-dockerrel">Docker</a> •
|
||||||
<a href="#fájl-struktúra">Struktúra</a> •
|
<a href="#fájl-struktúra">Struktúra</a> •
|
||||||
<a href="#képernyőképek">Képernyőképek</a><br>
|
<a href="#képernyőképek">Képernyőképek</a><br>
|
||||||
[<a href="README-CS.md">česky</a>] | [<a href="README-ZH.md">中文</a>] | | [<a href="README-HU.md">Magyar</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-DE.md">Deutsch</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-ID.md">Indonesian</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-JP.md">日本語</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-IT.md">Italiano</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-AR.md">العربي</a>]<br>
|
[<a href="README-CS.md">česky</a>] | [<a href="README-ZH.md">中文</a>] | | [<a href="README-HU.md">Magyar</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-DE.md">Deutsch</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-ID.md">Indonesian</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-JP.md">日本語</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-IT.md">Italiano</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-AR.md">العربي</a>] | [<a href="README-VN.md">Tiếng Việt</a>]<br>
|
||||||
<b>Kell a segítséged, hogy lefordítsuk ezt a README-t, <a href="https://github.com/rustdesk/rustdesk/tree/master/src/lang">a RustDesk UI-t</a> és a <a href="https://github.com/rustdesk/doc.rustdesk.com">Dokumentációt</a> az anyanyelvedre</b>
|
<b>Kell a segítséged, hogy lefordítsuk ezt a README-t, <a href="https://github.com/rustdesk/rustdesk/tree/master/src/lang">a RustDesk UI-t</a> és a <a href="https://github.com/rustdesk/doc.rustdesk.com">Dokumentációt</a> az anyanyelvedre</b>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
<a href="#how-to-build-with-docker">Docker</a> •
|
<a href="#how-to-build-with-docker">Docker</a> •
|
||||||
<a href="#file-structure">Structure</a> •
|
<a href="#file-structure">Structure</a> •
|
||||||
<a href="#snapshot">Snapshot</a><br>
|
<a href="#snapshot">Snapshot</a><br>
|
||||||
[<a href="README-CS.md">česky</a>] | [<a href="README-ZH.md">中文</a>] | | [<a href="README-HU.md">Magyar</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-DE.md">Deutsch</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-ID.md">Indonesian</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-JP.md">日本語</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-IT.md">Italiano</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-AR.md">العربي</a>]<br>
|
[<a href="README-CS.md">česky</a>] | [<a href="README-ZH.md">中文</a>] | | [<a href="README-HU.md">Magyar</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-DE.md">Deutsch</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-ID.md">Indonesian</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-JP.md">日本語</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-IT.md">Italiano</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-AR.md">العربي</a>] | [<a href="README-VN.md">Tiếng Việt</a>]<br>
|
||||||
<b>Kami membutuhkan bantuan Anda untuk menerjemahkan README ini dan <a href="https://github.com/rustdesk/rustdesk/tree/master/src/lang">RustDesk UI</a> ke bahasa asli anda</b>
|
<b>Kami membutuhkan bantuan Anda untuk menerjemahkan README ini dan <a href="https://github.com/rustdesk/rustdesk/tree/master/src/lang">RustDesk UI</a> ke bahasa asli anda</b>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
<a href="#come-compilare-con-docker">Docker</a> •
|
<a href="#come-compilare-con-docker">Docker</a> •
|
||||||
<a href="#struttura-dei-file">Struttura</a> •
|
<a href="#struttura-dei-file">Struttura</a> •
|
||||||
<a href="#screenshots">Screenshots</a><br>
|
<a href="#screenshots">Screenshots</a><br>
|
||||||
[<a href="README-CS.md">česky</a>] | [<a href="README-ZH.md">中文</a>] | | [<a href="README-HU.md">Magyar</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-DE.md">Deutsch</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-ID.md">Indonesian</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-JP.md">日本語</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-IT.md">Italiano</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-AR.md">العربي</a>]<br>
|
[<a href="README-CS.md">česky</a>] | [<a href="README-ZH.md">中文</a>] | | [<a href="README-HU.md">Magyar</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-DE.md">Deutsch</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-ID.md">Indonesian</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-JP.md">日本語</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-IT.md">Italiano</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-AR.md">العربي</a>] | [<a href="README-VN.md">Tiếng Việt</a>]<br>
|
||||||
<b>Abbiamo bisogno del tuo aiuto per tradurre questo README e la <a href="https://github.com/rustdesk/rustdesk/tree/master/src/lang">RustDesk UI</a> nella tua lingua nativa</b>
|
<b>Abbiamo bisogno del tuo aiuto per tradurre questo README e la <a href="https://github.com/rustdesk/rustdesk/tree/master/src/lang">RustDesk UI</a> nella tua lingua nativa</b>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
<a href="#how-to-build-with-docker">Docker</a> •
|
<a href="#how-to-build-with-docker">Docker</a> •
|
||||||
<a href="#file-structure">Structure</a> •
|
<a href="#file-structure">Structure</a> •
|
||||||
<a href="#snapshot">Snapshot</a><br>
|
<a href="#snapshot">Snapshot</a><br>
|
||||||
[<a href="README-CS.md">česky</a>] | [<a href="README-ZH.md">中文</a>] | | [<a href="README-HU.md">Magyar</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-DE.md">Deutsch</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-ID.md">Indonesian</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-JP.md">日本語</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-IT.md">Italiano</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-AR.md">العربي</a>]<br>
|
[<a href="README-CS.md">česky</a>] | [<a href="README-ZH.md">中文</a>] | | [<a href="README-HU.md">Magyar</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-DE.md">Deutsch</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-ID.md">Indonesian</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-JP.md">日本語</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-IT.md">Italiano</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-AR.md">العربي</a>] | [<a href="README-VN.md">Tiếng Việt</a>]<br>
|
||||||
<b>このREADMEをあなたの母国語に翻訳するために、あなたの助けが必要です。</b>
|
<b>このREADMEをあなたの母国語に翻訳するために、あなたの助けが必要です。</b>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
<a href="#how-to-build-with-docker">Docker</a> •
|
<a href="#how-to-build-with-docker">Docker</a> •
|
||||||
<a href="#file-structure">Structure</a> •
|
<a href="#file-structure">Structure</a> •
|
||||||
<a href="#snapshot">Snapshot</a><br>
|
<a href="#snapshot">Snapshot</a><br>
|
||||||
[<a href="README-CS.md">česky</a>] | [<a href="README-ZH.md">中文</a>] | | [<a href="README-HU.md">Magyar</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-DE.md">Deutsch</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-ID.md">Indonesian</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-JP.md">日本語</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-IT.md">Italiano</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-AR.md">العربي</a>]<br>
|
[<a href="README-CS.md">česky</a>] | [<a href="README-ZH.md">中文</a>] | | [<a href="README-HU.md">Magyar</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-DE.md">Deutsch</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-ID.md">Indonesian</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-JP.md">日本語</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-IT.md">Italiano</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-AR.md">العربي</a>] | [<a href="README-VN.md">Tiếng Việt</a>]<br>
|
||||||
<b>README를 모국어로 번역하기 위한 당신의 도움의 필요합니다.</b>
|
<b>README를 모국어로 번역하기 위한 당신의 도움의 필요합니다.</b>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
<a href="#how-to-build-with-docker">Docker</a> •
|
<a href="#how-to-build-with-docker">Docker</a> •
|
||||||
<a href="#file-structure">Structure</a> •
|
<a href="#file-structure">Structure</a> •
|
||||||
<a href="#snapshot">Snapshot</a><br>
|
<a href="#snapshot">Snapshot</a><br>
|
||||||
[<a href="README-CS.md">česky</a>] | [<a href="README-ZH.md">中文</a>] | | [<a href="README-HU.md">Magyar</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-DE.md">Deutsch</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-ID.md">Indonesian</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-JP.md">日本語</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-IT.md">Italiano</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-AR.md">العربي</a>]<br>
|
[<a href="README-CS.md">česky</a>] | [<a href="README-ZH.md">中文</a>] | | [<a href="README-HU.md">Magyar</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-DE.md">Deutsch</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-ID.md">Indonesian</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-JP.md">日本語</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-IT.md">Italiano</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-AR.md">العربي</a>] | [<a href="README-VN.md">Tiếng Việt</a>]<br>
|
||||||
<b>ഈ README നിങ്ങളുടെ മാതൃഭാഷയിലേക്ക് വിവർത്തനം ചെയ്യാൻ ഞങ്ങൾക്ക് നിങ്ങളുടെ സഹായം ആവശ്യമാണ്</b>
|
<b>ഈ README നിങ്ങളുടെ മാതൃഭാഷയിലേക്ക് വിവർത്തനം ചെയ്യാൻ ഞങ്ങൾക്ക് നിങ്ങളുടെ സഹായം ആവശ്യമാണ്</b>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
<a href="#how-to-build-with-docker">Docker</a> •
|
<a href="#how-to-build-with-docker">Docker</a> •
|
||||||
<a href="#file-structure">Structuur</a> •
|
<a href="#file-structure">Structuur</a> •
|
||||||
<a href="#snapshot">Snapshot</a><br>
|
<a href="#snapshot">Snapshot</a><br>
|
||||||
[<a href="README-CS.md">česky</a>] | [<a href="README-ZH.md">中文</a>] | | [<a href="README-HU.md">Magyar</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-DE.md">Deutsch</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-ID.md">Indonesian</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-JP.md">日本語</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-IT.md">Italiano</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-AR.md">العربي</a>]<br>
|
[<a href="README-CS.md">česky</a>] | [<a href="README-ZH.md">中文</a>] | | [<a href="README-HU.md">Magyar</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-DE.md">Deutsch</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-ID.md">Indonesian</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-JP.md">日本語</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-IT.md">Italiano</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-AR.md">العربي</a>] | [<a href="README-VN.md">Tiếng Việt</a>]<br>
|
||||||
<b>We hebben je hulp nodig om deze README te vertalen naar jouw moedertaal</b>
|
<b>We hebben je hulp nodig om deze README te vertalen naar jouw moedertaal</b>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
<a href="#jak-kompilować-za-pomocą-dockera">Docker</a> •
|
<a href="#jak-kompilować-za-pomocą-dockera">Docker</a> •
|
||||||
<a href="#struktura-plików">Struktura</a> •
|
<a href="#struktura-plików">Struktura</a> •
|
||||||
<a href="#migawkisnapshoty">Snapshot</a><br>
|
<a href="#migawkisnapshoty">Snapshot</a><br>
|
||||||
[<a href="README-CS.md">česky</a>] | [<a href="README-ZH.md">中文</a>] | | [<a href="README-HU.md">Magyar</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-DE.md">Deutsch</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-ID.md">Indonesian</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-JP.md">日本語</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-IT.md">Italiano</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-AR.md">العربي</a>]<br>
|
[<a href="README-CS.md">česky</a>] | [<a href="README-ZH.md">中文</a>] | | [<a href="README-HU.md">Magyar</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-DE.md">Deutsch</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-ID.md">Indonesian</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-JP.md">日本語</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-IT.md">Italiano</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-AR.md">العربي</a>] | [<a href="README-VN.md">Tiếng Việt</a>]<br>
|
||||||
<b>Potrzebujemy twojej pomocy w tłumaczeniu README na twój ojczysty język</b>
|
<b>Potrzebujemy twojej pomocy w tłumaczeniu README na twój ojczysty język</b>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
<a href="#como-compilar-com-docker">Docker</a> •
|
<a href="#como-compilar-com-docker">Docker</a> •
|
||||||
<a href="#estrutura-de-arquivos">Estrutura</a> •
|
<a href="#estrutura-de-arquivos">Estrutura</a> •
|
||||||
<a href="#screenshots">Screenshots</a><br>
|
<a href="#screenshots">Screenshots</a><br>
|
||||||
[<a href="README-CS.md">česky</a>] | [<a href="README-ZH.md">中文</a>] | | [<a href="README-HU.md">Magyar</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-DE.md">Deutsch</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-ID.md">Indonesian</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-JP.md">日本語</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-IT.md">Italiano</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-AR.md">العربي</a>]<br>
|
[<a href="README-CS.md">česky</a>] | [<a href="README-ZH.md">中文</a>] | | [<a href="README-HU.md">Magyar</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-DE.md">Deutsch</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-ID.md">Indonesian</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-JP.md">日本語</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-IT.md">Italiano</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-AR.md">العربي</a>] | [<a href="README-VN.md">Tiếng Việt</a>]<br>
|
||||||
<b>Precisamos de sua ajuda para traduzir este README e a <a href="https://github.com/rustdesk/rustdesk/tree/master/src/lang">UI do RustDesk</a> para sua língua nativa</b>
|
<b>Precisamos de sua ajuda para traduzir este README e a <a href="https://github.com/rustdesk/rustdesk/tree/master/src/lang">UI do RustDesk</a> para sua língua nativa</b>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
<a href="#how-to-build-with-docker">Docker</a> •
|
<a href="#how-to-build-with-docker">Docker</a> •
|
||||||
<a href="#file-structure">Structure</a> •
|
<a href="#file-structure">Structure</a> •
|
||||||
<a href="#snapshot">Snapshot</a><br>
|
<a href="#snapshot">Snapshot</a><br>
|
||||||
[<a href="README-CS.md">česky</a>] | [<a href="README-ZH.md">中文</a>] | | [<a href="README-HU.md">Magyar</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-DE.md">Deutsch</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-ID.md">Indonesian</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-JP.md">日本語</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-IT.md">Italiano</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-AR.md">العربي</a>]<br>
|
[<a href="README-CS.md">česky</a>] | [<a href="README-ZH.md">中文</a>] | | [<a href="README-HU.md">Magyar</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-DE.md">Deutsch</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-ID.md">Indonesian</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-JP.md">日本語</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-IT.md">Italiano</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-AR.md">العربي</a>] | [<a href="README-VN.md">Tiếng Việt</a>]<br>
|
||||||
<b>Нам нужна ваша помощь для перевода этого README и <a href="https://github.com/rustdesk/rustdesk/tree/master/src/rustdesk/tree/master/src/lang">RustDesk UI</a> на ваш родной язык</B>
|
<b>Нам нужна ваша помощь для перевода этого README и <a href="https://github.com/rustdesk/rustdesk/tree/master/src/rustdesk/tree/master/src/lang">RustDesk UI</a> на ваш родной язык</B>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
182
README-VN.md
Normal file
182
README-VN.md
Normal file
@ -0,0 +1,182 @@
|
|||||||
|
<p align="center">
|
||||||
|
<img src="logo-header.svg" alt="RustDesk - Phần mềm điểu khiển máy tính từ xa dành cho bạn"><br>
|
||||||
|
<a href="#free-public-servers">Máy chủ</a> •
|
||||||
|
<a href="#raw-steps-to-build">Build</a> •
|
||||||
|
<a href="#how-to-build-with-docker">Docker</a> •
|
||||||
|
<a href="#file-structure">Cấu trúc tệp tin</a> •
|
||||||
|
<a href="#snapshot">Snapshot</a><br>
|
||||||
|
[<a href="README-CS.md">česky</a>] | [<a href="README-ZH.md">中文</a>] | | [<a href="README-HU.md">Magyar</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-DE.md">Deutsch</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-ID.md">Indonesian</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-JP.md">日本語</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-IT.md">Italiano</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-AR.md">العربي</a>] | [<a href="README-VN.md">Tiếng Việt</a>]<br>
|
||||||
|
<b>Chúng tôi cần sự gíup đỡ của bạn để dịch trang README này, <a href="https://github.com/rustdesk/rustdesk/tree/master/src/lang">RustDesk UI</a> và <a href="https://github.com/rustdesk/doc.rustdesk.com">tài liệu</a> sang ngôn ngữ bản địa của bạn</b>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
Chat với chúng tôi qua: [Discord](https://discord.gg/nDceKgxnkV) | [Twitter](https://twitter.com/rustdesk) | [Reddit](https://www.reddit.com/r/rustdesk)
|
||||||
|
|
||||||
|
[![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/I2I04VU09)
|
||||||
|
|
||||||
|
Một phần mềm điểu khiển máy tính từ xa, đuợc lập trình bằng ngôn ngữ Rust. Hoạt động tức thì, không cần phải cài đặt. Bạn có toàn quyền điểu khiển với dữ liệu của bạn mà không cần phải lo lắng về sự bảo mật. Bạn có thể sử dụng máy chủ rendezvous/relay của chúng tôi, [tự cài đặt máy chủ](https://rustdesk.com/server), hay thậm chí [tự tạo máy chủ rendezvous/relay](https://github.com/rustdesk/rustdesk-server-demo).
|
||||||
|
|
||||||
|
![image](https://user-images.githubusercontent.com/71636191/171661982-430285f0-2e12-4b1d-9957-4a58e375304d.png)
|
||||||
|
|
||||||
|
Mọi người đều đuợc chào đón để đóng góp vào RustDesk. Để bắt đầu, hãy đọc [`CONTRIBUTING.md`](CONTRIBUTING.md).
|
||||||
|
|
||||||
|
[**RustDesk hoạt động như thế nào?**](https://github.com/rustdesk/rustdesk/wiki/How-does-RustDesk-work%3F)
|
||||||
|
|
||||||
|
[**CÁC BẢN PHÂN PHÁT MÃ NHỊ PHÂN**](https://github.com/rustdesk/rustdesk/releases)
|
||||||
|
|
||||||
|
[<img src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png"
|
||||||
|
alt="Get it on F-Droid"
|
||||||
|
height="80">](https://f-droid.org/en/packages/com.carriez.flutter_hbb)
|
||||||
|
|
||||||
|
## Các Máy Chủ Công Khai Miễn Phí
|
||||||
|
|
||||||
|
Dưới đây là những máy chủ mà bạn có thể sử dụng mà không mất phí, chú ý là máy chủ có thể thay đổi theo thời gian. Nếu địa điểm của bạn không gần một trong số những máy chủ này, thì kết nói có thể chậm.
|
||||||
|
|
||||||
|
| Địa điểm | Nhà cung cấp | Cấu hình |
|
||||||
|
| --------- | ------------- | ------------------ |
|
||||||
|
| Seoul | AWS lightsail | 1 VCPU / 0.5GB RAM |
|
||||||
|
| Singapore | Vultr | 1 VCPU / 1GB RAM |
|
||||||
|
| Dallas | Vultr | 1 VCPU / 1GB RAM | |
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
Phiên bản cho máy tính sử dụng [sciter](https://sciter.com/) cho giao diện của phần mềm, vậy nên bạn cần tự tải về thư viện sciter.
|
||||||
|
|
||||||
|
[Windows](https://raw.githubusercontent.com/c-smile/sciter-sdk/master/bin.win/x64/sciter.dll) |
|
||||||
|
[Linux](https://raw.githubusercontent.com/c-smile/sciter-sdk/master/bin.lnx/x64/libsciter-gtk.so) |
|
||||||
|
[MacOS](https://raw.githubusercontent.com/c-smile/sciter-sdk/master/bin.osx/libsciter.dylib)
|
||||||
|
|
||||||
|
Phiên bản cho điện thoại sử dụng Flutter. Chúng tôi sẽ chuyển sang sử dụng Flutter thay cho Sciter cho phiên bản máy tính.
|
||||||
|
|
||||||
|
## Cách để build
|
||||||
|
|
||||||
|
- Chuẩn bị môi trường phát triển Rust và môi trường build C++
|
||||||
|
|
||||||
|
- Tải và cài [vcpkg](https://github.com/microsoft/vcpkg), và đặt biến môi trường `VCPKG_ROOT` sao cho đúng.
|
||||||
|
|
||||||
|
- Đối với Windows: vcpkg install libvpx:x64-windows-static libyuv:x64-windows-static opus:x64-windows-static
|
||||||
|
- Đối với Linux/MacOS: vcpkg install libvpx libyuv opus
|
||||||
|
|
||||||
|
- Chạy lệnh `cargo run`
|
||||||
|
|
||||||
|
## [Build](https://rustdesk.com/docs/en/dev/build/)
|
||||||
|
|
||||||
|
## Cách để build cho Linux
|
||||||
|
|
||||||
|
### Ubuntu 18 (Debian 10)
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sudo apt install -y g++ gcc git curl wget nasm yasm libgtk-3-dev clang libxcb-randr0-dev libxdo-dev libxfixes-dev libxcb-shape0-dev libxcb-xfixes0-dev libasound2-dev libpulse-dev cmake
|
||||||
|
```
|
||||||
|
|
||||||
|
### Fedora 28 (CentOS 8)
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sudo yum -y install gcc-c++ git curl wget nasm yasm gcc gtk3-devel clang libxcb-devel libxdo-devel libXfixes-devel pulseaudio-libs-devel cmake alsa-lib-devel
|
||||||
|
```
|
||||||
|
|
||||||
|
### Arch (Manjaro)
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sudo pacman -Syu --needed unzip git cmake gcc curl wget yasm nasm zip make pkg-config clang gtk3 xdotool libxcb libxfixes alsa-lib pulseaudio
|
||||||
|
```
|
||||||
|
|
||||||
|
### Cách tải về gói hàng pynput
|
||||||
|
|
||||||
|
```sh
|
||||||
|
pip3 install pynput
|
||||||
|
```
|
||||||
|
|
||||||
|
### Cách cài vcpkg
|
||||||
|
|
||||||
|
```sh
|
||||||
|
git clone https://github.com/microsoft/vcpkg
|
||||||
|
cd vcpkg
|
||||||
|
git checkout 2021.12.01
|
||||||
|
cd ..
|
||||||
|
vcpkg/bootstrap-vcpkg.sh
|
||||||
|
export VCPKG_ROOT=$HOME/vcpkg
|
||||||
|
vcpkg/vcpkg install libvpx libyuv opus
|
||||||
|
```
|
||||||
|
|
||||||
|
### Cách sửa lỗi libvpx (Dành cho hệ điều hành Fedora)
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cd vcpkg/buildtrees/libvpx/src
|
||||||
|
cd *
|
||||||
|
./configure
|
||||||
|
sed -i 's/CFLAGS+=-I/CFLAGS+=-fPIC -I/g' Makefile
|
||||||
|
sed -i 's/CXXFLAGS+=-I/CXXFLAGS+=-fPIC -I/g' Makefile
|
||||||
|
make
|
||||||
|
cp libvpx.a $HOME/vcpkg/installed/x64-linux/lib/
|
||||||
|
cd
|
||||||
|
```
|
||||||
|
|
||||||
|
### Cách build
|
||||||
|
|
||||||
|
```sh
|
||||||
|
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
|
||||||
|
source $HOME/.cargo/env
|
||||||
|
git clone https://github.com/rustdesk/rustdesk
|
||||||
|
cd rustdesk
|
||||||
|
mkdir -p target/debug
|
||||||
|
wget https://raw.githubusercontent.com/c-smile/sciter-sdk/master/bin.lnx/x64/libsciter-gtk.so
|
||||||
|
mv libsciter-gtk.so target/debug
|
||||||
|
VCPKG_ROOT=$HOME/vcpkg cargo run
|
||||||
|
```
|
||||||
|
|
||||||
|
### Chuyển từ Wayland sang X11 (Xorg)
|
||||||
|
|
||||||
|
RustDesk hiện không hỗ trợ Wayland. Hãy xem [đường linh ở đây](https://docs.fedoraproject.org/en-US/quick-docs/configuring-xorg-as-default-gnome-session/) cách để cài đặt Xorg làm session mặc định của GNOME.
|
||||||
|
|
||||||
|
## Cách để build sử dụng Docker
|
||||||
|
|
||||||
|
Bắt đầu bằng cách sao chép repo này về máy tính và build cái Docker cointainer:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
git clone https://github.com/rustdesk/rustdesk
|
||||||
|
cd rustdesk
|
||||||
|
docker build -t "rustdesk-builder" .
|
||||||
|
```
|
||||||
|
|
||||||
|
Rồi mỗi khi bạn chạy ứng dụng, thì hãy chạy lệnh này:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
docker run --rm -it -v $PWD:/home/user/rustdesk -v rustdesk-git-cache:/home/user/.cargo/git -v rustdesk-registry-cache:/home/user/.cargo/registry -e PUID="$(id -u)" -e PGID="$(id -g)" rustdesk-builder
|
||||||
|
```
|
||||||
|
|
||||||
|
Chú ý: Lần build đầu tiên có thể sẽ mất lâu hơn truớc khi các dependecies đuợc lưu lại, những lần build sau sẽ nhanh hơn. Hơn nũa, nếu bạn cần cung cấp các cài đặt lệnh khác cho lệnh build, bạn có thể đặt những cài đặt lệnh này vào cuối lệnh ở phần `<OPTIONAL-ARGS>`. Ví dụ nếu bạn cần build phiên bản đuợc tối ưu hóa, bạn sẽ chạy lệnh trên cùng với cài đặt lệnh ‘--release’. Kết quả build sẽ được lưu trong thư mục target trên máy tính của bạn, và có thể chạy với lệnh:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
target/debug/rustdesk
|
||||||
|
```
|
||||||
|
|
||||||
|
Nếu bạn đang chạy bản build đuợc tối ưu hóa, thì bạn có thể chạy với lệnh:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
target/release/rustdesk
|
||||||
|
```
|
||||||
|
|
||||||
|
Hãy đảm bảo là bạn đang chạy những lệnh này từ thu mục rễ của repo RustDesk, vì nếu không thì ứng dụng có thể sẽ không tìm đuợc những tệp tài nguyên cần thiết. Cũng như nhớ rằng những lệnh con của cargo như `install` hoặc `run` hiện chưa được hỗ trợ bởi phương pháp này vì chúng sẽ cài đặt hoặc chạy ứng dụng trong container thay vì trên máy tính của bạn.
|
||||||
|
|
||||||
|
## Cấu trúc tệp tin
|
||||||
|
|
||||||
|
- **[libs/hbb_common](https://github.com/rustdesk/rustdesk/tree/master/libs/hbb_common)**: video codec, cấu hình, tcp/udp wrapper, protobuf, fs functions để truyền file, và một số hàm tiện ích khác
|
||||||
|
- **[libs/scrap](https://github.com/rustdesk/rustdesk/tree/master/libs/scrap)**: để ghi lại màn hình
|
||||||
|
- **[libs/enigo](https://github.com/rustdesk/rustdesk/tree/master/libs/enigo)**: để điều khiển máy tính/con chuột trên những nền tảng khác nhau
|
||||||
|
- **[src/ui](https://github.com/rustdesk/rustdesk/tree/master/src/ui)**: giao diện người dùng
|
||||||
|
- **[src/server](https://github.com/rustdesk/rustdesk/tree/master/src/server)**: các dịch vụ âm thanh, clipboard, đầu vào, video và các kết nối mạng
|
||||||
|
- **[src/client.rs](https://github.com/rustdesk/rustdesk/tree/master/src/client.rs)**: để bắt đầu kết nối với một peer
|
||||||
|
- **[src/rendezvous_mediator.rs](https://github.com/rustdesk/rustdesk/tree/master/src/rendezvous_mediator.rs)**: Để liên lạc với [rustdesk-server](https://github.com/rustdesk/rustdesk-server), đợi cho kết nối trực tiếp (TCP hole punching) hoặc kết nối được relayed.
|
||||||
|
- **[src/platform](https://github.com/rustdesk/rustdesk/tree/master/src/platform)**: mã nguồn riêng cho mỗi nền tảng
|
||||||
|
- **[flutter](https://github.com/rustdesk/rustdesk/tree/master/flutter)**: Mã Flutter dành cho điện thoại
|
||||||
|
- **[flutter/web/js](https://github.com/rustdesk/rustdesk/tree/master/flutter/web/js)**: Mã JavaScript dành cho giao diện trên web bằng Flutter
|
||||||
|
|
||||||
|
## Snapshot
|
||||||
|
|
||||||
|
![image](https://user-images.githubusercontent.com/71636191/113112362-ae4deb80-923b-11eb-957d-ff88daad4f06.png)
|
||||||
|
|
||||||
|
![image](https://user-images.githubusercontent.com/71636191/113112619-f705a480-923b-11eb-911d-97e984ef52b6.png)
|
||||||
|
|
||||||
|
![image](https://user-images.githubusercontent.com/71636191/113112857-3fbd5d80-923c-11eb-9836-768325faf906.png)
|
||||||
|
|
||||||
|
![image](https://user-images.githubusercontent.com/71636191/135385039-38fdbd72-379a-422d-b97f-33df71fb1cec.png)
|
@ -5,7 +5,7 @@
|
|||||||
<a href="#使用Docker编译">Docker</a> •
|
<a href="#使用Docker编译">Docker</a> •
|
||||||
<a href="#文件结构">结构</a> •
|
<a href="#文件结构">结构</a> •
|
||||||
<a href="#截图">截图</a><br>
|
<a href="#截图">截图</a><br>
|
||||||
[<a href="README-CS.md">česky</a>] | [<a href="README-ZH.md">中文</a>] | | [<a href="README-HU.md">Magyar</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-DE.md">Deutsch</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-ID.md">Indonesian</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-JP.md">日本語</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-IT.md">Italiano</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-AR.md">العربي</a>]<br>
|
[<a href="README-CS.md">česky</a>] | [<a href="README-ZH.md">中文</a>] | | [<a href="README-HU.md">Magyar</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-DE.md">Deutsch</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-ID.md">Indonesian</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-JP.md">日本語</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-IT.md">Italiano</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-AR.md">العربي</a>] | [<a href="README-VN.md">Tiếng Việt</a>]<br>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
Chat with us: [知乎](https://www.zhihu.com/people/rustdesk) | [Discord](https://discord.gg/nDceKgxnkV) | [Reddit](https://www.reddit.com/r/rustdesk)
|
Chat with us: [知乎](https://www.zhihu.com/people/rustdesk) | [Discord](https://discord.gg/nDceKgxnkV) | [Reddit](https://www.reddit.com/r/rustdesk)
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
<a href="#how-to-build-with-docker">Docker</a> •
|
<a href="#how-to-build-with-docker">Docker</a> •
|
||||||
<a href="#file-structure">Structure</a> •
|
<a href="#file-structure">Structure</a> •
|
||||||
<a href="#snapshot">Snapshot</a><br>
|
<a href="#snapshot">Snapshot</a><br>
|
||||||
[<a href="README-CS.md">česky</a>] | [<a href="README-ZH.md">中文</a>] | | [<a href="README-HU.md">Magyar</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-DE.md">Deutsch</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-ID.md">Indonesian</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-JP.md">日本語</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-IT.md">Italiano</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-AR.md">العربي</a>]<br>
|
[<a href="README-CS.md">česky</a>] | [<a href="README-ZH.md">中文</a>] | | [<a href="README-HU.md">Magyar</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-DE.md">Deutsch</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-ID.md">Indonesian</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-JP.md">日本語</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-IT.md">Italiano</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-AR.md">العربي</a>] | [<a href="README-VN.md">Tiếng Việt</a>]<br>
|
||||||
<b>We need your help to translate this README, <a href="https://github.com/rustdesk/rustdesk/tree/master/src/lang">RustDesk UI</a> and <a href="https://github.com/rustdesk/doc.rustdesk.com">Doc</a> to your native language</b>
|
<b>We need your help to translate this README, <a href="https://github.com/rustdesk/rustdesk/tree/master/src/lang">RustDesk UI</a> and <a href="https://github.com/rustdesk/doc.rustdesk.com">Doc</a> to your native language</b>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
@ -155,7 +155,7 @@ Or, if you're running a release executable:
|
|||||||
target/release/rustdesk
|
target/release/rustdesk
|
||||||
```
|
```
|
||||||
|
|
||||||
Please ensure that you are running these commands from the root of the RustDesk repository, otherwise the application may be unable to find the required resources. Also note that other cargo subcommands such as `install` or `run` are not currently supported via this method as they would install or run the program inside the container instead of the host.
|
Please ensure that you are running these commands from the root of the RustDesk repository, otherwise the application might not be able to find the required resources. Also note that other cargo subcommands such as `install` or `run` are not currently supported via this method as they would install or run the program inside the container instead of the host.
|
||||||
|
|
||||||
## File Structure
|
## File Structure
|
||||||
|
|
||||||
|
@ -32,7 +32,7 @@ apply plugin: 'kotlin-android'
|
|||||||
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
|
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 31
|
compileSdkVersion 32
|
||||||
sourceSets {
|
sourceSets {
|
||||||
main.java.srcDirs += 'src/main/kotlin'
|
main.java.srcDirs += 'src/main/kotlin'
|
||||||
}
|
}
|
||||||
|
@ -199,7 +199,7 @@ const G = M * K;
|
|||||||
|
|
||||||
String readableFileSize(double size) {
|
String readableFileSize(double size) {
|
||||||
if (size < K) {
|
if (size < K) {
|
||||||
return size.toString() + " B";
|
return size.toStringAsFixed(2) + " B";
|
||||||
} else if (size < M) {
|
} else if (size < M) {
|
||||||
return (size / K).toStringAsFixed(2) + " KB";
|
return (size / K).toStringAsFixed(2) + " KB";
|
||||||
} else if (size < G) {
|
} else if (size < G) {
|
||||||
@ -313,3 +313,15 @@ class PermissionManager {
|
|||||||
_current = "";
|
_current = "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RadioListTile<T> getRadio<T>(
|
||||||
|
String name, T toValue, T curValue, void Function(T?) onChange) {
|
||||||
|
return RadioListTile<T>(
|
||||||
|
controlAffinity: ListTileControlAffinity.trailing,
|
||||||
|
title: Text(translate(name)),
|
||||||
|
value: toValue,
|
||||||
|
groupValue: curValue,
|
||||||
|
onChanged: onChange,
|
||||||
|
dense: true,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
@ -39,7 +39,7 @@ class App extends StatelessWidget {
|
|||||||
primarySwatch: Colors.blue,
|
primarySwatch: Colors.blue,
|
||||||
visualDensity: VisualDensity.adaptivePlatformDensity,
|
visualDensity: VisualDensity.adaptivePlatformDensity,
|
||||||
),
|
),
|
||||||
home: !isAndroid ? WebHomePage() : HomePage(),
|
home: !isAndroid ? WebHomePage() : HomePage(key: homeKey),
|
||||||
navigatorObservers: [
|
navigatorObservers: [
|
||||||
FirebaseAnalyticsObserver(analytics: analytics),
|
FirebaseAnalyticsObserver(analytics: analytics),
|
||||||
FlutterSmartDialog.observer
|
FlutterSmartDialog.observer
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:dash_chat/dash_chat.dart';
|
import 'package:dash_chat_2/dash_chat_2.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import '../widgets/overlay.dart';
|
import '../widgets/overlay.dart';
|
||||||
@ -11,8 +11,8 @@ class MessageBody {
|
|||||||
List<ChatMessage> chatMessages;
|
List<ChatMessage> chatMessages;
|
||||||
MessageBody(this.chatUser, this.chatMessages);
|
MessageBody(this.chatUser, this.chatMessages);
|
||||||
|
|
||||||
void add(ChatMessage cm) {
|
void insert(ChatMessage cm) {
|
||||||
this.chatMessages.add(cm);
|
this.chatMessages.insert(0, cm);
|
||||||
}
|
}
|
||||||
|
|
||||||
void clear() {
|
void clear() {
|
||||||
@ -24,19 +24,15 @@ class ChatModel with ChangeNotifier {
|
|||||||
static final clientModeID = -1;
|
static final clientModeID = -1;
|
||||||
|
|
||||||
final ChatUser me = ChatUser(
|
final ChatUser me = ChatUser(
|
||||||
uid: "",
|
id: "",
|
||||||
name: "Me",
|
firstName: "Me",
|
||||||
);
|
);
|
||||||
|
|
||||||
late final Map<int, MessageBody> _messages = Map()
|
late final Map<int, MessageBody> _messages = Map()
|
||||||
..[clientModeID] = MessageBody(me, []);
|
..[clientModeID] = MessageBody(me, []);
|
||||||
|
|
||||||
final _scroller = ScrollController();
|
|
||||||
|
|
||||||
var _currentID = clientModeID;
|
var _currentID = clientModeID;
|
||||||
|
|
||||||
ScrollController get scroller => _scroller;
|
|
||||||
|
|
||||||
Map<int, MessageBody> get messages => _messages;
|
Map<int, MessageBody> get messages => _messages;
|
||||||
|
|
||||||
int get currentID => _currentID;
|
int get currentID => _currentID;
|
||||||
@ -62,8 +58,8 @@ class ChatModel with ChangeNotifier {
|
|||||||
"Failed to changeCurrentID,remote user doesn't exist");
|
"Failed to changeCurrentID,remote user doesn't exist");
|
||||||
}
|
}
|
||||||
final chatUser = ChatUser(
|
final chatUser = ChatUser(
|
||||||
uid: client.peerId,
|
id: client.peerId,
|
||||||
name: client.name,
|
firstName: client.name,
|
||||||
);
|
);
|
||||||
_messages[id] = MessageBody(chatUser, []);
|
_messages[id] = MessageBody(chatUser, []);
|
||||||
_currentID = id;
|
_currentID = id;
|
||||||
@ -80,48 +76,39 @@ class ChatModel with ChangeNotifier {
|
|||||||
late final chatUser;
|
late final chatUser;
|
||||||
if (id == clientModeID) {
|
if (id == clientModeID) {
|
||||||
chatUser = ChatUser(
|
chatUser = ChatUser(
|
||||||
name: FFI.ffiModel.pi.username,
|
firstName: FFI.ffiModel.pi.username,
|
||||||
uid: FFI.getId(),
|
id: FFI.getId(),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
final client = FFI.serverModel.clients[id];
|
final client = FFI.serverModel.clients[id];
|
||||||
if (client == null) {
|
if (client == null) {
|
||||||
return debugPrint("Failed to receive msg,user doesn't exist");
|
return debugPrint("Failed to receive msg,user doesn't exist");
|
||||||
}
|
}
|
||||||
chatUser = ChatUser(uid: client.peerId, name: client.name);
|
chatUser = ChatUser(id: client.peerId, firstName: client.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_messages.containsKey(id)) {
|
if (!_messages.containsKey(id)) {
|
||||||
_messages[id] = MessageBody(chatUser, []);
|
_messages[id] = MessageBody(chatUser, []);
|
||||||
}
|
}
|
||||||
_messages[id]!.add(ChatMessage(text: text, user: chatUser));
|
_messages[id]!.insert(
|
||||||
|
ChatMessage(text: text, user: chatUser, createdAt: DateTime.now()));
|
||||||
_currentID = id;
|
_currentID = id;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
scrollToBottom();
|
|
||||||
}
|
|
||||||
|
|
||||||
scrollToBottom() {
|
|
||||||
Future.delayed(Duration(milliseconds: 500), () {
|
|
||||||
_scroller.animateTo(_scroller.position.maxScrollExtent,
|
|
||||||
duration: Duration(milliseconds: 200),
|
|
||||||
curve: Curves.fastLinearToSlowEaseIn);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
send(ChatMessage message) {
|
send(ChatMessage message) {
|
||||||
if (message.text != null && message.text!.isNotEmpty) {
|
if (message.text.isNotEmpty) {
|
||||||
_messages[_currentID]?.add(message);
|
_messages[_currentID]?.insert(message);
|
||||||
if (_currentID == clientModeID) {
|
if (_currentID == clientModeID) {
|
||||||
FFI.setByName("chat_client_mode", message.text!);
|
FFI.setByName("chat_client_mode", message.text);
|
||||||
} else {
|
} else {
|
||||||
final msg = Map()
|
final msg = Map()
|
||||||
..["id"] = _currentID
|
..["id"] = _currentID
|
||||||
..["text"] = message.text!;
|
..["text"] = message.text;
|
||||||
FFI.setByName("chat_server_mode", jsonEncode(msg));
|
FFI.setByName("chat_server_mode", jsonEncode(msg));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
scrollToBottom();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
close() {
|
close() {
|
||||||
|
@ -199,6 +199,7 @@ class FileModel extends ChangeNotifier {
|
|||||||
|
|
||||||
onClose() {
|
onClose() {
|
||||||
SmartDialog.dismiss();
|
SmartDialog.dismiss();
|
||||||
|
jobReset();
|
||||||
|
|
||||||
// save config
|
// save config
|
||||||
Map<String, String> msg = Map();
|
Map<String, String> msg = Map();
|
||||||
|
@ -68,7 +68,7 @@ class FfiModel with ChangeNotifier {
|
|||||||
|
|
||||||
void updatePermission(Map<String, dynamic> evt) {
|
void updatePermission(Map<String, dynamic> evt) {
|
||||||
evt.forEach((k, v) {
|
evt.forEach((k, v) {
|
||||||
if (k == 'name') return;
|
if (k == 'name' || k.isEmpty) return;
|
||||||
_permissions[k] = v == 'true';
|
_permissions[k] = v == 'true';
|
||||||
});
|
});
|
||||||
print('$_permissions');
|
print('$_permissions');
|
||||||
@ -162,6 +162,8 @@ class FfiModel with ChangeNotifier {
|
|||||||
FFI.serverModel.onClientAuthorized(evt);
|
FFI.serverModel.onClientAuthorized(evt);
|
||||||
} else if (name == 'on_client_remove') {
|
} else if (name == 'on_client_remove') {
|
||||||
FFI.serverModel.onClientRemove(evt);
|
FFI.serverModel.onClientRemove(evt);
|
||||||
|
} else if (name == 'update_quality_status') {
|
||||||
|
FFI.qualityMonitorModel.updateQualityStatus(evt);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
PlatformFFI.setEventCallback(cb);
|
PlatformFFI.setEventCallback(cb);
|
||||||
@ -193,14 +195,17 @@ class FfiModel with ChangeNotifier {
|
|||||||
wrongPasswordDialog(id);
|
wrongPasswordDialog(id);
|
||||||
} else if (type == 'input-password') {
|
} else if (type == 'input-password') {
|
||||||
enterPasswordDialog(id);
|
enterPasswordDialog(id);
|
||||||
|
} else if (type == 'restarting') {
|
||||||
|
showMsgBox(type, title, text, false, hasCancel: false);
|
||||||
} else {
|
} else {
|
||||||
var hasRetry = evt['hasRetry'] == 'true';
|
var hasRetry = evt['hasRetry'] == 'true';
|
||||||
showMsgBox(type, title, text, hasRetry);
|
showMsgBox(type, title, text, hasRetry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void showMsgBox(String type, String title, String text, bool hasRetry) {
|
void showMsgBox(String type, String title, String text, bool hasRetry,
|
||||||
msgBox(type, title, text);
|
{bool? hasCancel}) {
|
||||||
|
msgBox(type, title, text, hasCancel: hasCancel);
|
||||||
_timer?.cancel();
|
_timer?.cancel();
|
||||||
if (hasRetry) {
|
if (hasRetry) {
|
||||||
_timer = Timer(Duration(seconds: _reconnects), () {
|
_timer = Timer(Duration(seconds: _reconnects), () {
|
||||||
@ -655,6 +660,44 @@ class CursorModel with ChangeNotifier {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class QualityMonitorData {
|
||||||
|
String? speed;
|
||||||
|
String? fps;
|
||||||
|
String? delay;
|
||||||
|
String? targetBitrate;
|
||||||
|
String? codecFormat;
|
||||||
|
}
|
||||||
|
|
||||||
|
class QualityMonitorModel with ChangeNotifier {
|
||||||
|
var _show = FFI.getByName('toggle_option', 'show-quality-monitor') == 'true';
|
||||||
|
final _data = QualityMonitorData();
|
||||||
|
|
||||||
|
bool get show => _show;
|
||||||
|
QualityMonitorData get data => _data;
|
||||||
|
|
||||||
|
checkShowQualityMonitor() {
|
||||||
|
final show =
|
||||||
|
FFI.getByName('toggle_option', 'show-quality-monitor') == 'true';
|
||||||
|
if (_show != show) {
|
||||||
|
_show = show;
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateQualityStatus(Map<String, dynamic> evt) {
|
||||||
|
try {
|
||||||
|
if ((evt["speed"] as String).isNotEmpty) _data.speed = evt["speed"];
|
||||||
|
if ((evt["fps"] as String).isNotEmpty) _data.fps = evt["fps"];
|
||||||
|
if ((evt["delay"] as String).isNotEmpty) _data.delay = evt["delay"];
|
||||||
|
if ((evt["target_bitrate"] as String).isNotEmpty)
|
||||||
|
_data.targetBitrate = evt["target_bitrate"];
|
||||||
|
if ((evt["codec_format"] as String).isNotEmpty)
|
||||||
|
_data.codecFormat = evt["codec_format"];
|
||||||
|
notifyListeners();
|
||||||
|
} catch (e) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
enum MouseButtons { left, right, wheel }
|
enum MouseButtons { left, right, wheel }
|
||||||
|
|
||||||
extension ToString on MouseButtons {
|
extension ToString on MouseButtons {
|
||||||
@ -684,6 +727,7 @@ class FFI {
|
|||||||
static final serverModel = ServerModel();
|
static final serverModel = ServerModel();
|
||||||
static final chatModel = ChatModel();
|
static final chatModel = ChatModel();
|
||||||
static final fileModel = FileModel();
|
static final fileModel = FileModel();
|
||||||
|
static final qualityMonitorModel = QualityMonitorModel();
|
||||||
|
|
||||||
static String getId() {
|
static String getId() {
|
||||||
return getByName('remote_id');
|
return getByName('remote_id');
|
||||||
|
@ -9,6 +9,10 @@ import 'model.dart';
|
|||||||
const loginDialogTag = "LOGIN";
|
const loginDialogTag = "LOGIN";
|
||||||
final _emptyIdShow = translate("Generating ...");
|
final _emptyIdShow = translate("Generating ...");
|
||||||
|
|
||||||
|
const kUseTemporaryPassword = "use-temporary-password";
|
||||||
|
const kUsePermanentPassword = "use-permanent-password";
|
||||||
|
const kUseBothPasswords = "use-both-passwords";
|
||||||
|
|
||||||
class ServerModel with ChangeNotifier {
|
class ServerModel with ChangeNotifier {
|
||||||
bool _isStart = false; // Android MainService status
|
bool _isStart = false; // Android MainService status
|
||||||
bool _mediaOk = false;
|
bool _mediaOk = false;
|
||||||
@ -16,6 +20,7 @@ class ServerModel with ChangeNotifier {
|
|||||||
bool _audioOk = false;
|
bool _audioOk = false;
|
||||||
bool _fileOk = false;
|
bool _fileOk = false;
|
||||||
int _connectStatus = 0; // Rendezvous Server status
|
int _connectStatus = 0; // Rendezvous Server status
|
||||||
|
String _verificationMethod = "";
|
||||||
|
|
||||||
final _serverId = TextEditingController(text: _emptyIdShow);
|
final _serverId = TextEditingController(text: _emptyIdShow);
|
||||||
final _serverPasswd = TextEditingController(text: "");
|
final _serverPasswd = TextEditingController(text: "");
|
||||||
@ -34,6 +39,8 @@ class ServerModel with ChangeNotifier {
|
|||||||
|
|
||||||
int get connectStatus => _connectStatus;
|
int get connectStatus => _connectStatus;
|
||||||
|
|
||||||
|
String get verificationMethod => _verificationMethod;
|
||||||
|
|
||||||
TextEditingController get serverId => _serverId;
|
TextEditingController get serverId => _serverId;
|
||||||
|
|
||||||
TextEditingController get serverPasswd => _serverPasswd;
|
TextEditingController get serverPasswd => _serverPasswd;
|
||||||
@ -96,9 +103,29 @@ class ServerModel with ChangeNotifier {
|
|||||||
debugPrint("clients not match!");
|
debugPrint("clients not match!");
|
||||||
updateClientState(res);
|
updateClientState(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updatePasswordModel();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updatePasswordModel() {
|
||||||
|
var update = false;
|
||||||
|
final temporaryPassword = FFI.getByName("temporary_password");
|
||||||
|
final verificationMethod = FFI.getByName("option", "verification-method");
|
||||||
|
if (_serverPasswd.text != temporaryPassword) {
|
||||||
|
_serverPasswd.text = temporaryPassword;
|
||||||
|
update = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_verificationMethod != verificationMethod) {
|
||||||
|
_verificationMethod = verificationMethod;
|
||||||
|
update = true;
|
||||||
|
}
|
||||||
|
if (update) {
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
toggleAudio() async {
|
toggleAudio() async {
|
||||||
if (!_audioOk && !await PermissionManager.check("audio")) {
|
if (!_audioOk && !await PermissionManager.check("audio")) {
|
||||||
final res = await PermissionManager.request("audio");
|
final res = await PermissionManager.request("audio");
|
||||||
@ -195,7 +222,7 @@ class ServerModel with ChangeNotifier {
|
|||||||
FFI.ffiModel.updateEventListener("");
|
FFI.ffiModel.updateEventListener("");
|
||||||
await FFI.invokeMethod("init_service");
|
await FFI.invokeMethod("init_service");
|
||||||
FFI.setByName("start_service");
|
FFI.setByName("start_service");
|
||||||
getIDPasswd();
|
_fetchID();
|
||||||
updateClientState();
|
updateClientState();
|
||||||
Wakelock.enable();
|
Wakelock.enable();
|
||||||
}
|
}
|
||||||
@ -213,54 +240,33 @@ class ServerModel with ChangeNotifier {
|
|||||||
await FFI.invokeMethod("init_input");
|
await FFI.invokeMethod("init_input");
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> updatePassword(String pw) async {
|
Future<bool> setPermanentPassword(String newPW) async {
|
||||||
final oldPasswd = _serverPasswd.text;
|
FFI.setByName("permanent_password", newPW);
|
||||||
FFI.setByName("update_password", pw);
|
|
||||||
await Future.delayed(Duration(milliseconds: 500));
|
await Future.delayed(Duration(milliseconds: 500));
|
||||||
await getIDPasswd(force: true);
|
final pw = FFI.getByName("permanent_password", newPW);
|
||||||
|
if (newPW == pw) {
|
||||||
// check result
|
return true;
|
||||||
if (pw == "") {
|
|
||||||
if (_serverPasswd.text.isNotEmpty && _serverPasswd.text != oldPasswd) {
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
if (_serverPasswd.text == pw) {
|
return false;
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getIDPasswd({bool force = false}) async {
|
_fetchID() async {
|
||||||
if (!force && _serverId.text != _emptyIdShow && _serverPasswd.text != "") {
|
final old = _serverId.text;
|
||||||
return;
|
|
||||||
}
|
|
||||||
var count = 0;
|
var count = 0;
|
||||||
const maxCount = 10;
|
const maxCount = 10;
|
||||||
while (count < maxCount) {
|
while (count < maxCount) {
|
||||||
await Future.delayed(Duration(seconds: 1));
|
await Future.delayed(Duration(seconds: 1));
|
||||||
final id = FFI.getByName("server_id");
|
final id = FFI.getByName("server_id");
|
||||||
final passwd = FFI.getByName("server_password");
|
|
||||||
if (id.isEmpty) {
|
if (id.isEmpty) {
|
||||||
continue;
|
continue;
|
||||||
} else {
|
} else {
|
||||||
_serverId.text = id;
|
_serverId.text = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (passwd.isEmpty) {
|
debugPrint("fetch id again at $count:id:${_serverId.text}");
|
||||||
continue;
|
|
||||||
} else {
|
|
||||||
_serverPasswd.text = passwd;
|
|
||||||
}
|
|
||||||
|
|
||||||
debugPrint(
|
|
||||||
"fetch id & passwd again at $count:id:${_serverId.text},passwd:${_serverPasswd.text}");
|
|
||||||
count++;
|
count++;
|
||||||
if (_serverId.text != _emptyIdShow && _serverPasswd.text.isNotEmpty) {
|
if (_serverId.text != old) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import 'package:dash_chat/dash_chat.dart';
|
import 'package:dash_chat_2/dash_chat_2.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_hbb/common.dart';
|
import 'package:flutter_hbb/common.dart';
|
||||||
import 'package:flutter_hbb/models/chat_model.dart';
|
import 'package:flutter_hbb/models/chat_model.dart';
|
||||||
@ -6,8 +6,6 @@ import 'package:provider/provider.dart';
|
|||||||
import '../models/model.dart';
|
import '../models/model.dart';
|
||||||
import 'home_page.dart';
|
import 'home_page.dart';
|
||||||
|
|
||||||
ChatPage chatPage = ChatPage();
|
|
||||||
|
|
||||||
class ChatPage extends StatelessWidget implements PageShape {
|
class ChatPage extends StatelessWidget implements PageShape {
|
||||||
@override
|
@override
|
||||||
final title = translate("Chat");
|
final title = translate("Chat");
|
||||||
@ -25,7 +23,7 @@ class ChatPage extends StatelessWidget implements PageShape {
|
|||||||
final id = entry.key;
|
final id = entry.key;
|
||||||
final user = entry.value.chatUser;
|
final user = entry.value.chatUser;
|
||||||
return PopupMenuItem<int>(
|
return PopupMenuItem<int>(
|
||||||
child: Text("${user.name} ${user.uid}"),
|
child: Text("${user.firstName} ${user.id}"),
|
||||||
value: id,
|
value: id,
|
||||||
);
|
);
|
||||||
}).toList();
|
}).toList();
|
||||||
@ -46,19 +44,24 @@ class ChatPage extends StatelessWidget implements PageShape {
|
|||||||
return Stack(
|
return Stack(
|
||||||
children: [
|
children: [
|
||||||
DashChat(
|
DashChat(
|
||||||
inputContainerStyle: BoxDecoration(color: Colors.white70),
|
|
||||||
sendOnEnter: false,
|
|
||||||
// if true,reload keyboard everytime,need fix
|
|
||||||
onSend: (chatMsg) {
|
onSend: (chatMsg) {
|
||||||
chatModel.send(chatMsg);
|
chatModel.send(chatMsg);
|
||||||
},
|
},
|
||||||
user: chatModel.me,
|
currentUser: chatModel.me,
|
||||||
messages:
|
messages:
|
||||||
chatModel.messages[chatModel.currentID]?.chatMessages ??
|
chatModel.messages[chatModel.currentID]?.chatMessages ??
|
||||||
[],
|
[],
|
||||||
// default scrollToBottom has bug https://github.com/fayeed/dash_chat/issues/53
|
messageOptions: MessageOptions(
|
||||||
scrollToBottom: false,
|
showOtherUsersAvatar: false,
|
||||||
scrollController: chatModel.scroller,
|
showTime: true,
|
||||||
|
messageDecorationBuilder: (_, __, ___) =>
|
||||||
|
defaultMessageDecoration(
|
||||||
|
color: MyTheme.accent80,
|
||||||
|
borderTopLeft: 8,
|
||||||
|
borderTopRight: 8,
|
||||||
|
borderBottomRight: 8,
|
||||||
|
borderBottomLeft: 8,
|
||||||
|
)),
|
||||||
),
|
),
|
||||||
chatModel.currentID == ChatModel.clientModeID
|
chatModel.currentID == ChatModel.clientModeID
|
||||||
? SizedBox.shrink()
|
? SizedBox.shrink()
|
||||||
@ -70,7 +73,7 @@ class ChatPage extends StatelessWidget implements PageShape {
|
|||||||
color: MyTheme.accent80),
|
color: MyTheme.accent80),
|
||||||
SizedBox(width: 5),
|
SizedBox(width: 5),
|
||||||
Text(
|
Text(
|
||||||
"${currentUser.name ?? ""} ${currentUser.uid ?? ""}",
|
"${currentUser.firstName} ${currentUser.id}",
|
||||||
style: TextStyle(color: MyTheme.accent50),
|
style: TextStyle(color: MyTheme.accent50),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -28,6 +28,7 @@ class _FileManagerPageState extends State<FileManagerPage> {
|
|||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
FFI.connect(widget.id, isFileTransfer: true);
|
FFI.connect(widget.id, isFileTransfer: true);
|
||||||
|
showLoading(translate('Connecting...'));
|
||||||
FFI.ffiModel.updateEventListener(widget.id);
|
FFI.ffiModel.updateEventListener(widget.id);
|
||||||
Wakelock.enable();
|
Wakelock.enable();
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,8 @@ abstract class PageShape extends Widget {
|
|||||||
final List<Widget> appBarActions = [];
|
final List<Widget> appBarActions = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final homeKey = GlobalKey<_HomePageState>();
|
||||||
|
|
||||||
class HomePage extends StatefulWidget {
|
class HomePage extends StatefulWidget {
|
||||||
HomePage({Key? key}) : super(key: key);
|
HomePage({Key? key}) : super(key: key);
|
||||||
|
|
||||||
@ -23,12 +25,23 @@ class _HomePageState extends State<HomePage> {
|
|||||||
var _selectedIndex = 0;
|
var _selectedIndex = 0;
|
||||||
final List<PageShape> _pages = [];
|
final List<PageShape> _pages = [];
|
||||||
|
|
||||||
|
void refreshPages() {
|
||||||
|
setState(() {
|
||||||
|
initPages();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
initPages();
|
||||||
|
}
|
||||||
|
|
||||||
|
void initPages() {
|
||||||
|
_pages.clear();
|
||||||
_pages.add(ConnectionPage());
|
_pages.add(ConnectionPage());
|
||||||
if (isAndroid) {
|
if (isAndroid) {
|
||||||
_pages.addAll([chatPage, ServerPage()]);
|
_pages.addAll([ChatPage(), ServerPage()]);
|
||||||
}
|
}
|
||||||
_pages.add(SettingsPage());
|
_pages.add(SettingsPage());
|
||||||
}
|
}
|
||||||
|
@ -592,6 +592,7 @@ class _RemotePageState extends State<RemotePage> {
|
|||||||
child: Stack(children: [
|
child: Stack(children: [
|
||||||
ImagePaint(),
|
ImagePaint(),
|
||||||
CursorPaint(),
|
CursorPaint(),
|
||||||
|
QualityMonitor(),
|
||||||
getHelpTools(),
|
getHelpTools(),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width: 0,
|
width: 0,
|
||||||
@ -658,7 +659,7 @@ class _RemotePageState extends State<RemotePage> {
|
|||||||
more.add(PopupMenuItem<String>(
|
more.add(PopupMenuItem<String>(
|
||||||
child: Row(
|
child: Row(
|
||||||
children: ([
|
children: ([
|
||||||
Container(width: 100.0, child: Text(translate('OS Password'))),
|
Text(translate('OS Password')),
|
||||||
TextButton(
|
TextButton(
|
||||||
style: flatButtonStyle,
|
style: flatButtonStyle,
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
@ -693,6 +694,13 @@ class _RemotePageState extends State<RemotePage> {
|
|||||||
value: 'block-input'));
|
value: 'block-input'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (FFI.ffiModel.permissions["restart"] != false &&
|
||||||
|
(pi.platform == "Linux" ||
|
||||||
|
pi.platform == "Windows" ||
|
||||||
|
pi.platform == "Mac OS")) {
|
||||||
|
more.add(PopupMenuItem<String>(
|
||||||
|
child: Text(translate('Restart Remote Device')), value: 'restart'));
|
||||||
|
}
|
||||||
() async {
|
() async {
|
||||||
var value = await showMenu(
|
var value = await showMenu(
|
||||||
context: context,
|
context: context,
|
||||||
@ -726,6 +734,8 @@ class _RemotePageState extends State<RemotePage> {
|
|||||||
}
|
}
|
||||||
} else if (value == 'reset_canvas') {
|
} else if (value == 'reset_canvas') {
|
||||||
FFI.cursorModel.reset();
|
FFI.cursorModel.reset();
|
||||||
|
} else if (value == 'restart') {
|
||||||
|
showRestartRemoteDevice(pi, widget.id);
|
||||||
}
|
}
|
||||||
}();
|
}();
|
||||||
}
|
}
|
||||||
@ -948,6 +958,47 @@ class ImagePainter extends CustomPainter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class QualityMonitor extends StatelessWidget {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) => ChangeNotifierProvider.value(
|
||||||
|
value: FFI.qualityMonitorModel,
|
||||||
|
child: Consumer<QualityMonitorModel>(
|
||||||
|
builder: (context, qualityMonitorModel, child) => Positioned(
|
||||||
|
top: 10,
|
||||||
|
right: 10,
|
||||||
|
child: qualityMonitorModel.show
|
||||||
|
? Container(
|
||||||
|
padding: EdgeInsets.all(8),
|
||||||
|
color: MyTheme.canvasColor.withAlpha(120),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
"Speed: ${qualityMonitorModel.data.speed}",
|
||||||
|
style: TextStyle(color: MyTheme.grayBg),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
"FPS: ${qualityMonitorModel.data.fps}",
|
||||||
|
style: TextStyle(color: MyTheme.grayBg),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
"Delay: ${qualityMonitorModel.data.delay} ms",
|
||||||
|
style: TextStyle(color: MyTheme.grayBg),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
"Target Bitrate: ${qualityMonitorModel.data.targetBitrate}kb",
|
||||||
|
style: TextStyle(color: MyTheme.grayBg),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
"Codec: ${qualityMonitorModel.data.codecFormat}",
|
||||||
|
style: TextStyle(color: MyTheme.grayBg),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: SizedBox.shrink())));
|
||||||
|
}
|
||||||
|
|
||||||
CheckboxListTile getToggle(
|
CheckboxListTile getToggle(
|
||||||
void Function(void Function()) setState, option, name) {
|
void Function(void Function()) setState, option, name) {
|
||||||
return CheckboxListTile(
|
return CheckboxListTile(
|
||||||
@ -956,23 +1007,14 @@ CheckboxListTile getToggle(
|
|||||||
setState(() {
|
setState(() {
|
||||||
FFI.setByName('toggle_option', option);
|
FFI.setByName('toggle_option', option);
|
||||||
});
|
});
|
||||||
|
if (option == "show-quality-monitor") {
|
||||||
|
FFI.qualityMonitorModel.checkShowQualityMonitor();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
dense: true,
|
dense: true,
|
||||||
title: Text(translate(name)));
|
title: Text(translate(name)));
|
||||||
}
|
}
|
||||||
|
|
||||||
RadioListTile<String> getRadio(String name, String toValue, String curValue,
|
|
||||||
void Function(String?) onChange) {
|
|
||||||
return RadioListTile<String>(
|
|
||||||
controlAffinity: ListTileControlAffinity.trailing,
|
|
||||||
title: Text(translate(name)),
|
|
||||||
value: toValue,
|
|
||||||
groupValue: curValue,
|
|
||||||
onChanged: onChange,
|
|
||||||
dense: true,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void showOptions() {
|
void showOptions() {
|
||||||
String quality = FFI.getByName('image_quality');
|
String quality = FFI.getByName('image_quality');
|
||||||
if (quality == '') quality = 'balanced';
|
if (quality == '') quality = 'balanced';
|
||||||
@ -1070,6 +1112,27 @@ void showOptions() {
|
|||||||
}, clickMaskDismiss: true, backDismiss: true);
|
}, clickMaskDismiss: true, backDismiss: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void showRestartRemoteDevice(PeerInfo pi, String id) async {
|
||||||
|
final res =
|
||||||
|
await DialogManager.show<bool>((setState, close) => CustomAlertDialog(
|
||||||
|
title: Row(children: [
|
||||||
|
Icon(Icons.warning_amber_sharp,
|
||||||
|
color: Colors.redAccent, size: 28),
|
||||||
|
SizedBox(width: 10),
|
||||||
|
Text(translate("Restart Remote Device")),
|
||||||
|
]),
|
||||||
|
content: Text(
|
||||||
|
"${translate('Are you sure you want to restart')} \n${pi.username}@${pi.hostname}($id) ?"),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => close(), child: Text(translate("Cancel"))),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: () => close(true), child: Text(translate("OK"))),
|
||||||
|
],
|
||||||
|
));
|
||||||
|
if (res == true) FFI.setByName('restart_remote_device');
|
||||||
|
}
|
||||||
|
|
||||||
void showSetOSPassword(bool login) {
|
void showSetOSPassword(bool login) {
|
||||||
final controller = TextEditingController();
|
final controller = TextEditingController();
|
||||||
var password = FFI.getByName('peer_option', "os-password");
|
var password = FFI.getByName('peer_option', "os-password");
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_hbb/models/model.dart';
|
import 'package:flutter_hbb/models/model.dart';
|
||||||
import 'package:flutter_hbb/widgets/dialog.dart';
|
import 'package:flutter_hbb/widgets/dialog.dart';
|
||||||
@ -24,36 +26,84 @@ class ServerPage extends StatelessWidget implements PageShape {
|
|||||||
return [
|
return [
|
||||||
PopupMenuItem(
|
PopupMenuItem(
|
||||||
child: Text(translate("Change ID")),
|
child: Text(translate("Change ID")),
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 16.0),
|
||||||
value: "changeID",
|
value: "changeID",
|
||||||
enabled: false,
|
enabled: false,
|
||||||
),
|
),
|
||||||
PopupMenuItem(
|
PopupMenuItem(
|
||||||
child: Text(translate("Set your own password")),
|
child: Text(translate("Set permanent password")),
|
||||||
value: "changePW",
|
padding: EdgeInsets.symmetric(horizontal: 16.0),
|
||||||
enabled: FFI.serverModel.isStart,
|
value: "setPermanentPassword",
|
||||||
|
enabled:
|
||||||
|
FFI.serverModel.verificationMethod != kUseTemporaryPassword,
|
||||||
),
|
),
|
||||||
PopupMenuItem(
|
PopupMenuItem(
|
||||||
child: Text(translate("Refresh random password")),
|
child: Text(translate("Set temporary password length")),
|
||||||
value: "refreshPW",
|
padding: EdgeInsets.symmetric(horizontal: 16.0),
|
||||||
enabled: FFI.serverModel.isStart,
|
value: "setTemporaryPasswordLength",
|
||||||
)
|
enabled:
|
||||||
|
FFI.serverModel.verificationMethod != kUsePermanentPassword,
|
||||||
|
),
|
||||||
|
const PopupMenuDivider(),
|
||||||
|
PopupMenuItem(
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 0.0),
|
||||||
|
value: kUseTemporaryPassword,
|
||||||
|
child: Container(
|
||||||
|
child: ListTile(
|
||||||
|
title: Text(translate("Use temporary password")),
|
||||||
|
trailing: Icon(
|
||||||
|
Icons.check,
|
||||||
|
color: FFI.serverModel.verificationMethod ==
|
||||||
|
kUseTemporaryPassword
|
||||||
|
? null
|
||||||
|
: Color(0xFFFFFFFF),
|
||||||
|
))),
|
||||||
|
),
|
||||||
|
PopupMenuItem(
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 0.0),
|
||||||
|
value: kUsePermanentPassword,
|
||||||
|
child: ListTile(
|
||||||
|
title: Text(translate("Use permanent password")),
|
||||||
|
trailing: Icon(
|
||||||
|
Icons.check,
|
||||||
|
color: FFI.serverModel.verificationMethod ==
|
||||||
|
kUsePermanentPassword
|
||||||
|
? null
|
||||||
|
: Color(0xFFFFFFFF),
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
PopupMenuItem(
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 0.0),
|
||||||
|
value: kUseBothPasswords,
|
||||||
|
child: ListTile(
|
||||||
|
title: Text(translate("Use both passwords")),
|
||||||
|
trailing: Icon(
|
||||||
|
Icons.check,
|
||||||
|
color: FFI.serverModel.verificationMethod !=
|
||||||
|
kUseTemporaryPassword &&
|
||||||
|
FFI.serverModel.verificationMethod !=
|
||||||
|
kUsePermanentPassword
|
||||||
|
? null
|
||||||
|
: Color(0xFFFFFFFF),
|
||||||
|
)),
|
||||||
|
),
|
||||||
];
|
];
|
||||||
},
|
},
|
||||||
onSelected: (value) {
|
onSelected: (value) {
|
||||||
if (value == "changeID") {
|
if (value == "changeID") {
|
||||||
// TODO
|
// TODO
|
||||||
} else if (value == "changePW") {
|
} else if (value == "setPermanentPassword") {
|
||||||
updatePasswordDialog();
|
setPermanentPasswordDialog();
|
||||||
} else if (value == "refreshPW") {
|
} else if (value == "setTemporaryPasswordLength") {
|
||||||
() async {
|
setTemporaryPasswordLengthDialog();
|
||||||
showLoading(translate("Waiting"));
|
} else if (value == kUsePermanentPassword ||
|
||||||
if (await FFI.serverModel.updatePassword("")) {
|
value == kUseTemporaryPassword ||
|
||||||
showSuccess();
|
value == kUseBothPasswords) {
|
||||||
} else {
|
Map<String, String> msg = Map()
|
||||||
showError();
|
..["name"] = "verification-method"
|
||||||
}
|
..["value"] = value;
|
||||||
debugPrint("end updatePassword");
|
FFI.setByName('option', jsonEncode(msg));
|
||||||
}();
|
FFI.serverModel.updatePasswordModel();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
];
|
];
|
||||||
@ -90,17 +140,13 @@ void checkService() async {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ServerInfo extends StatefulWidget {
|
class ServerInfo extends StatelessWidget {
|
||||||
@override
|
|
||||||
_ServerInfoState createState() => _ServerInfoState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _ServerInfoState extends State<ServerInfo> {
|
|
||||||
final model = FFI.serverModel;
|
final model = FFI.serverModel;
|
||||||
var _passwdShow = false;
|
final emptyController = TextEditingController(text: "-");
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final isPermanent = model.verificationMethod == kUsePermanentPassword;
|
||||||
return model.isStart
|
return model.isStart
|
||||||
? PaddingCard(
|
? PaddingCard(
|
||||||
child: Column(
|
child: Column(
|
||||||
@ -123,24 +169,23 @@ class _ServerInfoState extends State<ServerInfo> {
|
|||||||
),
|
),
|
||||||
TextFormField(
|
TextFormField(
|
||||||
readOnly: true,
|
readOnly: true,
|
||||||
obscureText: !_passwdShow,
|
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 25.0,
|
fontSize: 25.0,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
color: MyTheme.accent),
|
color: MyTheme.accent),
|
||||||
controller: model.serverPasswd,
|
controller: isPermanent ? emptyController : model.serverPasswd,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
icon: const Icon(Icons.lock),
|
icon: const Icon(Icons.lock),
|
||||||
labelText: translate("Password"),
|
labelText: translate("Password"),
|
||||||
labelStyle: TextStyle(
|
labelStyle: TextStyle(
|
||||||
fontWeight: FontWeight.bold, color: MyTheme.accent50),
|
fontWeight: FontWeight.bold, color: MyTheme.accent50),
|
||||||
suffix: IconButton(
|
suffix: isPermanent
|
||||||
icon: Icon(Icons.visibility),
|
? null
|
||||||
onPressed: () {
|
: IconButton(
|
||||||
setState(() {
|
icon: const Icon(Icons.refresh),
|
||||||
_passwdShow = !_passwdShow;
|
onPressed: () {
|
||||||
});
|
FFI.setByName("temporary_password");
|
||||||
})),
|
})),
|
||||||
onSaved: (String? value) {},
|
onSaved: (String? value) {},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -155,7 +200,8 @@ class _ServerInfoState extends State<ServerInfo> {
|
|||||||
Icon(Icons.warning_amber_sharp,
|
Icon(Icons.warning_amber_sharp,
|
||||||
color: Colors.redAccent, size: 24),
|
color: Colors.redAccent, size: 24),
|
||||||
SizedBox(width: 10),
|
SizedBox(width: 10),
|
||||||
Text(
|
Expanded(
|
||||||
|
child: Text(
|
||||||
translate("Service is not running"),
|
translate("Service is not running"),
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontFamily: 'WorkSans',
|
fontFamily: 'WorkSans',
|
||||||
@ -163,7 +209,7 @@ class _ServerInfoState extends State<ServerInfo> {
|
|||||||
fontSize: 18,
|
fontSize: 18,
|
||||||
color: MyTheme.accent80,
|
color: MyTheme.accent80,
|
||||||
),
|
),
|
||||||
)
|
))
|
||||||
],
|
],
|
||||||
)),
|
)),
|
||||||
SizedBox(height: 5),
|
SizedBox(height: 5),
|
||||||
@ -271,30 +317,35 @@ class PermissionRow extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Row(
|
return Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
children: [
|
||||||
Row(
|
Expanded(
|
||||||
children: [
|
flex: 5,
|
||||||
SizedBox(
|
child: FittedBox(
|
||||||
width: 140,
|
fit: BoxFit.scaleDown,
|
||||||
|
alignment: Alignment.centerLeft,
|
||||||
child: Text(name,
|
child: Text(name,
|
||||||
style: TextStyle(fontSize: 16.0, color: MyTheme.accent50))),
|
style:
|
||||||
SizedBox(
|
TextStyle(fontSize: 16.0, color: MyTheme.accent50)))),
|
||||||
width: 50,
|
Expanded(
|
||||||
|
flex: 2,
|
||||||
|
child: FittedBox(
|
||||||
|
fit: BoxFit.scaleDown,
|
||||||
child: Text(isOk ? translate("ON") : translate("OFF"),
|
child: Text(isOk ? translate("ON") : translate("OFF"),
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 16.0,
|
fontSize: 16.0,
|
||||||
color: isOk ? Colors.green : Colors.grey)),
|
color: isOk ? Colors.green : Colors.grey))),
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
TextButton(
|
Expanded(
|
||||||
onPressed: onPressed,
|
flex: 3,
|
||||||
child: Text(
|
child: FittedBox(
|
||||||
translate(isOk ? "CLOSE" : "OPEN"),
|
fit: BoxFit.scaleDown,
|
||||||
style: TextStyle(fontWeight: FontWeight.bold),
|
alignment: Alignment.centerRight,
|
||||||
)),
|
child: TextButton(
|
||||||
const Divider(height: 0)
|
onPressed: onPressed,
|
||||||
|
child: Text(
|
||||||
|
translate(isOk ? "CLOSE" : "OPEN"),
|
||||||
|
style: TextStyle(fontWeight: FontWeight.bold),
|
||||||
|
)))),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -26,11 +26,11 @@ class SettingsPage extends StatefulWidget implements PageShape {
|
|||||||
_SettingsState createState() => _SettingsState();
|
_SettingsState createState() => _SettingsState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
|
const url = 'https://rustdesk.com/';
|
||||||
static const url = 'https://rustdesk.com/';
|
final _hasIgnoreBattery = androidVersion >= 26;
|
||||||
final _hasIgnoreBattery = androidVersion >= 26;
|
var _ignoreBatteryOpt = false;
|
||||||
var _ignoreBatteryOpt = false;
|
|
||||||
|
|
||||||
|
class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
@ -146,6 +146,12 @@ class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
|
|||||||
leading: Icon(Icons.cloud),
|
leading: Icon(Icons.cloud),
|
||||||
onPressed: (context) {
|
onPressed: (context) {
|
||||||
showServerSettings();
|
showServerSettings();
|
||||||
|
}),
|
||||||
|
SettingsTile.navigation(
|
||||||
|
title: Text(translate('Language')),
|
||||||
|
leading: Icon(Icons.translate),
|
||||||
|
onPressed: (context) {
|
||||||
|
showLanguageSettings();
|
||||||
})
|
})
|
||||||
]),
|
]),
|
||||||
SettingsSection(
|
SettingsSection(
|
||||||
@ -185,6 +191,42 @@ void showServerSettings() {
|
|||||||
showServerSettingsWithValue(id, relay, key, api);
|
showServerSettingsWithValue(id, relay, key, api);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void showLanguageSettings() {
|
||||||
|
try {
|
||||||
|
final langs = json.decode(FFI.getByName('langs')) as List<dynamic>;
|
||||||
|
var lang = FFI.getByName('local_option', 'lang');
|
||||||
|
DialogManager.show((setState, close) {
|
||||||
|
final setLang = (v) {
|
||||||
|
if (lang != v) {
|
||||||
|
setState(() {
|
||||||
|
lang = v;
|
||||||
|
});
|
||||||
|
final msg = Map()
|
||||||
|
..['name'] = 'lang'
|
||||||
|
..['value'] = v;
|
||||||
|
FFI.setByName('local_option', json.encode(msg));
|
||||||
|
homeKey.currentState?.refreshPages();
|
||||||
|
Future.delayed(Duration(milliseconds: 200), close);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return CustomAlertDialog(
|
||||||
|
title: SizedBox.shrink(),
|
||||||
|
content: Column(
|
||||||
|
children: [
|
||||||
|
getRadio('Default', '', lang, setLang),
|
||||||
|
Divider(color: MyTheme.border),
|
||||||
|
] +
|
||||||
|
langs.map((e) {
|
||||||
|
final key = e[0] as String;
|
||||||
|
final name = e[1] as String;
|
||||||
|
return getRadio(name, key, lang, setLang);
|
||||||
|
}).toList(),
|
||||||
|
),
|
||||||
|
actions: []);
|
||||||
|
}, backDismiss: true, clickMaskDismiss: true);
|
||||||
|
} catch (_e) {}
|
||||||
|
}
|
||||||
|
|
||||||
void showAbout() {
|
void showAbout() {
|
||||||
DialogManager.show((setState, close) {
|
DialogManager.show((setState, close) {
|
||||||
return CustomAlertDialog(
|
return CustomAlertDialog(
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||||
@ -20,9 +21,10 @@ void showError({Duration duration = SEC1}) {
|
|||||||
showToast(translate("Error"), duration: SEC1);
|
showToast(translate("Error"), duration: SEC1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void updatePasswordDialog() {
|
void setPermanentPasswordDialog() {
|
||||||
final p0 = TextEditingController();
|
final pw = FFI.getByName("permanent_password");
|
||||||
final p1 = TextEditingController();
|
final p0 = TextEditingController(text: pw);
|
||||||
|
final p1 = TextEditingController(text: pw);
|
||||||
var validateLength = false;
|
var validateLength = false;
|
||||||
var validateSame = false;
|
var validateSame = false;
|
||||||
DialogManager.show((setState, close) {
|
DialogManager.show((setState, close) {
|
||||||
@ -86,7 +88,7 @@ void updatePasswordDialog() {
|
|||||||
? () async {
|
? () async {
|
||||||
close();
|
close();
|
||||||
showLoading(translate("Waiting"));
|
showLoading(translate("Waiting"));
|
||||||
if (await FFI.serverModel.updatePassword(p0.text)) {
|
if (await FFI.serverModel.setPermanentPassword(p0.text)) {
|
||||||
showSuccess();
|
showSuccess();
|
||||||
} else {
|
} else {
|
||||||
showError();
|
showError();
|
||||||
@ -100,6 +102,41 @@ void updatePasswordDialog() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setTemporaryPasswordLengthDialog() {
|
||||||
|
List<String> lengths = ['6', '8', '10'];
|
||||||
|
String length = FFI.getByName('option', 'temporary-password-length');
|
||||||
|
var index = lengths.indexOf(length);
|
||||||
|
if (index < 0) index = 0;
|
||||||
|
length = lengths[index];
|
||||||
|
DialogManager.show((setState, close) {
|
||||||
|
final setLength = (newValue) {
|
||||||
|
final oldValue = length;
|
||||||
|
if (oldValue == newValue) return;
|
||||||
|
setState(() {
|
||||||
|
length = newValue;
|
||||||
|
});
|
||||||
|
Map<String, String> msg = Map()
|
||||||
|
..["name"] = "temporary-password-length"
|
||||||
|
..["value"] = newValue;
|
||||||
|
FFI.setByName("option", jsonEncode(msg));
|
||||||
|
FFI.setByName("temporary_password");
|
||||||
|
Future.delayed(Duration(milliseconds: 200), () {
|
||||||
|
close();
|
||||||
|
showSuccess();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
return CustomAlertDialog(
|
||||||
|
title: Text(translate("Set temporary password length")),
|
||||||
|
content: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children:
|
||||||
|
lengths.map((e) => getRadio(e, e, length, setLength)).toList()),
|
||||||
|
actions: [],
|
||||||
|
contentPadding: 14,
|
||||||
|
);
|
||||||
|
}, backDismiss: true, clickMaskDismiss: true);
|
||||||
|
}
|
||||||
|
|
||||||
void enterPasswordDialog(String id) {
|
void enterPasswordDialog(String id) {
|
||||||
final controller = TextEditingController();
|
final controller = TextEditingController();
|
||||||
var remember = FFI.getByName('remember', id) == 'true';
|
var remember = FFI.getByName('remember', id) == 'true';
|
||||||
|
@ -27,7 +27,7 @@ class DraggableChatWindow extends StatelessWidget {
|
|||||||
height: height,
|
height: height,
|
||||||
builder: (_, onPanUpdate) {
|
builder: (_, onPanUpdate) {
|
||||||
return isIOS
|
return isIOS
|
||||||
? chatPage
|
? ChatPage()
|
||||||
: Scaffold(
|
: Scaffold(
|
||||||
resizeToAvoidBottomInset: false,
|
resizeToAvoidBottomInset: false,
|
||||||
appBar: CustomAppBar(
|
appBar: CustomAppBar(
|
||||||
@ -68,7 +68,7 @@ class DraggableChatWindow extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
body: chatPage,
|
body: ChatPage(),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,27 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.0"
|
version: "2.1.0"
|
||||||
|
cached_network_image:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: cached_network_image
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "3.2.1"
|
||||||
|
cached_network_image_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: cached_network_image_platform_interface
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.0"
|
||||||
|
cached_network_image_web:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: cached_network_image_web
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.1"
|
||||||
characters:
|
characters:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -71,6 +92,13 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.2"
|
version: "3.0.2"
|
||||||
|
csslib:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: csslib
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.17.2"
|
||||||
cupertino_icons:
|
cupertino_icons:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -78,13 +106,13 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.4"
|
version: "1.0.4"
|
||||||
dash_chat:
|
dash_chat_2:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: dash_chat
|
name: dash_chat_2
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.16"
|
version: "0.0.12"
|
||||||
device_info:
|
device_info:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -195,6 +223,13 @@ packages:
|
|||||||
description: flutter
|
description: flutter
|
||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.0"
|
version: "0.0.0"
|
||||||
|
flutter_blurhash:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: flutter_blurhash
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.7.0"
|
||||||
flutter_breadcrumb:
|
flutter_breadcrumb:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -202,6 +237,13 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.1"
|
version: "1.0.1"
|
||||||
|
flutter_cache_manager:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: flutter_cache_manager
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "3.3.0"
|
||||||
flutter_launcher_icons:
|
flutter_launcher_icons:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
@ -247,6 +289,13 @@ packages:
|
|||||||
description: flutter
|
description: flutter
|
||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.0"
|
version: "0.0.0"
|
||||||
|
html:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: html
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.15.0"
|
||||||
http:
|
http:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -345,6 +394,13 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.0"
|
version: "1.0.0"
|
||||||
|
octo_image:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: octo_image
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.2"
|
||||||
package_info:
|
package_info:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -449,16 +505,14 @@ packages:
|
|||||||
name: provider
|
name: provider
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "5.0.0"
|
version: "6.0.3"
|
||||||
qr_code_scanner:
|
qr_code_scanner:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
path: "."
|
name: qr_code_scanner
|
||||||
ref: fix_break_changes_platform
|
url: "https://pub.dartlang.org"
|
||||||
resolved-ref: "0feca6f15042c279ff575c559a3430df917b623d"
|
source: hosted
|
||||||
url: "https://github.com/Heap-Hop/qr_code_scanner.git"
|
version: "1.0.0"
|
||||||
source: git
|
|
||||||
version: "0.7.0"
|
|
||||||
quiver:
|
quiver:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -466,6 +520,13 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.0"
|
version: "3.1.0"
|
||||||
|
rxdart:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: rxdart
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.27.5"
|
||||||
settings_ui:
|
settings_ui:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -541,6 +602,20 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.8.2"
|
version: "1.8.2"
|
||||||
|
sqflite:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: sqflite
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.3"
|
||||||
|
sqflite_common:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: sqflite_common
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.2.1+1"
|
||||||
stack_trace:
|
stack_trace:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -562,6 +637,13 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.0"
|
version: "1.1.0"
|
||||||
|
synchronized:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: synchronized
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "3.0.0+2"
|
||||||
term_glyph:
|
term_glyph:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -583,13 +665,6 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.4.0"
|
version: "1.4.0"
|
||||||
transparent_image:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: transparent_image
|
|
||||||
url: "https://pub.dartlang.org"
|
|
||||||
source: hosted
|
|
||||||
version: "2.0.0"
|
|
||||||
tuple:
|
tuple:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -674,6 +749,41 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.2"
|
version: "2.1.2"
|
||||||
|
video_player:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: video_player
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.4.5"
|
||||||
|
video_player_android:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: video_player_android
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.3.8"
|
||||||
|
video_player_avfoundation:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: video_player_avfoundation
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.3.5"
|
||||||
|
video_player_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: video_player_platform_interface
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "5.1.3"
|
||||||
|
video_player_web:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: video_player_web
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.12"
|
||||||
wakelock:
|
wakelock:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -745,5 +855,5 @@ packages:
|
|||||||
source: hosted
|
source: hosted
|
||||||
version: "0.1.0"
|
version: "0.1.0"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=2.17.0-0 <3.0.0"
|
dart: ">=2.17.0 <3.0.0"
|
||||||
flutter: ">=3.0.0"
|
flutter: ">=3.0.0"
|
||||||
|
@ -32,7 +32,7 @@ dependencies:
|
|||||||
ffi: ^1.1.2
|
ffi: ^1.1.2
|
||||||
path_provider: ^2.0.2
|
path_provider: ^2.0.2
|
||||||
external_path: ^1.0.1
|
external_path: ^1.0.1
|
||||||
provider: ^5.0.0
|
provider: ^6.0.3
|
||||||
tuple: ^2.0.0
|
tuple: ^2.0.0
|
||||||
wakelock: ^0.5.2
|
wakelock: ^0.5.2
|
||||||
device_info: ^2.0.2
|
device_info: ^2.0.2
|
||||||
@ -41,15 +41,12 @@ dependencies:
|
|||||||
url_launcher: ^6.0.9
|
url_launcher: ^6.0.9
|
||||||
shared_preferences: ^2.0.6
|
shared_preferences: ^2.0.6
|
||||||
toggle_switch: ^1.4.0
|
toggle_switch: ^1.4.0
|
||||||
dash_chat: ^1.1.16
|
dash_chat_2: ^0.0.12
|
||||||
draggable_float_widget: ^0.0.2
|
draggable_float_widget: ^0.0.2
|
||||||
settings_ui: ^2.0.2
|
settings_ui: ^2.0.2
|
||||||
flutter_breadcrumb: ^1.0.1
|
flutter_breadcrumb: ^1.0.1
|
||||||
http: ^0.13.4
|
http: ^0.13.4
|
||||||
qr_code_scanner:
|
qr_code_scanner: ^1.0.0
|
||||||
git:
|
|
||||||
url: https://github.com/Heap-Hop/qr_code_scanner.git
|
|
||||||
ref: fix_break_changes_platform
|
|
||||||
zxing2: ^0.1.0
|
zxing2: ^0.1.0
|
||||||
image_picker: ^0.8.5
|
image_picker: ^0.8.5
|
||||||
image: ^3.1.3
|
image: ^3.1.3
|
||||||
|
76
lang.py
76
lang.py
@ -1,17 +1,17 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
# Based on 'cn.rs', generate entries that are not completed in other languages
|
import os
|
||||||
|
import glob
|
||||||
import os
|
import sys
|
||||||
import glob
|
import csv
|
||||||
|
|
||||||
def get_lang(lang):
|
def get_lang(lang):
|
||||||
out = {}
|
out = {}
|
||||||
for ln in open('./src/lang/%s.rs'%lang):
|
for ln in open('./src/lang/%s.rs'%lang):
|
||||||
ln = ln.strip()
|
ln = ln.strip()
|
||||||
if ln.startswith('("'):
|
if ln.startswith('("'):
|
||||||
k,v = line_split(ln)
|
k, v = line_split(ln)
|
||||||
out[k] = v
|
out[k] = v
|
||||||
return out
|
return out
|
||||||
|
|
||||||
def line_split(line):
|
def line_split(line):
|
||||||
@ -19,28 +19,64 @@ def line_split(line):
|
|||||||
assert(len(toks) == 2)
|
assert(len(toks) == 2)
|
||||||
k = toks[0][2:]
|
k = toks[0][2:]
|
||||||
v = toks[1][:-3]
|
v = toks[1][:-3]
|
||||||
return k,v
|
return k, v
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
if len(sys.argv) == 1:
|
||||||
|
expand()
|
||||||
|
elif sys.argv[1] == '1':
|
||||||
|
to_csv()
|
||||||
|
else:
|
||||||
|
to_rs(sys.argv[1])
|
||||||
|
|
||||||
|
|
||||||
|
def expand():
|
||||||
for fn in glob.glob('./src/lang/*'):
|
for fn in glob.glob('./src/lang/*'):
|
||||||
lang = os.path.basename(fn)[:-3]
|
lang = os.path.basename(fn)[:-3]
|
||||||
if lang in ['en','cn']: continue
|
if lang in ['en','cn']: continue
|
||||||
fw = open("%s.rs.gen"%lang, "wb+")
|
|
||||||
dict = get_lang(lang)
|
dict = get_lang(lang)
|
||||||
|
fw = open("%s.rs"%lang, "wt")
|
||||||
for line in open('./src/lang/cn.rs'):
|
for line in open('./src/lang/cn.rs'):
|
||||||
line_strip = line.strip()
|
line_strip = line.strip()
|
||||||
if line_strip.startswith('("'):
|
if line_strip.startswith('("'):
|
||||||
k,v = line_split(line_strip)
|
k, v = line_split(line_strip)
|
||||||
if k in dict:
|
if k in dict:
|
||||||
line = line.replace(v, dict[k])
|
line = line.replace(v, dict[k])
|
||||||
else:
|
else:
|
||||||
line = line.replace(v, "")
|
line = line.replace(v, "")
|
||||||
fw.write(line.encode())
|
fw.write(line)
|
||||||
else:
|
else:
|
||||||
fw.write(line.encode())
|
fw.write(line)
|
||||||
fw.close()
|
fw.close()
|
||||||
os.remove("./src/lang/%s.rs"%lang)
|
|
||||||
os.rename(fw.name, "./src/lang/%s.rs"%lang)
|
|
||||||
|
def to_csv():
|
||||||
main()
|
for fn in glob.glob('./src/lang/*.rs'):
|
||||||
|
lang = os.path.basename(fn)[:-3]
|
||||||
|
csvfile = open('./src/lang/%s.csv'%lang, "wt")
|
||||||
|
csvwriter = csv.writer(csvfile)
|
||||||
|
for line in open(fn):
|
||||||
|
line_strip = line.strip()
|
||||||
|
if line_strip.startswith('("'):
|
||||||
|
k, v = line_split(line_strip)
|
||||||
|
csvwriter.writerow([k, v])
|
||||||
|
csvfile.close()
|
||||||
|
|
||||||
|
|
||||||
|
def to_rs(lang):
|
||||||
|
csvfile = open('%s.csv'%lang, "rt")
|
||||||
|
fw = open("./src/lang/%s.rs"%lang, "wt")
|
||||||
|
fw.write('''lazy_static::lazy_static! {
|
||||||
|
pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||||
|
[
|
||||||
|
''')
|
||||||
|
for row in csv.reader(csvfile):
|
||||||
|
fw.write(' ("%s", "%s"),\n'%(row[0].replace('"', '\"'), row[1].replace('"', '\"')))
|
||||||
|
fw.write(''' ].iter().cloned().collect();
|
||||||
|
}
|
||||||
|
''')
|
||||||
|
fw.close()
|
||||||
|
|
||||||
|
|
||||||
|
main()
|
||||||
|
@ -64,6 +64,7 @@ message LoginRequest {
|
|||||||
}
|
}
|
||||||
bool video_ack_required = 9;
|
bool video_ack_required = 9;
|
||||||
uint64 session_id = 10;
|
uint64 session_id = 10;
|
||||||
|
string version = 11;
|
||||||
}
|
}
|
||||||
|
|
||||||
message ChatMessage { string text = 1; }
|
message ChatMessage { string text = 1; }
|
||||||
@ -434,6 +435,7 @@ message PermissionInfo {
|
|||||||
Clipboard = 2;
|
Clipboard = 2;
|
||||||
Audio = 3;
|
Audio = 3;
|
||||||
File = 4;
|
File = 4;
|
||||||
|
Restart = 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
Permission permission = 1;
|
Permission permission = 1;
|
||||||
@ -506,33 +508,33 @@ message AudioFrame {
|
|||||||
message BackNotification {
|
message BackNotification {
|
||||||
// no need to consider block input by someone else
|
// no need to consider block input by someone else
|
||||||
enum BlockInputState {
|
enum BlockInputState {
|
||||||
StateUnknown = 1;
|
BlkStateUnknown = 0;
|
||||||
OnSucceeded = 2;
|
BlkOnSucceeded = 2;
|
||||||
OnFailed = 3;
|
BlkOnFailed = 3;
|
||||||
OffSucceeded = 4;
|
BlkOffSucceeded = 4;
|
||||||
OffFailed = 5;
|
BlkOffFailed = 5;
|
||||||
}
|
}
|
||||||
enum PrivacyModeState {
|
enum PrivacyModeState {
|
||||||
StateUnknown = 1;
|
PrvStateUnknown = 0;
|
||||||
// Privacy mode on by someone else
|
// Privacy mode on by someone else
|
||||||
OnByOther = 2;
|
PrvOnByOther = 2;
|
||||||
// Privacy mode is not supported on the remote side
|
// Privacy mode is not supported on the remote side
|
||||||
NotSupported = 3;
|
PrvNotSupported = 3;
|
||||||
// Privacy mode on by self
|
// Privacy mode on by self
|
||||||
OnSucceeded = 4;
|
PrvOnSucceeded = 4;
|
||||||
// Privacy mode on by self, but denied
|
// Privacy mode on by self, but denied
|
||||||
OnFailedDenied = 5;
|
PrvOnFailedDenied = 5;
|
||||||
// Some plugins are not found
|
// Some plugins are not found
|
||||||
OnFailedPlugin = 6;
|
PrvOnFailedPlugin = 6;
|
||||||
// Privacy mode on by self, but failed
|
// Privacy mode on by self, but failed
|
||||||
OnFailed = 7;
|
PrvOnFailed = 7;
|
||||||
// Privacy mode off by self
|
// Privacy mode off by self
|
||||||
OffSucceeded = 8;
|
PrvOffSucceeded = 8;
|
||||||
// Ctrl + P
|
// Ctrl + P
|
||||||
OffByPeer = 9;
|
PrvOffByPeer = 9;
|
||||||
// Privacy mode off by self, but failed
|
// Privacy mode off by self, but failed
|
||||||
OffFailed = 10;
|
PrvOffFailed = 10;
|
||||||
OffUnknown = 11;
|
PrvOffUnknown = 11;
|
||||||
}
|
}
|
||||||
|
|
||||||
oneof union {
|
oneof union {
|
||||||
@ -552,6 +554,7 @@ message Misc {
|
|||||||
bool refresh_video = 10;
|
bool refresh_video = 10;
|
||||||
bool video_received = 12;
|
bool video_received = 12;
|
||||||
BackNotification back_notification = 13;
|
BackNotification back_notification = 13;
|
||||||
|
bool restart_remote_device = 14;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
log,
|
log,
|
||||||
password_security::config::{
|
password_security::{
|
||||||
decrypt_str_or_original, decrypt_vec_or_original, encrypt_str_or_original,
|
decrypt_str_or_original, decrypt_vec_or_original, encrypt_str_or_original,
|
||||||
encrypt_vec_or_original,
|
encrypt_vec_or_original,
|
||||||
},
|
},
|
||||||
@ -46,6 +46,7 @@ lazy_static::lazy_static! {
|
|||||||
pub static ref ONLINE: Arc<Mutex<HashMap<String, i64>>> = Default::default();
|
pub static ref ONLINE: Arc<Mutex<HashMap<String, i64>>> = Default::default();
|
||||||
pub static ref PROD_RENDEZVOUS_SERVER: Arc<RwLock<String>> = Default::default();
|
pub static ref PROD_RENDEZVOUS_SERVER: Arc<RwLock<String>> = Default::default();
|
||||||
pub static ref APP_NAME: Arc<RwLock<String>> = Arc::new(RwLock::new("RustDesk".to_owned()));
|
pub static ref APP_NAME: Arc<RwLock<String>> = Arc::new(RwLock::new("RustDesk".to_owned()));
|
||||||
|
static ref KEY_PAIR: Arc<Mutex<Option<(Vec<u8>, Vec<u8>)>>> = Default::default();
|
||||||
}
|
}
|
||||||
#[cfg(target_os = "android")]
|
#[cfg(target_os = "android")]
|
||||||
lazy_static::lazy_static! {
|
lazy_static::lazy_static! {
|
||||||
@ -69,6 +70,7 @@ pub const RENDEZVOUS_SERVERS: &'static [&'static str] = &[
|
|||||||
"rs-sg.rustdesk.com",
|
"rs-sg.rustdesk.com",
|
||||||
"rs-cn.rustdesk.com",
|
"rs-cn.rustdesk.com",
|
||||||
];
|
];
|
||||||
|
pub const RS_PUB_KEY: &'static str = "OeVuKk5nlHiXp+APNn0Y3pC1Iwpwn44JGqrQCsWqmBw=";
|
||||||
pub const RENDEZVOUS_PORT: i32 = 21116;
|
pub const RENDEZVOUS_PORT: i32 = 21116;
|
||||||
pub const RELAY_PORT: i32 = 21117;
|
pub const RELAY_PORT: i32 = 21117;
|
||||||
|
|
||||||
@ -87,7 +89,7 @@ pub struct Config {
|
|||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
salt: String,
|
salt: String,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub key_pair: (Vec<u8>, Vec<u8>), // sk, pk
|
key_pair: (Vec<u8>, Vec<u8>), // sk, pk
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
key_confirmed: bool,
|
key_confirmed: bool,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
@ -217,7 +219,8 @@ impl Config2 {
|
|||||||
fn load() -> Config2 {
|
fn load() -> Config2 {
|
||||||
let mut config = Config::load_::<Config2>("2");
|
let mut config = Config::load_::<Config2>("2");
|
||||||
if let Some(mut socks) = config.socks {
|
if let Some(mut socks) = config.socks {
|
||||||
let (password, store) = decrypt_str_or_original(&socks.password, PASSWORD_ENC_VERSION);
|
let (password, _, store) =
|
||||||
|
decrypt_str_or_original(&socks.password, PASSWORD_ENC_VERSION);
|
||||||
socks.password = password;
|
socks.password = password;
|
||||||
config.socks = Some(socks);
|
config.socks = Some(socks);
|
||||||
if store {
|
if store {
|
||||||
@ -268,6 +271,11 @@ pub fn load_path<T: serde::Serialize + serde::de::DeserializeOwned + Default + s
|
|||||||
cfg
|
cfg
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn store_path<T: serde::Serialize>(path: PathBuf, cfg: T) -> crate::ResultType<()> {
|
||||||
|
Ok(confy::store_path(path, cfg)?)
|
||||||
|
}
|
||||||
|
|
||||||
impl Config {
|
impl Config {
|
||||||
fn load_<T: serde::Serialize + serde::de::DeserializeOwned + Default + std::fmt::Debug>(
|
fn load_<T: serde::Serialize + serde::de::DeserializeOwned + Default + std::fmt::Debug>(
|
||||||
suffix: &str,
|
suffix: &str,
|
||||||
@ -283,14 +291,14 @@ impl Config {
|
|||||||
|
|
||||||
fn store_<T: serde::Serialize>(config: &T, suffix: &str) {
|
fn store_<T: serde::Serialize>(config: &T, suffix: &str) {
|
||||||
let file = Self::file_(suffix);
|
let file = Self::file_(suffix);
|
||||||
if let Err(err) = confy::store_path(file, config) {
|
if let Err(err) = store_path(file, config) {
|
||||||
log::error!("Failed to store config: {}", err);
|
log::error!("Failed to store config: {}", err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load() -> Config {
|
fn load() -> Config {
|
||||||
let mut config = Config::load_::<Config>("");
|
let mut config = Config::load_::<Config>("");
|
||||||
let (password, store) = decrypt_str_or_original(&config.password, PASSWORD_ENC_VERSION);
|
let (password, _, store) = decrypt_str_or_original(&config.password, PASSWORD_ENC_VERSION);
|
||||||
config.password = password;
|
config.password = password;
|
||||||
if store {
|
if store {
|
||||||
config.store();
|
config.store();
|
||||||
@ -313,6 +321,10 @@ impl Config {
|
|||||||
Config::with_extension(Self::path(name))
|
Config::with_extension(Self::path(name))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.id.is_empty() || self.key_pair.0.is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_home() -> PathBuf {
|
pub fn get_home() -> PathBuf {
|
||||||
#[cfg(any(target_os = "android", target_os = "ios"))]
|
#[cfg(any(target_os = "android", target_os = "ios"))]
|
||||||
return Self::path(APP_HOME_DIR.read().unwrap().as_str());
|
return Self::path(APP_HOME_DIR.read().unwrap().as_str());
|
||||||
@ -534,9 +546,9 @@ impl Config {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_auto_password() -> String {
|
pub fn get_auto_password(length: usize) -> String {
|
||||||
let mut rng = rand::thread_rng();
|
let mut rng = rand::thread_rng();
|
||||||
(0..6)
|
(0..length)
|
||||||
.map(|_| CHARS[rng.gen::<usize>() % CHARS.len()])
|
.map(|_| CHARS[rng.gen::<usize>() % CHARS.len()])
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
@ -574,24 +586,26 @@ impl Config {
|
|||||||
config.store();
|
config.store();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_key_pair(pair: (Vec<u8>, Vec<u8>)) {
|
|
||||||
let mut config = CONFIG.write().unwrap();
|
|
||||||
if config.key_pair == pair {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
config.key_pair = pair;
|
|
||||||
config.store();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_key_pair() -> (Vec<u8>, Vec<u8>) {
|
pub fn get_key_pair() -> (Vec<u8>, Vec<u8>) {
|
||||||
// lock here to make sure no gen_keypair more than once
|
// lock here to make sure no gen_keypair more than once
|
||||||
let mut config = CONFIG.write().unwrap();
|
// no use of CONFIG directly here to ensure no recursive calling in Config::load because of password dec which calling this function
|
||||||
|
let mut lock = KEY_PAIR.lock().unwrap();
|
||||||
|
if let Some(p) = lock.as_ref() {
|
||||||
|
return p.clone();
|
||||||
|
}
|
||||||
|
let mut config = Config::load_::<Config>("");
|
||||||
if config.key_pair.0.is_empty() {
|
if config.key_pair.0.is_empty() {
|
||||||
let (pk, sk) = sign::gen_keypair();
|
let (pk, sk) = sign::gen_keypair();
|
||||||
config.key_pair = (sk.0.to_vec(), pk.0.into());
|
let key_pair = (sk.0.to_vec(), pk.0.into());
|
||||||
config.store();
|
config.key_pair = key_pair.clone();
|
||||||
|
std::thread::spawn(|| {
|
||||||
|
let mut config = CONFIG.write().unwrap();
|
||||||
|
config.key_pair = key_pair;
|
||||||
|
config.store();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
config.key_pair.clone()
|
*lock = Some(config.key_pair.clone());
|
||||||
|
return config.key_pair;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_id() -> String {
|
pub fn get_id() -> String {
|
||||||
@ -657,7 +671,7 @@ impl Config {
|
|||||||
log::info!("id updated from {} to {}", id, new_id);
|
log::info!("id updated from {} to {}", id, new_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_security_password(password: &str) {
|
pub fn set_permanent_password(password: &str) {
|
||||||
let mut config = CONFIG.write().unwrap();
|
let mut config = CONFIG.write().unwrap();
|
||||||
if password == config.password {
|
if password == config.password {
|
||||||
return;
|
return;
|
||||||
@ -666,7 +680,7 @@ impl Config {
|
|||||||
config.store();
|
config.store();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_security_password() -> String {
|
pub fn get_permanent_password() -> String {
|
||||||
CONFIG.read().unwrap().password.clone()
|
CONFIG.read().unwrap().password.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -682,7 +696,7 @@ impl Config {
|
|||||||
pub fn get_salt() -> String {
|
pub fn get_salt() -> String {
|
||||||
let mut salt = CONFIG.read().unwrap().salt.clone();
|
let mut salt = CONFIG.read().unwrap().salt.clone();
|
||||||
if salt.is_empty() {
|
if salt.is_empty() {
|
||||||
salt = Config::get_auto_password();
|
salt = Config::get_auto_password(6);
|
||||||
Config::set_salt(&salt);
|
Config::set_salt(&salt);
|
||||||
}
|
}
|
||||||
salt
|
salt
|
||||||
@ -742,17 +756,17 @@ impl PeerConfig {
|
|||||||
Ok(config) => {
|
Ok(config) => {
|
||||||
let mut config: PeerConfig = config;
|
let mut config: PeerConfig = config;
|
||||||
let mut store = false;
|
let mut store = false;
|
||||||
let (password, store2) =
|
let (password, _, store2) =
|
||||||
decrypt_vec_or_original(&config.password, PASSWORD_ENC_VERSION);
|
decrypt_vec_or_original(&config.password, PASSWORD_ENC_VERSION);
|
||||||
config.password = password;
|
config.password = password;
|
||||||
store = store || store2;
|
store = store || store2;
|
||||||
config.options.get_mut("rdp_password").map(|v| {
|
config.options.get_mut("rdp_password").map(|v| {
|
||||||
let (password, store2) = decrypt_str_or_original(v, PASSWORD_ENC_VERSION);
|
let (password, _, store2) = decrypt_str_or_original(v, PASSWORD_ENC_VERSION);
|
||||||
*v = password;
|
*v = password;
|
||||||
store = store || store2;
|
store = store || store2;
|
||||||
});
|
});
|
||||||
config.options.get_mut("os-password").map(|v| {
|
config.options.get_mut("os-password").map(|v| {
|
||||||
let (password, store2) = decrypt_str_or_original(v, PASSWORD_ENC_VERSION);
|
let (password, _, store2) = decrypt_str_or_original(v, PASSWORD_ENC_VERSION);
|
||||||
*v = password;
|
*v = password;
|
||||||
store = store || store2;
|
store = store || store2;
|
||||||
});
|
});
|
||||||
@ -780,7 +794,7 @@ impl PeerConfig {
|
|||||||
.options
|
.options
|
||||||
.get_mut("os-password")
|
.get_mut("os-password")
|
||||||
.map(|v| *v = encrypt_str_or_original(v, PASSWORD_ENC_VERSION));
|
.map(|v| *v = encrypt_str_or_original(v, PASSWORD_ENC_VERSION));
|
||||||
if let Err(err) = confy::store_path(Self::path(id), config) {
|
if let Err(err) = store_path(Self::path(id), config) {
|
||||||
log::error!("Failed to store config: {}", err);
|
log::error!("Failed to store config: {}", err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -950,7 +964,7 @@ impl LanPeers {
|
|||||||
let f = LanPeers {
|
let f = LanPeers {
|
||||||
peers: peers.clone(),
|
peers: peers.clone(),
|
||||||
};
|
};
|
||||||
if let Err(err) = confy::store_path(Config::file_("_lan_peers"), f) {
|
if let Err(err) = store_path(Config::file_("_lan_peers"), f) {
|
||||||
log::error!("Failed to store lan peers: {}", err);
|
log::error!("Failed to store lan peers: {}", err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -276,7 +276,7 @@ impl TransferJob {
|
|||||||
show_hidden: bool,
|
show_hidden: bool,
|
||||||
is_remote: bool,
|
is_remote: bool,
|
||||||
files: Vec<FileEntry>,
|
files: Vec<FileEntry>,
|
||||||
enable_override_detection: bool,
|
enable_overwrite_detection: bool,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
log::info!("new write {}", path);
|
log::info!("new write {}", path);
|
||||||
let total_size = files.iter().map(|x| x.size as u64).sum();
|
let total_size = files.iter().map(|x| x.size as u64).sum();
|
||||||
@ -289,7 +289,7 @@ impl TransferJob {
|
|||||||
is_remote,
|
is_remote,
|
||||||
files,
|
files,
|
||||||
total_size,
|
total_size,
|
||||||
enable_overwrite_detection: enable_override_detection,
|
enable_overwrite_detection,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -301,7 +301,7 @@ impl TransferJob {
|
|||||||
file_num: i32,
|
file_num: i32,
|
||||||
show_hidden: bool,
|
show_hidden: bool,
|
||||||
is_remote: bool,
|
is_remote: bool,
|
||||||
enable_override_detection: bool,
|
enable_overwrite_detection: bool,
|
||||||
) -> ResultType<Self> {
|
) -> ResultType<Self> {
|
||||||
log::info!("new read {}", path);
|
log::info!("new read {}", path);
|
||||||
let files = get_recursive_files(&path, show_hidden)?;
|
let files = get_recursive_files(&path, show_hidden)?;
|
||||||
@ -315,7 +315,7 @@ impl TransferJob {
|
|||||||
is_remote,
|
is_remote,
|
||||||
files,
|
files,
|
||||||
total_size,
|
total_size,
|
||||||
enable_overwrite_detection: enable_override_detection,
|
enable_overwrite_detection,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
pub mod compress;
|
pub mod compress;
|
||||||
pub mod protos;
|
|
||||||
pub mod platform;
|
pub mod platform;
|
||||||
pub use protos::message as message_proto;
|
pub mod protos;
|
||||||
pub use protos::rendezvous as rendezvous_proto;
|
|
||||||
pub use bytes;
|
pub use bytes;
|
||||||
use config::Config;
|
use config::Config;
|
||||||
pub use futures;
|
pub use futures;
|
||||||
pub use protobuf;
|
pub use protobuf;
|
||||||
|
pub use protos::message as message_proto;
|
||||||
|
pub use protos::rendezvous as rendezvous_proto;
|
||||||
use std::{
|
use std::{
|
||||||
fs::File,
|
fs::File,
|
||||||
io::{self, BufRead},
|
io::{self, BufRead},
|
||||||
|
@ -1,316 +1,143 @@
|
|||||||
pub mod password {
|
use crate::config::Config;
|
||||||
use crate::config::Config;
|
use sodiumoxide::base64;
|
||||||
use std::{
|
use std::sync::{Arc, RwLock};
|
||||||
fmt::Display,
|
|
||||||
str::FromStr,
|
|
||||||
sync::{Arc, RwLock},
|
|
||||||
};
|
|
||||||
|
|
||||||
lazy_static::lazy_static! {
|
lazy_static::lazy_static! {
|
||||||
pub static ref RANDOM_PASSWORD:Arc<RwLock<String>> = Arc::new(RwLock::new(Config::get_auto_password()));
|
pub static ref TEMPORARY_PASSWORD:Arc<RwLock<String>> = Arc::new(RwLock::new(Config::get_auto_password(temporary_password_length())));
|
||||||
}
|
}
|
||||||
|
|
||||||
const SECURITY_ENABLED: &'static str = "security-password-enabled";
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
const RANDOM_ENABLED: &'static str = "random-password-enabled";
|
enum VerificationMethod {
|
||||||
const ONETIME_ENABLED: &'static str = "onetime-password-enabled";
|
OnlyUseTemporaryPassword,
|
||||||
const ONETIME_ACTIVATED: &'static str = "onetime-password-activated";
|
OnlyUsePermanentPassword,
|
||||||
const UPDATE_METHOD: &'static str = "random-password-update-method";
|
UseBothPasswords,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
// Should only be called in server
|
||||||
pub enum UpdateMethod {
|
pub fn update_temporary_password() {
|
||||||
KEEP,
|
*TEMPORARY_PASSWORD.write().unwrap() = Config::get_auto_password(temporary_password_length());
|
||||||
UPDATE,
|
}
|
||||||
DISABLE,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromStr for UpdateMethod {
|
// Should only be called in server
|
||||||
type Err = ();
|
pub fn temporary_password() -> String {
|
||||||
|
TEMPORARY_PASSWORD.read().unwrap().clone()
|
||||||
|
}
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
fn verification_method() -> VerificationMethod {
|
||||||
if s == "KEEP" {
|
let method = Config::get_option("verification-method");
|
||||||
Ok(Self::KEEP)
|
if method == "use-temporary-password" {
|
||||||
} else if s == "UPDATE" {
|
VerificationMethod::OnlyUseTemporaryPassword
|
||||||
Ok(Self::UPDATE)
|
} else if method == "use-permanent-password" {
|
||||||
} else if s == "DISABLE" {
|
VerificationMethod::OnlyUsePermanentPassword
|
||||||
Ok(Self::DISABLE)
|
} else {
|
||||||
} else {
|
VerificationMethod::UseBothPasswords // default
|
||||||
Err(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for UpdateMethod {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
match self {
|
|
||||||
UpdateMethod::KEEP => write!(f, "KEEP"),
|
|
||||||
UpdateMethod::UPDATE => write!(f, "UPDATE"),
|
|
||||||
UpdateMethod::DISABLE => write!(f, "DISABLE"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_random_password(password: &str) {
|
|
||||||
*RANDOM_PASSWORD.write().unwrap() = password.to_owned();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn random_password() -> String {
|
|
||||||
let mut password = RANDOM_PASSWORD.read().unwrap().clone();
|
|
||||||
if password.is_empty() {
|
|
||||||
password = Config::get_auto_password();
|
|
||||||
set_random_password(&password);
|
|
||||||
}
|
|
||||||
password
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn random_password_valid() -> bool {
|
|
||||||
if random_enabled() {
|
|
||||||
onetime_password_activated() || !onetime_password_enabled()
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn passwords() -> Vec<String> {
|
|
||||||
let mut v = vec![];
|
|
||||||
if random_password_valid() {
|
|
||||||
v.push(random_password());
|
|
||||||
}
|
|
||||||
if security_enabled() {
|
|
||||||
v.push(Config::get_security_password());
|
|
||||||
}
|
|
||||||
v
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn after_session(authorized: bool) {
|
|
||||||
if authorized && random_enabled() {
|
|
||||||
UpdateMethod::from_str(&update_method())
|
|
||||||
.map(|method| match method {
|
|
||||||
UpdateMethod::KEEP => {}
|
|
||||||
UpdateMethod::UPDATE => set_random_password(&Config::get_auto_password()),
|
|
||||||
UpdateMethod::DISABLE => set_random_enabled(false),
|
|
||||||
})
|
|
||||||
.ok();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn update_method() -> String {
|
|
||||||
let mut method = Config::get_option(UPDATE_METHOD);
|
|
||||||
if UpdateMethod::from_str(&method).is_err() {
|
|
||||||
method = UpdateMethod::KEEP.to_string(); // default is keep
|
|
||||||
set_update_method(&method);
|
|
||||||
}
|
|
||||||
method
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_update_method(method: &str) {
|
|
||||||
Config::set_option(UPDATE_METHOD.to_owned(), method.to_owned());
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn random_enabled() -> bool {
|
|
||||||
str2bool(RANDOM_ENABLED, true, || {
|
|
||||||
set_onetime_password_activated(false);
|
|
||||||
set_random_password(&Config::get_auto_password());
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_random_enabled(enabled: bool) {
|
|
||||||
if enabled != random_enabled() {
|
|
||||||
Config::set_option(RANDOM_ENABLED.to_owned(), bool2str(enabled));
|
|
||||||
set_onetime_password_activated(false);
|
|
||||||
if enabled {
|
|
||||||
set_random_password(&Config::get_auto_password());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn security_enabled() -> bool {
|
|
||||||
str2bool(SECURITY_ENABLED, true, || {})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_security_enabled(enabled: bool) {
|
|
||||||
if enabled != security_enabled() {
|
|
||||||
Config::set_option(SECURITY_ENABLED.to_owned(), bool2str(enabled));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn onetime_password_enabled() -> bool {
|
|
||||||
str2bool(ONETIME_ENABLED, false, || {
|
|
||||||
set_onetime_password_activated(false);
|
|
||||||
set_random_password(&Config::get_auto_password());
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_onetime_password_enabled(enabled: bool) {
|
|
||||||
if enabled != onetime_password_enabled() {
|
|
||||||
Config::set_option(ONETIME_ENABLED.to_owned(), bool2str(enabled));
|
|
||||||
set_onetime_password_activated(false);
|
|
||||||
set_random_password(&Config::get_auto_password());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn onetime_password_activated() -> bool {
|
|
||||||
str2bool(ONETIME_ACTIVATED, false, || {})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_onetime_password_activated(activated: bool) {
|
|
||||||
if activated != onetime_password_activated() {
|
|
||||||
Config::set_option(ONETIME_ACTIVATED.to_owned(), bool2str(activated));
|
|
||||||
if activated {
|
|
||||||
set_random_password(&Config::get_auto_password());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// notice: Function nesting
|
|
||||||
fn str2bool(key: &str, default: bool, default_set: impl Fn()) -> bool {
|
|
||||||
let option = Config::get_option(key);
|
|
||||||
if option == "Y" {
|
|
||||||
true
|
|
||||||
} else if option == "N" {
|
|
||||||
false
|
|
||||||
} else {
|
|
||||||
Config::set_option(key.to_owned(), bool2str(default));
|
|
||||||
default_set();
|
|
||||||
default
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn bool2str(option: bool) -> String {
|
|
||||||
if option { "Y" } else { "N" }.to_owned()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod config {
|
pub fn temporary_password_length() -> usize {
|
||||||
use super::base64::decrypt as decrypt00;
|
let length = Config::get_option("temporary-password-length");
|
||||||
use super::base64::encrypt as encrypt00;
|
if length == "8" {
|
||||||
|
8
|
||||||
const VERSION_LEN: usize = 2;
|
} else if length == "10" {
|
||||||
|
10
|
||||||
pub fn encrypt_str_or_original(s: &str, version: &str) -> String {
|
} else {
|
||||||
if version.len() == VERSION_LEN {
|
6 // default
|
||||||
if version == "00" {
|
|
||||||
if let Ok(s) = encrypt00(s.as_bytes()) {
|
|
||||||
return version.to_owned() + &s;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
s.to_owned()
|
|
||||||
}
|
|
||||||
|
|
||||||
// bool: whether should store to re-encrypt when load
|
|
||||||
pub fn decrypt_str_or_original(s: &str, current_version: &str) -> (String, bool) {
|
|
||||||
if s.len() > VERSION_LEN {
|
|
||||||
let version = &s[..VERSION_LEN];
|
|
||||||
if version == "00" {
|
|
||||||
if let Ok(v) = decrypt00(&s[VERSION_LEN..].as_bytes()) {
|
|
||||||
return (
|
|
||||||
String::from_utf8_lossy(&v).to_string(),
|
|
||||||
version != current_version,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
(s.to_owned(), !s.is_empty())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn encrypt_vec_or_original(v: &[u8], version: &str) -> Vec<u8> {
|
|
||||||
if version.len() == VERSION_LEN {
|
|
||||||
if version == "00" {
|
|
||||||
if let Ok(s) = encrypt00(v) {
|
|
||||||
let mut version = version.to_owned().into_bytes();
|
|
||||||
version.append(&mut s.into_bytes());
|
|
||||||
return version;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
v.to_owned()
|
|
||||||
}
|
|
||||||
|
|
||||||
// bool: whether should store to re-encrypt when load
|
|
||||||
pub fn decrypt_vec_or_original(v: &[u8], current_version: &str) -> (Vec<u8>, bool) {
|
|
||||||
if v.len() > VERSION_LEN {
|
|
||||||
let version = String::from_utf8_lossy(&v[..VERSION_LEN]);
|
|
||||||
if version == "00" {
|
|
||||||
if let Ok(v) = decrypt00(&v[VERSION_LEN..]) {
|
|
||||||
return (v, version != current_version);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
(v.to_owned(), !v.is_empty())
|
|
||||||
}
|
|
||||||
|
|
||||||
mod test {
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test() {
|
|
||||||
use crate::password_security::config::*;
|
|
||||||
|
|
||||||
println!("test str");
|
|
||||||
let data = "Hello World";
|
|
||||||
let encrypted = encrypt_str_or_original(data, "00");
|
|
||||||
let (decrypted, store) = decrypt_str_or_original(&encrypted, "00");
|
|
||||||
println!("data: {}", data);
|
|
||||||
println!("encrypted: {}", encrypted);
|
|
||||||
println!("decrypted: {}", decrypted);
|
|
||||||
assert_eq!(data, decrypted);
|
|
||||||
assert_eq!("00", &encrypted[..2]);
|
|
||||||
assert_eq!(store, false);
|
|
||||||
let (_, store2) = decrypt_str_or_original(&encrypted, "01");
|
|
||||||
assert_eq!(store2, true);
|
|
||||||
|
|
||||||
println!("test vec");
|
|
||||||
let data: Vec<u8> = vec![1, 2, 3, 4];
|
|
||||||
let encrypted = encrypt_vec_or_original(&data, "00");
|
|
||||||
let (decrypted, store) = decrypt_vec_or_original(&encrypted, "00");
|
|
||||||
println!("data: {:?}", data);
|
|
||||||
println!("encrypted: {:?}", encrypted);
|
|
||||||
println!("decrypted: {:?}", decrypted);
|
|
||||||
assert_eq!(data, decrypted);
|
|
||||||
assert_eq!("00".as_bytes(), &encrypted[..2]);
|
|
||||||
assert_eq!(store, false);
|
|
||||||
let (_, store2) = decrypt_vec_or_original(&encrypted, "01");
|
|
||||||
assert_eq!(store2, true);
|
|
||||||
|
|
||||||
println!("test old");
|
|
||||||
let data = "00Hello World";
|
|
||||||
let (decrypted, store) = decrypt_str_or_original(&data, "00");
|
|
||||||
assert_eq!(data, decrypted);
|
|
||||||
assert_eq!(store, true);
|
|
||||||
let data: Vec<u8> = vec!['0' as u8, '0' as u8, 1, 2, 3, 4];
|
|
||||||
let (decrypted, store) = decrypt_vec_or_original(&data, "00");
|
|
||||||
assert_eq!(data, decrypted);
|
|
||||||
assert_eq!(store, true);
|
|
||||||
let (_, store) = decrypt_str_or_original("", "00");
|
|
||||||
assert_eq!(store, false);
|
|
||||||
let (_, store) = decrypt_vec_or_original(&vec![], "00");
|
|
||||||
assert_eq!(store, false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mod base64 {
|
pub fn temporary_enabled() -> bool {
|
||||||
use super::symmetric_crypt;
|
verification_method() != VerificationMethod::OnlyUsePermanentPassword
|
||||||
use sodiumoxide::base64;
|
}
|
||||||
|
|
||||||
pub fn encrypt(v: &[u8]) -> Result<String, ()> {
|
pub fn permanent_enabled() -> bool {
|
||||||
if v.len() > 0 {
|
verification_method() != VerificationMethod::OnlyUseTemporaryPassword
|
||||||
symmetric_crypt(v, true).map(|v| base64::encode(v, base64::Variant::Original))
|
}
|
||||||
} else {
|
|
||||||
Err(())
|
pub fn has_valid_password() -> bool {
|
||||||
|
temporary_enabled() && !temporary_password().is_empty()
|
||||||
|
|| permanent_enabled() && !Config::get_permanent_password().is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
const VERSION_LEN: usize = 2;
|
||||||
|
|
||||||
|
pub fn encrypt_str_or_original(s: &str, version: &str) -> String {
|
||||||
|
if decrypt_str_or_original(s, version).1 {
|
||||||
|
log::error!("Duplicate encryption!");
|
||||||
|
return s.to_owned();
|
||||||
|
}
|
||||||
|
if version == "00" {
|
||||||
|
if let Ok(s) = encrypt(s.as_bytes()) {
|
||||||
|
return version.to_owned() + &s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s.to_owned()
|
||||||
|
}
|
||||||
|
|
||||||
|
// String: password
|
||||||
|
// bool: whether decryption is successful
|
||||||
|
// bool: whether should store to re-encrypt when load
|
||||||
|
pub fn decrypt_str_or_original(s: &str, current_version: &str) -> (String, bool, bool) {
|
||||||
|
if s.len() > VERSION_LEN {
|
||||||
|
let version = &s[..VERSION_LEN];
|
||||||
|
if version == "00" {
|
||||||
|
if let Ok(v) = decrypt(&s[VERSION_LEN..].as_bytes()) {
|
||||||
|
return (
|
||||||
|
String::from_utf8_lossy(&v).to_string(),
|
||||||
|
true,
|
||||||
|
version != current_version,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn decrypt(v: &[u8]) -> Result<Vec<u8>, ()> {
|
(s.to_owned(), false, !s.is_empty())
|
||||||
if v.len() > 0 {
|
}
|
||||||
base64::decode(v, base64::Variant::Original).and_then(|v| symmetric_crypt(&v, false))
|
|
||||||
} else {
|
pub fn encrypt_vec_or_original(v: &[u8], version: &str) -> Vec<u8> {
|
||||||
Err(())
|
if decrypt_vec_or_original(v, version).1 {
|
||||||
|
log::error!("Duplicate encryption!");
|
||||||
|
return v.to_owned();
|
||||||
|
}
|
||||||
|
if version == "00" {
|
||||||
|
if let Ok(s) = encrypt(v) {
|
||||||
|
let mut version = version.to_owned().into_bytes();
|
||||||
|
version.append(&mut s.into_bytes());
|
||||||
|
return version;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
v.to_owned()
|
||||||
|
}
|
||||||
|
|
||||||
|
// String: password
|
||||||
|
// bool: whether decryption is successful
|
||||||
|
// bool: whether should store to re-encrypt when load
|
||||||
|
pub fn decrypt_vec_or_original(v: &[u8], current_version: &str) -> (Vec<u8>, bool, bool) {
|
||||||
|
if v.len() > VERSION_LEN {
|
||||||
|
let version = String::from_utf8_lossy(&v[..VERSION_LEN]);
|
||||||
|
if version == "00" {
|
||||||
|
if let Ok(v) = decrypt(&v[VERSION_LEN..]) {
|
||||||
|
return (v, true, version != current_version);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(v.to_owned(), false, !v.is_empty())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn encrypt(v: &[u8]) -> Result<String, ()> {
|
||||||
|
if v.len() > 0 {
|
||||||
|
symmetric_crypt(v, true).map(|v| base64::encode(v, base64::Variant::Original))
|
||||||
|
} else {
|
||||||
|
Err(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn decrypt(v: &[u8]) -> Result<Vec<u8>, ()> {
|
||||||
|
if v.len() > 0 {
|
||||||
|
base64::decode(v, base64::Variant::Original).and_then(|v| symmetric_crypt(&v, false))
|
||||||
|
} else {
|
||||||
|
Err(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn symmetric_crypt(data: &[u8], encrypt: bool) -> Result<Vec<u8>, ()> {
|
fn symmetric_crypt(data: &[u8], encrypt: bool) -> Result<Vec<u8>, ()> {
|
||||||
@ -328,3 +155,64 @@ fn symmetric_crypt(data: &[u8], encrypt: bool) -> Result<Vec<u8>, ()> {
|
|||||||
secretbox::open(data, &nonce, &key)
|
secretbox::open(data, &nonce, &key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mod test {
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test() {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
let version = "00";
|
||||||
|
|
||||||
|
println!("test str");
|
||||||
|
let data = "Hello World";
|
||||||
|
let encrypted = encrypt_str_or_original(data, version);
|
||||||
|
let (decrypted, succ, store) = decrypt_str_or_original(&encrypted, version);
|
||||||
|
println!("data: {}", data);
|
||||||
|
println!("encrypted: {}", encrypted);
|
||||||
|
println!("decrypted: {}", decrypted);
|
||||||
|
assert_eq!(data, decrypted);
|
||||||
|
assert_eq!(version, &encrypted[..2]);
|
||||||
|
assert_eq!(succ, true);
|
||||||
|
assert_eq!(store, false);
|
||||||
|
let (_, _, store) = decrypt_str_or_original(&encrypted, "99");
|
||||||
|
assert_eq!(store, true);
|
||||||
|
assert_eq!(decrypt_str_or_original(&decrypted, version).1, false);
|
||||||
|
assert_eq!(encrypt_str_or_original(&encrypted, version), encrypted);
|
||||||
|
|
||||||
|
println!("test vec");
|
||||||
|
let data: Vec<u8> = vec![1, 2, 3, 4, 5, 6];
|
||||||
|
let encrypted = encrypt_vec_or_original(&data, version);
|
||||||
|
let (decrypted, succ, store) = decrypt_vec_or_original(&encrypted, version);
|
||||||
|
println!("data: {:?}", data);
|
||||||
|
println!("encrypted: {:?}", encrypted);
|
||||||
|
println!("decrypted: {:?}", decrypted);
|
||||||
|
assert_eq!(data, decrypted);
|
||||||
|
assert_eq!(version.as_bytes(), &encrypted[..2]);
|
||||||
|
assert_eq!(store, false);
|
||||||
|
assert_eq!(succ, true);
|
||||||
|
let (_, _, store) = decrypt_vec_or_original(&encrypted, "99");
|
||||||
|
assert_eq!(store, true);
|
||||||
|
assert_eq!(decrypt_vec_or_original(&decrypted, version).1, false);
|
||||||
|
assert_eq!(encrypt_vec_or_original(&encrypted, version), encrypted);
|
||||||
|
|
||||||
|
println!("test original");
|
||||||
|
let data = version.to_string() + "Hello World";
|
||||||
|
let (decrypted, succ, store) = decrypt_str_or_original(&data, version);
|
||||||
|
assert_eq!(data, decrypted);
|
||||||
|
assert_eq!(store, true);
|
||||||
|
assert_eq!(succ, false);
|
||||||
|
let verbytes = version.as_bytes();
|
||||||
|
let data: Vec<u8> = vec![verbytes[0] as u8, verbytes[1] as u8, 1, 2, 3, 4, 5, 6];
|
||||||
|
let (decrypted, succ, store) = decrypt_vec_or_original(&data, version);
|
||||||
|
assert_eq!(data, decrypted);
|
||||||
|
assert_eq!(store, true);
|
||||||
|
assert_eq!(succ, false);
|
||||||
|
let (_, succ, store) = decrypt_str_or_original("", version);
|
||||||
|
assert_eq!(store, false);
|
||||||
|
assert_eq!(succ, false);
|
||||||
|
let (_, succ, store) = decrypt_vec_or_original(&vec![], version);
|
||||||
|
assert_eq!(store, false);
|
||||||
|
assert_eq!(succ, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -43,6 +43,10 @@ fn get_display_server_of_session(session: &str) -> String {
|
|||||||
display_server
|
display_server
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
// loginctl has not given the expected output. try something else.
|
||||||
|
if let Ok(sestype) = std::env::var("XDG_SESSION_TYPE") {
|
||||||
|
return sestype.to_owned();
|
||||||
|
}
|
||||||
// If the session is not a tty, then just return the type as usual
|
// If the session is not a tty, then just return the type as usual
|
||||||
display_server
|
display_server
|
||||||
}
|
}
|
||||||
@ -80,6 +84,11 @@ pub fn get_value_of_seat0(i: usize) -> String {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// loginctl has not given the expected output. try something else.
|
||||||
|
if let Ok(sid) = std::env::var("XDG_SESSION_ID") { // could also execute "cat /proc/self/sessionid"
|
||||||
|
return sid.to_owned();
|
||||||
|
}
|
||||||
|
|
||||||
return "".to_owned();
|
return "".to_owned();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,14 +54,9 @@ pub enum Display {
|
|||||||
WAYLAND(wayland::Display),
|
WAYLAND(wayland::Display),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn is_x11() -> bool {
|
|
||||||
"x11" == hbb_common::platform::linux::get_display_server()
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display {
|
impl Display {
|
||||||
pub fn primary() -> io::Result<Display> {
|
pub fn primary() -> io::Result<Display> {
|
||||||
Ok(if is_x11() {
|
Ok(if super::is_x11() {
|
||||||
Display::X11(x11::Display::primary()?)
|
Display::X11(x11::Display::primary()?)
|
||||||
} else {
|
} else {
|
||||||
Display::WAYLAND(wayland::Display::primary()?)
|
Display::WAYLAND(wayland::Display::primary()?)
|
||||||
@ -69,7 +64,7 @@ impl Display {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn all() -> io::Result<Vec<Display>> {
|
pub fn all() -> io::Result<Vec<Display>> {
|
||||||
Ok(if is_x11() {
|
Ok(if super::is_x11() {
|
||||||
x11::Display::all()?
|
x11::Display::all()?
|
||||||
.drain(..)
|
.drain(..)
|
||||||
.map(|x| Display::X11(x))
|
.map(|x| Display::X11(x))
|
||||||
|
@ -59,3 +59,9 @@ pub trait TraitCapturer {
|
|||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
fn set_gdi(&mut self) -> bool;
|
fn set_gdi(&mut self) -> bool;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(x11)]
|
||||||
|
#[inline]
|
||||||
|
pub fn is_x11() -> bool {
|
||||||
|
"x11" == hbb_common::platform::linux::get_display_server()
|
||||||
|
}
|
||||||
|
@ -371,7 +371,7 @@ impl Client {
|
|||||||
conn: &mut Stream,
|
conn: &mut Stream,
|
||||||
) -> ResultType<()> {
|
) -> ResultType<()> {
|
||||||
let rs_pk = get_rs_pk(if key.is_empty() {
|
let rs_pk = get_rs_pk(if key.is_empty() {
|
||||||
"OeVuKk5nlHiXp+APNn0Y3pC1Iwpwn44JGqrQCsWqmBw="
|
hbb_common::config::RS_PUB_KEY
|
||||||
} else {
|
} else {
|
||||||
key
|
key
|
||||||
});
|
});
|
||||||
@ -785,6 +785,7 @@ pub struct LoginConfigHandler {
|
|||||||
features: Option<Features>,
|
features: Option<Features>,
|
||||||
session_id: u64,
|
session_id: u64,
|
||||||
pub supported_encoding: Option<(bool, bool)>,
|
pub supported_encoding: Option<(bool, bool)>,
|
||||||
|
pub restarting_remote_device: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Deref for LoginConfigHandler {
|
impl Deref for LoginConfigHandler {
|
||||||
@ -810,6 +811,7 @@ impl LoginConfigHandler {
|
|||||||
self.config = config;
|
self.config = config;
|
||||||
self.session_id = rand::random();
|
self.session_id = rand::random();
|
||||||
self.supported_encoding = None;
|
self.supported_encoding = None;
|
||||||
|
self.restarting_remote_device = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn should_auto_login(&self) -> String {
|
pub fn should_auto_login(&self) -> String {
|
||||||
@ -944,10 +946,6 @@ impl LoginConfigHandler {
|
|||||||
msg.lock_after_session_end = BoolOption::Yes.into();
|
msg.lock_after_session_end = BoolOption::Yes.into();
|
||||||
n += 1;
|
n += 1;
|
||||||
}
|
}
|
||||||
if self.get_toggle_option("privacy-mode") {
|
|
||||||
msg.privacy_mode = BoolOption::Yes.into();
|
|
||||||
n += 1;
|
|
||||||
}
|
|
||||||
if self.get_toggle_option("disable-audio") {
|
if self.get_toggle_option("disable-audio") {
|
||||||
msg.disable_audio = BoolOption::Yes.into();
|
msg.disable_audio = BoolOption::Yes.into();
|
||||||
n += 1;
|
n += 1;
|
||||||
@ -971,6 +969,23 @@ impl LoginConfigHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_option_message_after_login(&self) -> Option<OptionMessage> {
|
||||||
|
if self.is_port_forward || self.is_file_transfer {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let mut n = 0;
|
||||||
|
let mut msg = OptionMessage::new();
|
||||||
|
if self.get_toggle_option("privacy-mode") {
|
||||||
|
msg.privacy_mode = BoolOption::Yes.into();
|
||||||
|
n += 1;
|
||||||
|
}
|
||||||
|
if n > 0 {
|
||||||
|
Some(msg)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn get_image_quality_enum(&self, q: &str, ignore_default: bool) -> Option<ImageQuality> {
|
fn get_image_quality_enum(&self, q: &str, ignore_default: bool) -> Option<ImageQuality> {
|
||||||
if q == "low" {
|
if q == "low" {
|
||||||
Some(ImageQuality::Low)
|
Some(ImageQuality::Low)
|
||||||
@ -1144,11 +1159,12 @@ impl LoginConfigHandler {
|
|||||||
let my_id = Config::get_id();
|
let my_id = Config::get_id();
|
||||||
let mut lr = LoginRequest {
|
let mut lr = LoginRequest {
|
||||||
username: self.id.clone(),
|
username: self.id.clone(),
|
||||||
password:password.into(),
|
password: password.into(),
|
||||||
my_id,
|
my_id,
|
||||||
my_name: crate::username(),
|
my_name: crate::username(),
|
||||||
option: self.get_option_message(true).into(),
|
option: self.get_option_message(true).into(),
|
||||||
session_id: self.session_id,
|
session_id: self.session_id,
|
||||||
|
version: crate::VERSION.to_string(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
if self.is_file_transfer {
|
if self.is_file_transfer {
|
||||||
@ -1180,6 +1196,14 @@ impl LoginConfigHandler {
|
|||||||
msg_out.set_misc(misc);
|
msg_out.set_misc(misc);
|
||||||
msg_out
|
msg_out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn restart_remote_device(&self) -> Message {
|
||||||
|
let mut misc = Misc::new();
|
||||||
|
misc.set_restart_remote_device(true);
|
||||||
|
let mut msg_out = Message::new();
|
||||||
|
msg_out.set_misc(misc);
|
||||||
|
msg_out
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum MediaData {
|
pub enum MediaData {
|
||||||
@ -1334,11 +1358,21 @@ fn _input_os_password(p: String, activate: bool, interface: impl Interface) {
|
|||||||
|
|
||||||
pub async fn handle_hash(
|
pub async fn handle_hash(
|
||||||
lc: Arc<RwLock<LoginConfigHandler>>,
|
lc: Arc<RwLock<LoginConfigHandler>>,
|
||||||
|
password_preset: &str,
|
||||||
hash: Hash,
|
hash: Hash,
|
||||||
interface: &impl Interface,
|
interface: &impl Interface,
|
||||||
peer: &mut Stream,
|
peer: &mut Stream,
|
||||||
) {
|
) {
|
||||||
let mut password = lc.read().unwrap().password.clone();
|
let mut password = lc.read().unwrap().password.clone();
|
||||||
|
if password.is_empty() {
|
||||||
|
if !password_preset.is_empty() {
|
||||||
|
let mut hasher = Sha256::new();
|
||||||
|
hasher.update(password_preset);
|
||||||
|
hasher.update(&hash.salt);
|
||||||
|
let res = hasher.finalize();
|
||||||
|
password = res[..].into();
|
||||||
|
}
|
||||||
|
}
|
||||||
if password.is_empty() {
|
if password.is_empty() {
|
||||||
password = lc.read().unwrap().config.password.clone();
|
password = lc.read().unwrap().config.password.clone();
|
||||||
}
|
}
|
||||||
@ -1384,7 +1418,7 @@ pub trait Interface: Send + Clone + 'static + Sized {
|
|||||||
fn msgbox(&self, msgtype: &str, title: &str, text: &str);
|
fn msgbox(&self, msgtype: &str, title: &str, text: &str);
|
||||||
fn handle_login_error(&mut self, err: &str) -> bool;
|
fn handle_login_error(&mut self, err: &str) -> bool;
|
||||||
fn handle_peer_info(&mut self, pi: PeerInfo);
|
fn handle_peer_info(&mut self, pi: PeerInfo);
|
||||||
async fn handle_hash(&mut self, hash: Hash, peer: &mut Stream);
|
async fn handle_hash(&mut self, pass: &str, hash: Hash, peer: &mut Stream);
|
||||||
async fn handle_login_from_ui(&mut self, password: String, remember: bool, peer: &mut Stream);
|
async fn handle_login_from_ui(&mut self, password: String, remember: bool, peer: &mut Stream);
|
||||||
async fn handle_test_delay(&mut self, t: TestDelay, peer: &mut Stream);
|
async fn handle_test_delay(&mut self, t: TestDelay, peer: &mut Stream);
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,10 @@ use std::{
|
|||||||
time::Instant,
|
time::Instant,
|
||||||
};
|
};
|
||||||
|
|
||||||
use hbb_common::{log, message_proto::{VideoFrame, video_frame}};
|
use hbb_common::{
|
||||||
|
log,
|
||||||
|
message_proto::{video_frame, VideoFrame},
|
||||||
|
};
|
||||||
|
|
||||||
const MAX_LATENCY: i64 = 500;
|
const MAX_LATENCY: i64 = 500;
|
||||||
const MIN_LATENCY: i64 = 100;
|
const MIN_LATENCY: i64 = 100;
|
||||||
@ -87,3 +90,12 @@ impl ToString for CodecFormat {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct QualityStatus {
|
||||||
|
pub speed: Option<String>,
|
||||||
|
pub fps: Option<i32>,
|
||||||
|
pub delay: Option<i32>,
|
||||||
|
pub target_bitrate: Option<i32>,
|
||||||
|
pub codec_format: Option<CodecFormat>,
|
||||||
|
}
|
||||||
|
212
src/ipc.rs
212
src/ipc.rs
@ -1,4 +1,5 @@
|
|||||||
use crate::rendezvous_mediator::RendezvousMediator;
|
use crate::rendezvous_mediator::RendezvousMediator;
|
||||||
|
use bytes::Bytes;
|
||||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||||
pub use clipboard::ClipbaordFile;
|
pub use clipboard::ClipbaordFile;
|
||||||
use hbb_common::{
|
use hbb_common::{
|
||||||
@ -7,9 +8,7 @@ use hbb_common::{
|
|||||||
config::{self, Config, Config2},
|
config::{self, Config, Config2},
|
||||||
futures::StreamExt as _,
|
futures::StreamExt as _,
|
||||||
futures_util::sink::SinkExt,
|
futures_util::sink::SinkExt,
|
||||||
log,
|
log, password_security as password, timeout, tokio,
|
||||||
password_security::password,
|
|
||||||
timeout, tokio,
|
|
||||||
tokio::io::{AsyncRead, AsyncWrite},
|
tokio::io::{AsyncRead, AsyncWrite},
|
||||||
tokio_util::codec::Framed,
|
tokio_util::codec::Framed,
|
||||||
ResultType,
|
ResultType,
|
||||||
@ -19,20 +18,9 @@ use parity_tokio_ipc::{
|
|||||||
};
|
};
|
||||||
use serde_derive::{Deserialize, Serialize};
|
use serde_derive::{Deserialize, Serialize};
|
||||||
use std::{collections::HashMap, sync::atomic::Ordering};
|
use std::{collections::HashMap, sync::atomic::Ordering};
|
||||||
use bytes::Bytes;
|
|
||||||
#[cfg(not(windows))]
|
#[cfg(not(windows))]
|
||||||
use std::{fs::File, io::prelude::*};
|
use std::{fs::File, io::prelude::*};
|
||||||
|
|
||||||
const STR_RANDOM_PASSWORD: &'static str = "random-password";
|
|
||||||
const STR_SECURITY_PASSWORD: &'static str = "security-password";
|
|
||||||
const STR_RANDOM_PASSWORD_UPDATE_METHOD: &'static str = "random-password-update-method";
|
|
||||||
const STR_RANDOM_PASSWORD_ENABLED: &'static str = "random-password-enabled";
|
|
||||||
const STR_SECURITY_PASSWORD_ENABLED: &'static str = "security-password-enabled";
|
|
||||||
const STR_ONETIME_PASSWORD_ENABLED: &'static str = "onetime-password-enabled";
|
|
||||||
const STR_ONETIME_PASSWORD_ACTIVATED: &'static str = "onetime-password-activated";
|
|
||||||
const STR_RANDOM_PASSWORD_VALID: &'static str = "random-password-valid";
|
|
||||||
pub const STR_PASSWORD_DESCRIPTION: &'static str = "password-description";
|
|
||||||
|
|
||||||
// State with timestamp, because std::time::Instant cannot be serialized
|
// State with timestamp, because std::time::Instant cannot be serialized
|
||||||
#[derive(Debug, Serialize, Deserialize, Copy, Clone)]
|
#[derive(Debug, Serialize, Deserialize, Copy, Clone)]
|
||||||
#[serde(tag = "t", content = "c")]
|
#[serde(tag = "t", content = "c")]
|
||||||
@ -69,6 +57,7 @@ pub enum FS {
|
|||||||
id: i32,
|
id: i32,
|
||||||
file_num: i32,
|
file_num: i32,
|
||||||
files: Vec<(String, u64)>,
|
files: Vec<(String, u64)>,
|
||||||
|
overwrite_detection: bool,
|
||||||
},
|
},
|
||||||
CancelWrite {
|
CancelWrite {
|
||||||
id: i32,
|
id: i32,
|
||||||
@ -97,6 +86,7 @@ pub enum FS {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
#[serde(tag = "t", content = "c")]
|
#[serde(tag = "t", content = "c")]
|
||||||
pub enum DataKeyboard {
|
pub enum DataKeyboard {
|
||||||
@ -113,6 +103,7 @@ pub enum DataKeyboardResponse {
|
|||||||
GetKeyState(bool),
|
GetKeyState(bool),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(any(target_os = "android", target_os = "ios", feature = "cli")))]
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
#[serde(tag = "t", content = "c")]
|
#[serde(tag = "t", content = "c")]
|
||||||
pub enum DataMouse {
|
pub enum DataMouse {
|
||||||
@ -151,6 +142,7 @@ pub enum Data {
|
|||||||
audio: bool,
|
audio: bool,
|
||||||
file: bool,
|
file: bool,
|
||||||
file_transfer_enabled: bool,
|
file_transfer_enabled: bool,
|
||||||
|
restart: bool,
|
||||||
},
|
},
|
||||||
ChatMessage {
|
ChatMessage {
|
||||||
text: String,
|
text: String,
|
||||||
@ -180,9 +172,11 @@ pub enum Data {
|
|||||||
ClipboardFileEnabled(bool),
|
ClipboardFileEnabled(bool),
|
||||||
PrivacyModeState((i32, PrivacyModeState)),
|
PrivacyModeState((i32, PrivacyModeState)),
|
||||||
TestRendezvousServer,
|
TestRendezvousServer,
|
||||||
Bool((String, Option<bool>)),
|
#[cfg(not(any(target_os = "android", target_os = "ios", feature = "cli")))]
|
||||||
Keyboard(DataKeyboard),
|
Keyboard(DataKeyboard),
|
||||||
|
#[cfg(not(any(target_os = "android", target_os = "ios", feature = "cli")))]
|
||||||
KeyboardResponse(DataKeyboardResponse),
|
KeyboardResponse(DataKeyboardResponse),
|
||||||
|
#[cfg(not(any(target_os = "android", target_os = "ios", feature = "cli")))]
|
||||||
Mouse(DataMouse),
|
Mouse(DataMouse),
|
||||||
Control(DataControl),
|
Control(DataControl),
|
||||||
Empty,
|
Empty,
|
||||||
@ -340,20 +334,10 @@ async fn handle(data: Data, stream: &mut Connection) {
|
|||||||
let value;
|
let value;
|
||||||
if name == "id" {
|
if name == "id" {
|
||||||
value = Some(Config::get_id());
|
value = Some(Config::get_id());
|
||||||
} else if name == STR_RANDOM_PASSWORD {
|
} else if name == "temporary-password" {
|
||||||
value = Some(password::random_password());
|
value = Some(password::temporary_password());
|
||||||
} else if name == STR_SECURITY_PASSWORD {
|
} else if name == "permanent-password" {
|
||||||
value = Some(Config::get_security_password());
|
value = Some(Config::get_permanent_password());
|
||||||
} else if name == STR_RANDOM_PASSWORD_UPDATE_METHOD {
|
|
||||||
value = Some(password::update_method().to_string());
|
|
||||||
} else if name == STR_PASSWORD_DESCRIPTION {
|
|
||||||
value = Some(
|
|
||||||
password::random_password()
|
|
||||||
+ &password::security_enabled().to_string()
|
|
||||||
+ &password::random_enabled().to_string()
|
|
||||||
+ &password::onetime_password_enabled().to_string()
|
|
||||||
+ &password::onetime_password_activated().to_string(),
|
|
||||||
);
|
|
||||||
} else if name == "salt" {
|
} else if name == "salt" {
|
||||||
value = Some(Config::get_salt());
|
value = Some(Config::get_salt());
|
||||||
} else if name == "rendezvous_server" {
|
} else if name == "rendezvous_server" {
|
||||||
@ -373,12 +357,10 @@ async fn handle(data: Data, stream: &mut Connection) {
|
|||||||
if name == "id" {
|
if name == "id" {
|
||||||
Config::set_key_confirmed(false);
|
Config::set_key_confirmed(false);
|
||||||
Config::set_id(&value);
|
Config::set_id(&value);
|
||||||
} else if name == STR_RANDOM_PASSWORD {
|
} else if name == "temporary-password" {
|
||||||
password::set_random_password(&value);
|
password::update_temporary_password();
|
||||||
} else if name == STR_SECURITY_PASSWORD {
|
} else if name == "permanent-password" {
|
||||||
Config::set_security_password(&value);
|
Config::set_permanent_password(&value);
|
||||||
} else if name == STR_RANDOM_PASSWORD_UPDATE_METHOD {
|
|
||||||
password::set_update_method(&value);
|
|
||||||
} else if name == "salt" {
|
} else if name == "salt" {
|
||||||
Config::set_salt(&value);
|
Config::set_salt(&value);
|
||||||
} else {
|
} else {
|
||||||
@ -418,36 +400,6 @@ async fn handle(data: Data, stream: &mut Connection) {
|
|||||||
Data::TestRendezvousServer => {
|
Data::TestRendezvousServer => {
|
||||||
crate::test_rendezvous_server();
|
crate::test_rendezvous_server();
|
||||||
}
|
}
|
||||||
Data::Bool((name, value)) => match value {
|
|
||||||
None => {
|
|
||||||
let value;
|
|
||||||
if name == STR_SECURITY_PASSWORD_ENABLED {
|
|
||||||
value = Some(password::security_enabled());
|
|
||||||
} else if name == STR_RANDOM_PASSWORD_ENABLED {
|
|
||||||
value = Some(password::random_enabled());
|
|
||||||
} else if name == STR_ONETIME_PASSWORD_ENABLED {
|
|
||||||
value = Some(password::onetime_password_enabled());
|
|
||||||
} else if name == STR_ONETIME_PASSWORD_ACTIVATED {
|
|
||||||
value = Some(password::onetime_password_activated());
|
|
||||||
} else if name == STR_RANDOM_PASSWORD_VALID {
|
|
||||||
value = Some(password::random_password_valid());
|
|
||||||
} else {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
allow_err!(stream.send(&Data::Bool((name, value))).await);
|
|
||||||
}
|
|
||||||
Some(value) => {
|
|
||||||
if name == STR_SECURITY_PASSWORD_ENABLED {
|
|
||||||
password::set_security_enabled(value);
|
|
||||||
} else if name == STR_RANDOM_PASSWORD_ENABLED {
|
|
||||||
password::set_random_enabled(value);
|
|
||||||
} else if name == STR_ONETIME_PASSWORD_ENABLED {
|
|
||||||
password::set_onetime_password_enabled(value);
|
|
||||||
} else if name == STR_ONETIME_PASSWORD_ACTIVATED {
|
|
||||||
password::set_onetime_password_activated(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -530,10 +482,6 @@ where
|
|||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn send_bool(&mut self, name: &str, value: bool) -> ResultType<()> {
|
|
||||||
self.send(&Data::Bool((name.to_owned(), Some(value)))).await
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn next_timeout(&mut self, ms_timeout: u64) -> ResultType<Option<Data>> {
|
pub async fn next_timeout(&mut self, ms_timeout: u64) -> ResultType<Option<Data>> {
|
||||||
Ok(timeout(ms_timeout, self.next()).await??)
|
Ok(timeout(ms_timeout, self.next()).await??)
|
||||||
}
|
}
|
||||||
@ -605,128 +553,22 @@ pub async fn set_config(name: &str, value: String) -> ResultType<()> {
|
|||||||
set_config_async(name, value).await
|
set_config_async(name, value).await
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::main(flavor = "current_thread")]
|
pub fn update_temporary_password() -> ResultType<()> {
|
||||||
async fn get_bool(name: &str) -> ResultType<Option<bool>> {
|
set_config("temporary-password", "".to_owned())
|
||||||
get_bool_async(name, 1_000).await
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_bool_async(name: &str, ms_timeout: u64) -> ResultType<Option<bool>> {
|
pub fn get_permanent_password() -> String {
|
||||||
let mut c = connect(ms_timeout, "").await?;
|
if let Ok(Some(v)) = get_config("permanent-password") {
|
||||||
c.send(&Data::Bool((name.to_owned(), None))).await?;
|
Config::set_permanent_password(&v);
|
||||||
if let Some(Data::Bool((name2, value))) = c.next_timeout(ms_timeout).await? {
|
v
|
||||||
if name == name2 {
|
|
||||||
return Ok(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Ok(None);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn set_bool_async(name: &str, value: bool) -> ResultType<()> {
|
|
||||||
let mut c = connect(1000, "").await?;
|
|
||||||
c.send_bool(name, value).await?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::main(flavor = "current_thread")]
|
|
||||||
pub async fn set_bool(name: &str, value: bool) -> ResultType<()> {
|
|
||||||
set_bool_async(name, value).await
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_random_password() -> String {
|
|
||||||
if let Ok(Some(password)) = get_config(STR_RANDOM_PASSWORD) {
|
|
||||||
password::set_random_password(&password);
|
|
||||||
password
|
|
||||||
} else {
|
} else {
|
||||||
password::random_password()
|
Config::get_permanent_password()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_random_password(v: String) -> ResultType<()> {
|
pub fn set_permanent_password(v: String) -> ResultType<()> {
|
||||||
password::set_random_password(&v);
|
Config::set_permanent_password(&v);
|
||||||
set_config(STR_RANDOM_PASSWORD, v)
|
set_config("permanent-password", v)
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_security_password(v: String) -> ResultType<()> {
|
|
||||||
Config::set_security_password(&v);
|
|
||||||
set_config(STR_SECURITY_PASSWORD, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn random_password_update_method() -> String {
|
|
||||||
if let Ok(Some(method)) = get_config(STR_RANDOM_PASSWORD_UPDATE_METHOD) {
|
|
||||||
password::set_update_method(&method);
|
|
||||||
method
|
|
||||||
} else {
|
|
||||||
password::update_method()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_random_password_update_method(method: String) -> ResultType<()> {
|
|
||||||
password::set_update_method(&method);
|
|
||||||
set_config(STR_RANDOM_PASSWORD_UPDATE_METHOD, method)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_random_password_enabled() -> bool {
|
|
||||||
if let Ok(Some(enabled)) = get_bool(STR_RANDOM_PASSWORD_ENABLED) {
|
|
||||||
password::set_random_enabled(enabled);
|
|
||||||
enabled
|
|
||||||
} else {
|
|
||||||
password::random_enabled()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_random_password_enabled(enabled: bool) -> ResultType<()> {
|
|
||||||
password::set_random_enabled(enabled);
|
|
||||||
set_bool(STR_RANDOM_PASSWORD_ENABLED, enabled)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_security_password_enabled() -> bool {
|
|
||||||
if let Ok(Some(enabled)) = get_bool(STR_SECURITY_PASSWORD_ENABLED) {
|
|
||||||
password::set_security_enabled(enabled);
|
|
||||||
enabled
|
|
||||||
} else {
|
|
||||||
password::security_enabled()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_security_password_enabled(enabled: bool) -> ResultType<()> {
|
|
||||||
password::set_security_enabled(enabled);
|
|
||||||
set_bool(STR_SECURITY_PASSWORD_ENABLED, enabled)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_onetime_password_enabled() -> bool {
|
|
||||||
if let Ok(Some(enabled)) = get_bool(STR_ONETIME_PASSWORD_ENABLED) {
|
|
||||||
password::set_onetime_password_enabled(enabled);
|
|
||||||
enabled
|
|
||||||
} else {
|
|
||||||
password::onetime_password_enabled()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_onetime_password_enabled(enabled: bool) -> ResultType<()> {
|
|
||||||
password::set_onetime_password_enabled(enabled);
|
|
||||||
set_bool(STR_ONETIME_PASSWORD_ENABLED, enabled)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_onetime_password_activated() -> bool {
|
|
||||||
if let Ok(Some(activated)) = get_bool(STR_ONETIME_PASSWORD_ACTIVATED) {
|
|
||||||
password::set_onetime_password_activated(activated);
|
|
||||||
activated
|
|
||||||
} else {
|
|
||||||
password::onetime_password_activated()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_onetime_password_activated(activated: bool) -> ResultType<()> {
|
|
||||||
password::set_onetime_password_activated(activated);
|
|
||||||
set_bool(STR_ONETIME_PASSWORD_ACTIVATED, activated)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_random_password_valid() -> bool {
|
|
||||||
if let Ok(Some(valid)) = get_bool(STR_RANDOM_PASSWORD_VALID) {
|
|
||||||
valid
|
|
||||||
} else {
|
|
||||||
password::random_password_valid()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_id() -> String {
|
pub fn get_id() -> String {
|
||||||
|
25
src/lang.rs
25
src/lang.rs
@ -8,15 +8,18 @@ mod de;
|
|||||||
mod en;
|
mod en;
|
||||||
mod eo;
|
mod eo;
|
||||||
mod es;
|
mod es;
|
||||||
mod hu;
|
|
||||||
mod fr;
|
mod fr;
|
||||||
|
mod hu;
|
||||||
mod id;
|
mod id;
|
||||||
mod it;
|
mod it;
|
||||||
|
mod ja;
|
||||||
|
mod pl;
|
||||||
mod ptbr;
|
mod ptbr;
|
||||||
mod ru;
|
mod ru;
|
||||||
mod sk;
|
mod sk;
|
||||||
mod tr;
|
mod tr;
|
||||||
mod tw;
|
mod tw;
|
||||||
|
mod vn;
|
||||||
|
|
||||||
lazy_static::lazy_static! {
|
lazy_static::lazy_static! {
|
||||||
pub static ref LANGS: Value =
|
pub static ref LANGS: Value =
|
||||||
@ -37,6 +40,9 @@ lazy_static::lazy_static! {
|
|||||||
("da", "Dansk"),
|
("da", "Dansk"),
|
||||||
("eo", "Esperanto"),
|
("eo", "Esperanto"),
|
||||||
("tr", "Türkçe"),
|
("tr", "Türkçe"),
|
||||||
|
("vn", "Tiếng Việt"),
|
||||||
|
("pl", "Polski"),
|
||||||
|
("ja", "日本語"),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -81,16 +87,21 @@ pub fn translate_locale(name: String, locale: &str) -> String {
|
|||||||
"cs" => cs::T.deref(),
|
"cs" => cs::T.deref(),
|
||||||
"da" => da::T.deref(),
|
"da" => da::T.deref(),
|
||||||
"sk" => sk::T.deref(),
|
"sk" => sk::T.deref(),
|
||||||
|
"vn" => vn::T.deref(),
|
||||||
|
"pl" => pl::T.deref(),
|
||||||
|
"ja" => ja::T.deref(),
|
||||||
_ => en::T.deref(),
|
_ => en::T.deref(),
|
||||||
};
|
};
|
||||||
if let Some(v) = m.get(&name as &str) {
|
if let Some(v) = m.get(&name as &str) {
|
||||||
v.to_string()
|
if v.is_empty() {
|
||||||
} else {
|
if lang != "en" {
|
||||||
if lang != "en" {
|
if let Some(v) = en::T.get(&name as &str) {
|
||||||
if let Some(v) = en::T.get(&name as &str) {
|
return v.to_string();
|
||||||
return v.to_string();
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
return v.to_string();
|
||||||
}
|
}
|
||||||
name
|
|
||||||
}
|
}
|
||||||
|
name
|
||||||
}
|
}
|
||||||
|
@ -287,20 +287,19 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Keep RustDesk background service", "保持RustDesk后台服务"),
|
("Keep RustDesk background service", "保持RustDesk后台服务"),
|
||||||
("Ignore Battery Optimizations", "忽略电池优化"),
|
("Ignore Battery Optimizations", "忽略电池优化"),
|
||||||
("android_open_battery_optimizations_tip", "如需关闭此功能,请在接下来的RustDesk应用设置页面中,找到并进入 [电源] 页面,取消勾选 [不受限制]"),
|
("android_open_battery_optimizations_tip", "如需关闭此功能,请在接下来的RustDesk应用设置页面中,找到并进入 [电源] 页面,取消勾选 [不受限制]"),
|
||||||
("Random Password After Session", "会话结束更新随机密码"),
|
|
||||||
("Keep", "保持"),
|
|
||||||
("Update", "更新"),
|
|
||||||
("Disable", "禁用"),
|
|
||||||
("Onetime Password", "一次性口令"),
|
|
||||||
("Verification Method", "密码验证方式"),
|
|
||||||
("Enable security password", "启用安全密码"),
|
|
||||||
("Enable random password", "启用随机密码"),
|
|
||||||
("Enable onetime password", "启用一次性访问功能"),
|
|
||||||
("Disable onetime password", "禁用一次性访问功能"),
|
|
||||||
("Activate onetime password", "激活一次性访问功能"),
|
|
||||||
("Set security password", "设置安全密码"),
|
|
||||||
("Connection not allowed", "对方不允许连接"),
|
("Connection not allowed", "对方不允许连接"),
|
||||||
("Legacy mode", "传统模式"),
|
("Legacy mode", "传统模式"),
|
||||||
("Map mode", "1:1传输"),
|
("Map mode", "1:1传输"),
|
||||||
|
("Use temporary password", "使用临时密码"),
|
||||||
|
("Use permanent password", "使用固定密码"),
|
||||||
|
("Use both passwords", "同时使用两种密码"),
|
||||||
|
("Set permanent password", "设置固定密码"),
|
||||||
|
("Set temporary password length", "设置临时密码长度"),
|
||||||
|
("Enable Remote Restart", "允许远程重启"),
|
||||||
|
("Allow remote restart", "允许远程重启"),
|
||||||
|
("Restart Remote Device", "重启远程电脑"),
|
||||||
|
("Are you sure you want to restart", "确定要重启"),
|
||||||
|
("Restarting Remote Device", "正在重启远程设备"),
|
||||||
|
("remote_restarting_tip", "远程设备正在重启, 请关闭当前提示框, 并在一段时间后使用永久密码重新连接"),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -287,20 +287,19 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Keep RustDesk background service", ""),
|
("Keep RustDesk background service", ""),
|
||||||
("Ignore Battery Optimizations", ""),
|
("Ignore Battery Optimizations", ""),
|
||||||
("android_open_battery_optimizations_tip", ""),
|
("android_open_battery_optimizations_tip", ""),
|
||||||
("Random Password After Session", ""),
|
|
||||||
("Keep", ""),
|
|
||||||
("Update", ""),
|
|
||||||
("Disable", ""),
|
|
||||||
("Onetime Password", ""),
|
|
||||||
("Verification Method", ""),
|
|
||||||
("Enable security password", ""),
|
|
||||||
("Enable random password", ""),
|
|
||||||
("Enable onetime password", ""),
|
|
||||||
("Disable onetime password", ""),
|
|
||||||
("Activate onetime password", ""),
|
|
||||||
("Set security password", ""),
|
|
||||||
("Connection not allowed", ""),
|
("Connection not allowed", ""),
|
||||||
("Legacy mode", ""),
|
("Legacy mode", ""),
|
||||||
("Map mode", ""),
|
("Map mode", ""),
|
||||||
|
("Use temporary password", ""),
|
||||||
|
("Use permanent password", ""),
|
||||||
|
("Use both passwords", ""),
|
||||||
|
("Set permanent password", ""),
|
||||||
|
("Set temporary password length", ""),
|
||||||
|
("Enable Remote Restart", ""),
|
||||||
|
("Allow remote restart", ""),
|
||||||
|
("Restart Remote Device", ""),
|
||||||
|
("Are you sure you want to restart", ""),
|
||||||
|
("Restarting Remote Device", ""),
|
||||||
|
("remote_restarting_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -287,20 +287,19 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Keep RustDesk background service", ""),
|
("Keep RustDesk background service", ""),
|
||||||
("Ignore Battery Optimizations", ""),
|
("Ignore Battery Optimizations", ""),
|
||||||
("android_open_battery_optimizations_tip", ""),
|
("android_open_battery_optimizations_tip", ""),
|
||||||
("Random Password After Session", ""),
|
|
||||||
("Keep", ""),
|
|
||||||
("Update", ""),
|
|
||||||
("Disable", ""),
|
|
||||||
("Onetime Password", ""),
|
|
||||||
("Verification Method", ""),
|
|
||||||
("Enable security password", ""),
|
|
||||||
("Enable random password", ""),
|
|
||||||
("Enable onetime password", ""),
|
|
||||||
("Disable onetime password", ""),
|
|
||||||
("Activate onetime password", ""),
|
|
||||||
("Set security password", ""),
|
|
||||||
("Connection not allowed", ""),
|
("Connection not allowed", ""),
|
||||||
("Legacy mode", ""),
|
("Legacy mode", ""),
|
||||||
("Map mode", ""),
|
("Map mode", ""),
|
||||||
|
("Use temporary password", ""),
|
||||||
|
("Use permanent password", ""),
|
||||||
|
("Use both passwords", ""),
|
||||||
|
("Set permanent password", ""),
|
||||||
|
("Set temporary password length", ""),
|
||||||
|
("Enable Remote Restart", ""),
|
||||||
|
("Allow remote restart", ""),
|
||||||
|
("Restart Remote Device", ""),
|
||||||
|
("Are you sure you want to restart", ""),
|
||||||
|
("Restarting Remote Device", ""),
|
||||||
|
("remote_restarting_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -287,20 +287,19 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Keep RustDesk background service", "RustDesk im Hintergrund ausführen"),
|
("Keep RustDesk background service", "RustDesk im Hintergrund ausführen"),
|
||||||
("Ignore Battery Optimizations", "Batterieoptimierung ignorieren"),
|
("Ignore Battery Optimizations", "Batterieoptimierung ignorieren"),
|
||||||
("android_open_battery_optimizations_tip", "Möchten Sie die Batterieopimierungs-Einstellungen öffnen?"),
|
("android_open_battery_optimizations_tip", "Möchten Sie die Batterieopimierungs-Einstellungen öffnen?"),
|
||||||
("Random Password After Session", "Neues zufälliges Passwort nach jeder Sitzung"),
|
|
||||||
("Keep", "Behalten"),
|
|
||||||
("Update", "Aktualisieren"),
|
|
||||||
("Disable", "Deaktivieren"),
|
|
||||||
("Onetime Password", "Einmal-Passwort"),
|
|
||||||
("Verification Method", "Überprüfungsmethode"),
|
|
||||||
("Enable security password", "Sicheres Passwort aktivieren"),
|
|
||||||
("Enable random password", "Zufälliges Passwort aktivieren"),
|
|
||||||
("Enable onetime password", "Einmal-Passwort aktivieren"),
|
|
||||||
("Disable onetime password", "Einmal-Passwort deaktivieren"),
|
|
||||||
("Activate onetime password", "Einmal-Passwort aktivieren"),
|
|
||||||
("Set security password", "Sicheres Passwort setzen"),
|
|
||||||
("Connection not allowed", "Verbindung abgelehnt"),
|
("Connection not allowed", "Verbindung abgelehnt"),
|
||||||
("Legacy mode", ""),
|
("Legacy mode", ""),
|
||||||
("Map mode", ""),
|
("Map mode", ""),
|
||||||
|
("Use temporary password", "Temporäres Passwort verwenden"),
|
||||||
|
("Use permanent password", "Dauerhaftes Passwort verwenden"),
|
||||||
|
("Use both passwords", "Beide Passwörter verwenden"),
|
||||||
|
("Set permanent password", "Dauerhaftes Passwort setzen"),
|
||||||
|
("Set temporary password length", "Länge des temporären Passworts setzen"),
|
||||||
|
("Enable Remote Restart", "Entfernten Neustart aktivieren"),
|
||||||
|
("Allow remote restart", "Entfernten Neustart erlauben"),
|
||||||
|
("Restart Remote Device", "Entferntes Gerät neu starten"),
|
||||||
|
("Are you sure you want to restart", "Möchten Sie das entfernte Gerät wirklich neu starten?"),
|
||||||
|
("Restarting Remote Device", "Entferntes Gerät wird neu gestartet"),
|
||||||
|
("remote_restarting_tip", "Entferntes Gerät startet neu, bitte schließen Sie diese Meldung und verbinden Sie sich mit dem dauerhaften Passwort erneut."),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -28,5 +28,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("doc_fix_wayland", "https://rustdesk.com/docs/en/manual/linux/#x11-required"),
|
("doc_fix_wayland", "https://rustdesk.com/docs/en/manual/linux/#x11-required"),
|
||||||
("server_not_support", "Not yet supported by the server"),
|
("server_not_support", "Not yet supported by the server"),
|
||||||
("android_open_battery_optimizations_tip", "If you want to disable this feature, please go to the next RustDesk application settings page, find and enter [Battery], Uncheck [Unrestricted]"),
|
("android_open_battery_optimizations_tip", "If you want to disable this feature, please go to the next RustDesk application settings page, find and enter [Battery], Uncheck [Unrestricted]"),
|
||||||
|
("remote_restarting_tip", "Remote device is restarting, please close this message box and reconnect with permanent password after a while"),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -287,20 +287,19 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Keep RustDesk background service", ""),
|
("Keep RustDesk background service", ""),
|
||||||
("Ignore Battery Optimizations", ""),
|
("Ignore Battery Optimizations", ""),
|
||||||
("android_open_battery_optimizations_tip", ""),
|
("android_open_battery_optimizations_tip", ""),
|
||||||
("Random Password After Session", ""),
|
|
||||||
("Keep", ""),
|
|
||||||
("Update", ""),
|
|
||||||
("Disable", ""),
|
|
||||||
("Onetime Password", ""),
|
|
||||||
("Verification Method", ""),
|
|
||||||
("Enable security password", ""),
|
|
||||||
("Enable random password", ""),
|
|
||||||
("Enable onetime password", ""),
|
|
||||||
("Disable onetime password", ""),
|
|
||||||
("Activate onetime password", ""),
|
|
||||||
("Set security password", ""),
|
|
||||||
("Connection not allowed", ""),
|
("Connection not allowed", ""),
|
||||||
("Legacy mode", ""),
|
("Legacy mode", ""),
|
||||||
("Map mode", ""),
|
("Map mode", ""),
|
||||||
|
("Use temporary password", ""),
|
||||||
|
("Use permanent password", ""),
|
||||||
|
("Use both passwords", ""),
|
||||||
|
("Set permanent password", ""),
|
||||||
|
("Set temporary password length", ""),
|
||||||
|
("Enable Remote Restart", ""),
|
||||||
|
("Allow remote restart", ""),
|
||||||
|
("Restart Remote Device", ""),
|
||||||
|
("Are you sure you want to restart", ""),
|
||||||
|
("Restarting Remote Device", ""),
|
||||||
|
("remote_restarting_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -3,15 +3,15 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
[
|
[
|
||||||
("Status", "Estado"),
|
("Status", "Estado"),
|
||||||
("Your Desktop", "Tu escritorio"),
|
("Your Desktop", "Tu escritorio"),
|
||||||
("desk_tip", "Puoi accedere al tuo desktop usando l'ID e la password riportati qui."),
|
("desk_tip", "Tu escritorio puede ser ingresado con esta ID y contraseña."),
|
||||||
("Password", "Contraseña"),
|
("Password", "Contraseña"),
|
||||||
("Ready", "Listo"),
|
("Ready", "Listo"),
|
||||||
("Established", "Establecido"),
|
("Established", "Establecido"),
|
||||||
("connecting_status", "Conexión a la red RustDesk en progreso..."),
|
("connecting_status", "Conexión a la red RustDesk en progreso..."),
|
||||||
("Enable Service", "Habilitar Servicio"),
|
("Enable Service", "Habilitar Servicio"),
|
||||||
("Start Service", "Iniciar Servicio"),
|
("Start Service", "Iniciar Servicio"),
|
||||||
("Service is running", "Servicio se está ejecutando"),
|
("Service is running", "El servicio se está ejecutando"),
|
||||||
("Service is not running", "Servicio no se está ejecutando"),
|
("Service is not running", "El servicio no se está ejecutando"),
|
||||||
("not_ready_status", "No está listo. Comprueba tu conexión"),
|
("not_ready_status", "No está listo. Comprueba tu conexión"),
|
||||||
("Control Remote Desktop", "Controlar Escritorio Remoto"),
|
("Control Remote Desktop", "Controlar Escritorio Remoto"),
|
||||||
("Transfer File", "Transferir archivo"),
|
("Transfer File", "Transferir archivo"),
|
||||||
@ -27,15 +27,15 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Enable Clipboard", "Habilitar portapapeles"),
|
("Enable Clipboard", "Habilitar portapapeles"),
|
||||||
("Enable File Transfer", "Habilitar transferencia de archivos"),
|
("Enable File Transfer", "Habilitar transferencia de archivos"),
|
||||||
("Enable TCP Tunneling", "Habilitar tunel TCP"),
|
("Enable TCP Tunneling", "Habilitar tunel TCP"),
|
||||||
("IP Whitelisting", "Lista blanca IP"),
|
("IP Whitelisting", "Lista blanca de IP"),
|
||||||
("ID/Relay Server", "Servidor de ID/Relay"),
|
("ID/Relay Server", "Servidor ID/Relay"),
|
||||||
("Stop service", "Parar servicio"),
|
("Stop service", "Parar servicio"),
|
||||||
("Change ID", "Cambiar identificación"),
|
("Change ID", "Cambiar ID"),
|
||||||
("Website", "Sitio web"),
|
("Website", "Sitio web"),
|
||||||
("About", "Sobre"),
|
("About", "Acerca de"),
|
||||||
("Mute", "Silencio"),
|
("Mute", "Silencio"),
|
||||||
("Audio Input", "Entrada de audio"),
|
("Audio Input", "Entrada de audio"),
|
||||||
("Enhancements", ""),
|
("Enhancements", "mejoras"),
|
||||||
("Hardware Codec", ""),
|
("Hardware Codec", ""),
|
||||||
("Adaptive Bitrate", ""),
|
("Adaptive Bitrate", ""),
|
||||||
("ID Server", "ID server"),
|
("ID Server", "ID server"),
|
||||||
@ -46,12 +46,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("id_change_tip", "Solo puedes usar caracteres a-z, A-Z, 0-9 e _ (guion bajo). El primer carácter debe ser a-z o A-Z. La longitud debe estar entre 6 a 16 caracteres."),
|
("id_change_tip", "Solo puedes usar caracteres a-z, A-Z, 0-9 e _ (guion bajo). El primer carácter debe ser a-z o A-Z. La longitud debe estar entre 6 a 16 caracteres."),
|
||||||
("Invalid format", "Formato inválido"),
|
("Invalid format", "Formato inválido"),
|
||||||
("server_not_support", "Aún no es compatible con el servidor"),
|
("server_not_support", "Aún no es compatible con el servidor"),
|
||||||
("Not available", "Indisponible"),
|
("Not available", "No disponible"),
|
||||||
("Too frequent", "Demasiado frecuente"),
|
("Too frequent", "Demasiado frecuente"),
|
||||||
("Cancel", "Cancelar"),
|
("Cancel", "Cancelar"),
|
||||||
("Skip", "Saltar"),
|
("Skip", "Saltar"),
|
||||||
("Close", "Cerrar"),
|
("Close", "Cerrar"),
|
||||||
("Retry", "Volver"),
|
("Retry", "Reintentar"),
|
||||||
("OK", "OK"),
|
("OK", "OK"),
|
||||||
("Password Required", "Se requiere contraseña"),
|
("Password Required", "Se requiere contraseña"),
|
||||||
("Please enter your password", "Por favor, introduzca su contraseña"),
|
("Please enter your password", "Por favor, introduzca su contraseña"),
|
||||||
@ -108,7 +108,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Optimize reaction time", "Optimizar el tiempo de reacción"),
|
("Optimize reaction time", "Optimizar el tiempo de reacción"),
|
||||||
("Custom", "Personalizado"),
|
("Custom", "Personalizado"),
|
||||||
("Show remote cursor", "Mostrar cursor remoto"),
|
("Show remote cursor", "Mostrar cursor remoto"),
|
||||||
("Show quality monitor", ""),
|
("Show quality monitor", "Mostrar calidad del monitor"),
|
||||||
("Disable clipboard", "Deshabilitar portapapeles"),
|
("Disable clipboard", "Deshabilitar portapapeles"),
|
||||||
("Lock after session end", "Bloquear después del final de la sesión"),
|
("Lock after session end", "Bloquear después del final de la sesión"),
|
||||||
("Insert", "Insertar"),
|
("Insert", "Insertar"),
|
||||||
@ -117,7 +117,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("ID does not exist", "ID no existe"),
|
("ID does not exist", "ID no existe"),
|
||||||
("Failed to connect to rendezvous server", "No se pudo conectar al servidor de encuentro"),
|
("Failed to connect to rendezvous server", "No se pudo conectar al servidor de encuentro"),
|
||||||
("Please try later", "Por favor intente mas tarde"),
|
("Please try later", "Por favor intente mas tarde"),
|
||||||
("Remote desktop is offline", "El escritorio remoto está fuera de línea"),
|
("Remote desktop is offline", "El escritorio remoto está desconectado"),
|
||||||
("Key mismatch", "La clave no coincide"),
|
("Key mismatch", "La clave no coincide"),
|
||||||
("Timeout", "Timeout"),
|
("Timeout", "Timeout"),
|
||||||
("Failed to connect to relay server", "No se pudo conectar al servidor de retransmisión"),
|
("Failed to connect to relay server", "No se pudo conectar al servidor de retransmisión"),
|
||||||
@ -129,7 +129,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("install_tip", "Debido al Control de cuentas de usuario, es posible que RustDesk no funcione correctamente como escritorio remoto. Para evitar este problema, haga clic en el botón de abajo para instalar RustDesk a nivel de sistema."),
|
("install_tip", "Debido al Control de cuentas de usuario, es posible que RustDesk no funcione correctamente como escritorio remoto. Para evitar este problema, haga clic en el botón de abajo para instalar RustDesk a nivel de sistema."),
|
||||||
("Click to upgrade", "Clic para actualizar"),
|
("Click to upgrade", "Clic para actualizar"),
|
||||||
("Click to download", "Clic para descargar"),
|
("Click to download", "Clic para descargar"),
|
||||||
("Click to update", "Fare clic per aggiornare"),
|
("Click to update", "Clic para refrescar"),
|
||||||
("Configure", "Configurar"),
|
("Configure", "Configurar"),
|
||||||
("config_acc", "Para controlar su escritorio desde el exterior, debe otorgar permiso a RustDesk de \"Accesibilidad\"."),
|
("config_acc", "Para controlar su escritorio desde el exterior, debe otorgar permiso a RustDesk de \"Accesibilidad\"."),
|
||||||
("config_screen", "Para controlar su escritorio desde el exterior, debe otorgar permiso a RustDesk de \"Grabación de pantalla\"."),
|
("config_screen", "Para controlar su escritorio desde el exterior, debe otorgar permiso a RustDesk de \"Grabación de pantalla\"."),
|
||||||
@ -283,9 +283,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Turned off", "Apagado"),
|
("Turned off", "Apagado"),
|
||||||
("In privacy mode", "En modo de privacidad"),
|
("In privacy mode", "En modo de privacidad"),
|
||||||
("Out privacy mode", "Fuera del modo de privacidad"),
|
("Out privacy mode", "Fuera del modo de privacidad"),
|
||||||
("Language", ""),
|
("Language", "Idioma"),
|
||||||
("Keep RustDesk background service", ""),
|
("Keep RustDesk background service", "Dejar RustDesk como Servicio en 2do plano"),
|
||||||
("Ignore Battery Optimizations", ""),
|
("Ignore Battery Optimizations", "Ignorar optimizacioens de bateria"),
|
||||||
("android_open_battery_optimizations_tip", ""),
|
("android_open_battery_optimizations_tip", ""),
|
||||||
("Random Password After Session", ""),
|
("Random Password After Session", ""),
|
||||||
("Keep", ""),
|
("Keep", ""),
|
||||||
@ -302,5 +302,17 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Connection not allowed", ""),
|
("Connection not allowed", ""),
|
||||||
("Legacy mode", ""),
|
("Legacy mode", ""),
|
||||||
("Map mode", ""),
|
("Map mode", ""),
|
||||||
|
("Connection not allowed", "Conexión no disponible"),
|
||||||
|
("Use temporary password", "Usar contraseña temporal"),
|
||||||
|
("Use permanent password", "Usar contraseña permamente"),
|
||||||
|
("Use both passwords", "Usar ambas comtraseñas"),
|
||||||
|
("Set permanent password", "Establecer contraseña permamente"),
|
||||||
|
("Set temporary password length", "Establecer largo de contraseña temporal"),
|
||||||
|
("Enable Remote Restart", "Activar reinicio remoto"),
|
||||||
|
("Allow remote restart", "Permitir reinicio remoto"),
|
||||||
|
("Restart Remote Device", "Reiniciar dispositivo"),
|
||||||
|
("Are you sure you want to restart", "Esta Seguro que desea reiniciar?"),
|
||||||
|
("Restarting Remote Device", "Reiniciando dispositivo remoto"),
|
||||||
|
("remote_restarting_tip", "Dispositivo remoto reiniciando, favor de cerrar este mensaje y reconectarse con la contraseña permamente despues de un momento."),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -287,20 +287,19 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Keep RustDesk background service", ""),
|
("Keep RustDesk background service", ""),
|
||||||
("Ignore Battery Optimizations", ""),
|
("Ignore Battery Optimizations", ""),
|
||||||
("android_open_battery_optimizations_tip", ""),
|
("android_open_battery_optimizations_tip", ""),
|
||||||
("Random Password After Session", ""),
|
|
||||||
("Keep", ""),
|
|
||||||
("Update", ""),
|
|
||||||
("Disable", ""),
|
|
||||||
("Onetime Password", ""),
|
|
||||||
("Verification Method", ""),
|
|
||||||
("Enable security password", ""),
|
|
||||||
("Enable random password", ""),
|
|
||||||
("Enable onetime password", ""),
|
|
||||||
("Disable onetime password", ""),
|
|
||||||
("Activate onetime password", ""),
|
|
||||||
("Set security password", ""),
|
|
||||||
("Connection not allowed", ""),
|
("Connection not allowed", ""),
|
||||||
("Legacy mode", ""),
|
("Legacy mode", ""),
|
||||||
("Map mode", ""),
|
("Map mode", ""),
|
||||||
|
("Use temporary password", ""),
|
||||||
|
("Use permanent password", ""),
|
||||||
|
("Use both passwords", ""),
|
||||||
|
("Set permanent password", ""),
|
||||||
|
("Set temporary password length", ""),
|
||||||
|
("Enable Remote Restart", ""),
|
||||||
|
("Allow remote restart", ""),
|
||||||
|
("Restart Remote Device", ""),
|
||||||
|
("Are you sure you want to restart", ""),
|
||||||
|
("Restarting Remote Device", ""),
|
||||||
|
("remote_restarting_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -287,20 +287,19 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Keep RustDesk background service", ""),
|
("Keep RustDesk background service", ""),
|
||||||
("Ignore Battery Optimizations", ""),
|
("Ignore Battery Optimizations", ""),
|
||||||
("android_open_battery_optimizations_tip", ""),
|
("android_open_battery_optimizations_tip", ""),
|
||||||
("Random Password After Session", ""),
|
|
||||||
("Keep", ""),
|
|
||||||
("Update", ""),
|
|
||||||
("Disable", ""),
|
|
||||||
("Onetime Password", ""),
|
|
||||||
("Verification Method", ""),
|
|
||||||
("Enable security password", ""),
|
|
||||||
("Enable random password", ""),
|
|
||||||
("Enable onetime password", ""),
|
|
||||||
("Disable onetime password", ""),
|
|
||||||
("Activate onetime password", ""),
|
|
||||||
("Set security password", ""),
|
|
||||||
("Connection not allowed", ""),
|
("Connection not allowed", ""),
|
||||||
("Legacy mode", ""),
|
("Legacy mode", ""),
|
||||||
("Map mode", ""),
|
("Map mode", ""),
|
||||||
|
("Use temporary password", ""),
|
||||||
|
("Use permanent password", ""),
|
||||||
|
("Use both passwords", ""),
|
||||||
|
("Set permanent password", ""),
|
||||||
|
("Set temporary password length", ""),
|
||||||
|
("Enable Remote Restart", ""),
|
||||||
|
("Allow remote restart", ""),
|
||||||
|
("Restart Remote Device", ""),
|
||||||
|
("Are you sure you want to restart", ""),
|
||||||
|
("Restarting Remote Device", ""),
|
||||||
|
("remote_restarting_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -35,9 +35,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("About", "Tentang"),
|
("About", "Tentang"),
|
||||||
("Mute", "Bisukan"),
|
("Mute", "Bisukan"),
|
||||||
("Audio Input", "Masukkan Audio"),
|
("Audio Input", "Masukkan Audio"),
|
||||||
("Enhancements", ""),
|
("Enhancements", "Peningkatan"),
|
||||||
("Hardware Codec", ""),
|
("Hardware Codec", "Codec Perangkat Keras"),
|
||||||
("Adaptive Bitrate", ""),
|
("Adaptive Bitrate", "Kecepatan Bitrate Adaptif"),
|
||||||
("ID Server", "Server ID"),
|
("ID Server", "Server ID"),
|
||||||
("Relay Server", "Server Relay"),
|
("Relay Server", "Server Relay"),
|
||||||
("API Server", "API Server"),
|
("API Server", "API Server"),
|
||||||
@ -268,7 +268,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("android_start_service_tip", "Ketuk izin [Mulai Layanan] atau BUKA [Tangkapan Layar] untuk memulai layanan berbagi layar."),
|
("android_start_service_tip", "Ketuk izin [Mulai Layanan] atau BUKA [Tangkapan Layar] untuk memulai layanan berbagi layar."),
|
||||||
("Account", "Akun"),
|
("Account", "Akun"),
|
||||||
("Overwrite", "Timpa"),
|
("Overwrite", "Timpa"),
|
||||||
("This file exists, skip or overwrite this file?", ""),
|
("This file exists, skip or overwrite this file?", "File ini sudah ada, lewati atau timpa file ini?"),
|
||||||
("Quit", "Keluar"),
|
("Quit", "Keluar"),
|
||||||
("doc_mac_permission", "https://rustdesk.com/docs/en/manual/mac/#enable-permissions"),
|
("doc_mac_permission", "https://rustdesk.com/docs/en/manual/mac/#enable-permissions"),
|
||||||
("Help", "Bantuan"),
|
("Help", "Bantuan"),
|
||||||
@ -283,9 +283,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Turned off", "Matikan"),
|
("Turned off", "Matikan"),
|
||||||
("In privacy mode", "Dalam mode privasi"),
|
("In privacy mode", "Dalam mode privasi"),
|
||||||
("Out privacy mode", "Keluar dari mode privasi"),
|
("Out privacy mode", "Keluar dari mode privasi"),
|
||||||
("Language", ""),
|
("Language", "Bahasa"),
|
||||||
("Keep RustDesk background service", ""),
|
("Keep RustDesk background service", "Pertahankan RustDesk berjalan pada background service"),
|
||||||
("Ignore Battery Optimizations", ""),
|
("Ignore Battery Optimizations", "Abaikan Pengoptimalan Baterai"),
|
||||||
("android_open_battery_optimizations_tip", ""),
|
("android_open_battery_optimizations_tip", ""),
|
||||||
("Random Password After Session", ""),
|
("Random Password After Session", ""),
|
||||||
("Keep", ""),
|
("Keep", ""),
|
||||||
@ -302,5 +302,17 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Connection not allowed", ""),
|
("Connection not allowed", ""),
|
||||||
("Legacy mode", ""),
|
("Legacy mode", ""),
|
||||||
("Map mode", ""),
|
("Map mode", ""),
|
||||||
|
("Connection not allowed", "Koneksi tidak dijinkan"),
|
||||||
|
("Use temporary password", "Gunakan kata sandi sementara"),
|
||||||
|
("Use permanent password", "Gunakan kata sandi permanaen"),
|
||||||
|
("Use both passwords", "Gunakan kedua kata sandi "),
|
||||||
|
("Set permanent password", "Setel kata sandi permanen"),
|
||||||
|
("Set temporary password length", "Setel panjang kata sandi sementara"),
|
||||||
|
("Enable Remote Restart", "Aktifkan Restart Jarak Jauh"),
|
||||||
|
("Allow remote restart", "Ijinkan Restart Jarak Jauh"),
|
||||||
|
("Restart Remote Device", "Restart Perangkat Jarak Jauh"),
|
||||||
|
("Are you sure you want to restart", "Apakah Anda yakin untuk memulai ulang"),
|
||||||
|
("Restarting Remote Device", "Memulai Ulang Perangkat Jarak Jauh"),
|
||||||
|
("remote_restarting_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -287,20 +287,19 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Keep RustDesk background service", ""),
|
("Keep RustDesk background service", ""),
|
||||||
("Ignore Battery Optimizations", ""),
|
("Ignore Battery Optimizations", ""),
|
||||||
("android_open_battery_optimizations_tip", ""),
|
("android_open_battery_optimizations_tip", ""),
|
||||||
("Random Password After Session", ""),
|
|
||||||
("Keep", ""),
|
|
||||||
("Update", ""),
|
|
||||||
("Disable", ""),
|
|
||||||
("Onetime Password", ""),
|
|
||||||
("Verification Method", ""),
|
|
||||||
("Enable security password", ""),
|
|
||||||
("Enable random password", ""),
|
|
||||||
("Enable onetime password", ""),
|
|
||||||
("Disable onetime password", ""),
|
|
||||||
("Activate onetime password", ""),
|
|
||||||
("Set security password", ""),
|
|
||||||
("Connection not allowed", ""),
|
("Connection not allowed", ""),
|
||||||
("Legacy mode", ""),
|
("Legacy mode", ""),
|
||||||
("Map mode", ""),
|
("Map mode", ""),
|
||||||
|
("Use temporary password", ""),
|
||||||
|
("Use permanent password", ""),
|
||||||
|
("Use both passwords", ""),
|
||||||
|
("Set permanent password", ""),
|
||||||
|
("Set temporary password length", ""),
|
||||||
|
("Enable Remote Restart", ""),
|
||||||
|
("Allow remote restart", ""),
|
||||||
|
("Restart Remote Device", ""),
|
||||||
|
("Are you sure you want to restart", ""),
|
||||||
|
("Restarting Remote Device", ""),
|
||||||
|
("remote_restarting_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
303
src/lang/ja.rs
Normal file
303
src/lang/ja.rs
Normal file
@ -0,0 +1,303 @@
|
|||||||
|
lazy_static::lazy_static! {
|
||||||
|
pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||||
|
[
|
||||||
|
("Status", "状態"),
|
||||||
|
("Your Desktop", "デスクトップ"),
|
||||||
|
("desk_tip", "このIDとパスワードであなたのデスクトップにアクセスできます。"),
|
||||||
|
("Password", "パスワード"),
|
||||||
|
("Ready", "準備完了"),
|
||||||
|
("Established", "接続完了"),
|
||||||
|
("connecting_status", "RuskDeskネットワークに接続中..."),
|
||||||
|
("Enable Service", "サービスを有効化"),
|
||||||
|
("Start Service", "サービスを開始"),
|
||||||
|
("Service is running", "サービスは動作中"),
|
||||||
|
("Service is not running", "サービスは動作していません"),
|
||||||
|
("not_ready_status", "準備できていません。接続を確認してください。"),
|
||||||
|
("Control Remote Desktop", "リモートのデスクトップを操作する"),
|
||||||
|
("Transfer File", "ファイルを転送"),
|
||||||
|
("Connect", "接続"),
|
||||||
|
("Recent Sessions", "最近のセッション"),
|
||||||
|
("Address Book", "アドレス帳"),
|
||||||
|
("Confirmation", "確認用"),
|
||||||
|
("TCP Tunneling", "TCPトンネリング"),
|
||||||
|
("Remove", "削除"),
|
||||||
|
("Refresh random password", "ランダムパスワードを再生成"),
|
||||||
|
("Set your own password", "自分のパスワードを設定"),
|
||||||
|
("Enable Keyboard/Mouse", "キーボード・マウスを有効化"),
|
||||||
|
("Enable Clipboard", "クリップボードを有効化"),
|
||||||
|
("Enable File Transfer", "ファイル転送を有効化"),
|
||||||
|
("Enable TCP Tunneling", "TCPトンネリングを有効化"),
|
||||||
|
("IP Whitelisting", "IPホワイトリスト"),
|
||||||
|
("ID/Relay Server", "認証・中継サーバー"),
|
||||||
|
("Stop service", "サービスを停止"),
|
||||||
|
("Change ID", "IDを変更"),
|
||||||
|
("Website", "公式サイト"),
|
||||||
|
("About", "情報"),
|
||||||
|
("Mute", "ミュート"),
|
||||||
|
("Audio Input", "音声入力デバイス"),
|
||||||
|
("Enhancements", "追加機能"),
|
||||||
|
("Hardware Codec", "ハードウェア コーデック"),
|
||||||
|
("Adaptive Bitrate", "アダプティブビットレート"),
|
||||||
|
("ID Server", "認証サーバー"),
|
||||||
|
("Relay Server", "中継サーバー"),
|
||||||
|
("API Server", "APIサーバー"),
|
||||||
|
("invalid_http", "http:// もしくは https:// から入力してください"),
|
||||||
|
("Invalid IP", "無効なIP"),
|
||||||
|
("id_change_tip", "使用できるのは大文字・小文字のアルファベット、数字、アンダースコア(_)のみです。初めの文字はアルファベットにする必要があります。6文字から16文字までです。"),
|
||||||
|
("Invalid format", "無効な形式"),
|
||||||
|
("server_not_support", "サーバー側でまだサポートされていません"),
|
||||||
|
("Not available", "利用不可"),
|
||||||
|
("Too frequent", "使用量が多すぎです"),
|
||||||
|
("Cancel", "キャンセル"),
|
||||||
|
("Skip", "スキップ"),
|
||||||
|
("Close", "閉じる"),
|
||||||
|
("Retry", "再試行"),
|
||||||
|
("OK", "OK"),
|
||||||
|
("Password Required", "パスワードが必要"),
|
||||||
|
("Please enter your password", "パスワードを入力してください"),
|
||||||
|
("Remember password", "パスワードを記憶する"),
|
||||||
|
("Wrong Password", "パスワードが間違っています"),
|
||||||
|
("Do you want to enter again?", "もう一度入力しますか?"),
|
||||||
|
("Connection Error", "接続エラー"),
|
||||||
|
("Error", "エラー"),
|
||||||
|
("Reset by the peer", "相手がリセットしました"),
|
||||||
|
("Connecting...", "接続中..."),
|
||||||
|
("Connection in progress. Please wait.", "接続中です。しばらくお待ちください。"),
|
||||||
|
("Please try 1 minute later", "1分後にもう一度お試しください"),
|
||||||
|
("Login Error", "ログインエラー"),
|
||||||
|
("Successful", "成功"),
|
||||||
|
("Connected, waiting for image...", "接続完了、画像を取得中..."),
|
||||||
|
("Name", "名前"),
|
||||||
|
("Type", "種類"),
|
||||||
|
("Modified", "最終更新"),
|
||||||
|
("Size", "サイズ"),
|
||||||
|
("Show Hidden Files", "隠しファイルを表示"),
|
||||||
|
("Receive", "受信"),
|
||||||
|
("Send", "送信"),
|
||||||
|
("Refresh File", "ファイルを更新"),
|
||||||
|
("Local", "ローカル"),
|
||||||
|
("Remote", "リモート"),
|
||||||
|
("Remote Computer", "リモート側コンピューター"),
|
||||||
|
("Local Computer", "ローカル側コンピューター"),
|
||||||
|
("Confirm Delete", "削除の確認"),
|
||||||
|
("Delete", "削除"),
|
||||||
|
("Properties", "プロパティ"),
|
||||||
|
("Multi Select", "複数選択"),
|
||||||
|
("Empty Directory", "空のディレクトリ"),
|
||||||
|
("Not an empty directory", "空ではないディレクトリ"),
|
||||||
|
("Are you sure you want to delete this file?", "本当にこのファイルを削除しますか?"),
|
||||||
|
("Are you sure you want to delete this empty directory?", "本当にこの空のディレクトリを削除しますか?"),
|
||||||
|
("Are you sure you want to delete the file of this directory?", "本当にこのディレクトリ内のファイルを削除しますか?"),
|
||||||
|
("Do this for all conflicts", "他のすべてにも適用する"),
|
||||||
|
("This is irreversible!", "この操作は元に戻せません!"),
|
||||||
|
("Deleting", "削除中"),
|
||||||
|
("files", "ファイル"),
|
||||||
|
("Waiting", "待機中"),
|
||||||
|
("Finished", "完了"),
|
||||||
|
("Speed", "速度"),
|
||||||
|
("Custom Image Quality", "画質を調整"),
|
||||||
|
("Privacy mode", "プライバシーモード"),
|
||||||
|
("Block user input", "ユーザーの入力をブロック"),
|
||||||
|
("Unblock user input", "ユーザーの入力を許可"),
|
||||||
|
("Adjust Window", "ウィンドウを調整"),
|
||||||
|
("Original", "オリジナル"),
|
||||||
|
("Shrink", "縮小"),
|
||||||
|
("Stretch", "伸縮"),
|
||||||
|
("Good image quality", "画質優先"),
|
||||||
|
("Balanced", "バランス"),
|
||||||
|
("Optimize reaction time", "速度優先"),
|
||||||
|
("Custom", "カスタム"),
|
||||||
|
("Show remote cursor", "リモート側のカーソルを表示"),
|
||||||
|
("Show quality monitor", "品質モニターを表示"),
|
||||||
|
("Disable clipboard", "クリップボードを無効化"),
|
||||||
|
("Lock after session end", "セッション終了後にロックする"),
|
||||||
|
("Insert", "送信"),
|
||||||
|
("Insert Lock", "ロック命令を送信"),
|
||||||
|
("Refresh", "更新"),
|
||||||
|
("ID does not exist", "IDが存在しません"),
|
||||||
|
("Failed to connect to rendezvous server", "ランデブーサーバーに接続できませんでした"),
|
||||||
|
("Please try later", "後でもう一度お試しください"),
|
||||||
|
("Remote desktop is offline", "リモート側デスクトップがオフラインです"),
|
||||||
|
("Key mismatch", "キーが一致しません"),
|
||||||
|
("Timeout", "タイムアウト"),
|
||||||
|
("Failed to connect to relay server", "中継サーバーに接続できませんでした"),
|
||||||
|
("Failed to connect via rendezvous server", "ランデブーサーバー経由で接続できませんでした"),
|
||||||
|
("Failed to connect via relay server", "中継サーバー経由で接続できませんでした"),
|
||||||
|
("Failed to make direct connection to remote desktop", "リモート側デスクトップと直接接続できませんでした"),
|
||||||
|
("Set Password", "パスワードを設定"),
|
||||||
|
("OS Password", "OSのパスワード"),
|
||||||
|
("install_tip", "RustDeskがUACの影響によりリモート側で正常に動作しない場合があります。UACを回避するには、下のボタンをクリックしてシステムにRustDeskをインストールしてください。"),
|
||||||
|
("Click to upgrade", "アップグレード"),
|
||||||
|
("Click to download", "ダウンロード"),
|
||||||
|
("Click to update", "アップデート"),
|
||||||
|
("Configure", "設定"),
|
||||||
|
("config_acc", "リモートからあなたのデスクトップを操作するには、RustDeskに「アクセシビリティ」権限を与える必要があります。"),
|
||||||
|
("config_screen", "リモートからあなたのデスクトップにアクセスするには、RustDeskに「画面収録」権限を与える必要があります。"),
|
||||||
|
("Installing ...", "インストール中..."),
|
||||||
|
("Install", "インストール"),
|
||||||
|
("Installation", "インストール"),
|
||||||
|
("Installation Path", "インストール先のパス"),
|
||||||
|
("Create start menu shortcuts", "スタートメニューにショートカットを作成する"),
|
||||||
|
("Create desktop icon", "デスクトップにアイコンを作成する"),
|
||||||
|
("agreement_tip", "インストールを開始することで、ライセンス条項に同意したとみなされます。"),
|
||||||
|
("Accept and Install", "同意してインストール"),
|
||||||
|
("End-user license agreement", "エンドユーザー ライセンス条項"),
|
||||||
|
("Generating ...", "生成中 ..."),
|
||||||
|
("Your installation is lower version.", "インストール済みのバージョンが古いです。"),
|
||||||
|
("not_close_tcp_tip", "トンネルを使用中はこのウィンドウを閉じないでください"),
|
||||||
|
("Listening ...", "リッスン中 ..."),
|
||||||
|
("Remote Host", "リモートのホスト"),
|
||||||
|
("Remote Port", "リモートのポート"),
|
||||||
|
("Action", "操作"),
|
||||||
|
("Add", "追加"),
|
||||||
|
("Local Port", "ローカルのポート"),
|
||||||
|
("setup_server_tip", "接続をより速くするには、自分のサーバーをセットアップしてください"),
|
||||||
|
("Too short, at least 6 characters.", "短すぎます。最低6文字です。"),
|
||||||
|
("The confirmation is not identical.", "確認用と一致しません。"),
|
||||||
|
("Permissions", "権限"),
|
||||||
|
("Accept", "承諾"),
|
||||||
|
("Dismiss", "無視"),
|
||||||
|
("Disconnect", "切断"),
|
||||||
|
("Allow using keyboard and mouse", "キーボード・マウスの使用を許可"),
|
||||||
|
("Allow using clipboard", "クリップボードの使用を許可"),
|
||||||
|
("Allow hearing sound", "サウンドの受信を許可"),
|
||||||
|
("Allow file copy and paste", "ファイルのコピーアンドペーストを許可"),
|
||||||
|
("Connected", "接続済み"),
|
||||||
|
("Direct and encrypted connection", "接続は暗号化され、直接つながっている"),
|
||||||
|
("Relayed and encrypted connection", "接続は暗号化され、中継されている"),
|
||||||
|
("Direct and unencrypted connection", "接続は暗号化されてなく、直接つながっている"),
|
||||||
|
("Relayed and unencrypted connection", "接続は暗号化されてなく、中継されている"),
|
||||||
|
("Enter Remote ID", "リモートのIDを入力"),
|
||||||
|
("Enter your password", "パスワードを入力"),
|
||||||
|
("Logging in...", "ログイン中..."),
|
||||||
|
("Enable RDP session sharing", "RDPセッション共有を有効化"),
|
||||||
|
("Auto Login", "自動ログイン"),
|
||||||
|
("Enable Direct IP Access", "直接IPアクセスを有効化"),
|
||||||
|
("Rename", "名前の変更"),
|
||||||
|
("Space", "スペース"),
|
||||||
|
("Create Desktop Shortcut", "デスクトップにショートカットを作成する"),
|
||||||
|
("Change Path", "パスを変更"),
|
||||||
|
("Create Folder", "フォルダを作成"),
|
||||||
|
("Please enter the folder name", "フォルダ名を入力してください"),
|
||||||
|
("Fix it", "修復"),
|
||||||
|
("Warning", "注意"),
|
||||||
|
("Login screen using Wayland is not supported", "Waylandを使用したログインスクリーンはサポートされていません"),
|
||||||
|
("Reboot required", "再起動が必要"),
|
||||||
|
("Unsupported display server ", "サポートされていないディスプレイサーバー"),
|
||||||
|
("x11 expected", "X11 が必要です"),
|
||||||
|
("Port", "ポート"),
|
||||||
|
("Settings", "設定"),
|
||||||
|
("Username", "ユーザー名"),
|
||||||
|
("Invalid port", "無効なポート"),
|
||||||
|
("Closed manually by the peer", "相手が手動で切断しました"),
|
||||||
|
("Enable remote configuration modification", "リモート設定変更を有効化"),
|
||||||
|
("Run without install", "インストールせずに実行"),
|
||||||
|
("Always connected via relay", "常に中継サーバー経由で接続"),
|
||||||
|
("Always connect via relay", "常に中継サーバー経由で接続"),
|
||||||
|
("whitelist_tip", "ホワイトリストに登録されたIPからのみ接続を許可します"),
|
||||||
|
("Login", "ログイン"),
|
||||||
|
("Logout", "ログアウト"),
|
||||||
|
("Tags", "タグ"),
|
||||||
|
("Search ID", "IDを検索"),
|
||||||
|
("Current Wayland display server is not supported", "現在のWaylandディスプレイサーバーはサポートされていません"),
|
||||||
|
("whitelist_sep", "カンマやセミコロン、空白、改行で区切ってください"),
|
||||||
|
("Add ID", "IDを追加"),
|
||||||
|
("Add Tag", "タグを追加"),
|
||||||
|
("Unselect all tags", "全てのタグを選択解除"),
|
||||||
|
("Network error", "ネットワークエラー"),
|
||||||
|
("Username missed", "ユーザー名がありません"),
|
||||||
|
("Password missed", "パスワードがありません"),
|
||||||
|
("Wrong credentials", "資格情報が間違っています"),
|
||||||
|
("Edit Tag", "タグを編集"),
|
||||||
|
("Unremember Password", "パスワードの記憶を解除"),
|
||||||
|
("Favorites", "お気に入り"),
|
||||||
|
("Add to Favorites", "お気に入りに追加"),
|
||||||
|
("Remove from Favorites", "お気に入りから削除"),
|
||||||
|
("Empty", "空"),
|
||||||
|
("Invalid folder name", "無効なフォルダ名"),
|
||||||
|
("Socks5 Proxy", "SOCKS5プロキシ"),
|
||||||
|
("Hostname", "ホスト名"),
|
||||||
|
("Discovered", "探知済み"),
|
||||||
|
("install_daemon_tip", "起動時に開始するには、システムサービスをインストールする必要があります。"),
|
||||||
|
("Remote ID", "リモートのID"),
|
||||||
|
("Paste", "ペースト"),
|
||||||
|
("Paste here?", "ここにペースト?"),
|
||||||
|
("Are you sure to close the connection?", "本当に切断しますか?"),
|
||||||
|
("Download new version", "新しいバージョンをダウンロード"),
|
||||||
|
("Touch mode", "タッチモード"),
|
||||||
|
("Mouse mode", "マウスモード"),
|
||||||
|
("One-Finger Tap", "1本指でタップ"),
|
||||||
|
("Left Mouse", "マウス左クリック"),
|
||||||
|
("One-Long Tap", "1本指でロングタップ"),
|
||||||
|
("Two-Finger Tap", "2本指でタップ"),
|
||||||
|
("Right Mouse", "マウス右クリック"),
|
||||||
|
("One-Finger Move", "1本指でドラッグ"),
|
||||||
|
("Double Tap & Move", "2本指でタップ&ドラッグ"),
|
||||||
|
("Mouse Drag", "マウスドラッグ"),
|
||||||
|
("Three-Finger vertically", "3本指で縦方向"),
|
||||||
|
("Mouse Wheel", "マウスホイール"),
|
||||||
|
("Two-Finger Move", "2本指でドラッグ"),
|
||||||
|
("Canvas Move", "キャンバスの移動"),
|
||||||
|
("Pinch to Zoom", "ピンチしてズーム"),
|
||||||
|
("Canvas Zoom", "キャンバスのズーム"),
|
||||||
|
("Reset canvas", "キャンバスのリセット"),
|
||||||
|
("No permission of file transfer", "ファイル転送の権限がありません"),
|
||||||
|
("Note", "ノート"),
|
||||||
|
("Connection", "接続"),
|
||||||
|
("Share Screen", "画面を共有"),
|
||||||
|
("CLOSE", "閉じる"),
|
||||||
|
("OPEN", "開く"),
|
||||||
|
("Chat", "チャット"),
|
||||||
|
("Total", "計"),
|
||||||
|
("items", "個のアイテム"),
|
||||||
|
("Selected", "選択済み"),
|
||||||
|
("Screen Capture", "画面キャプチャ"),
|
||||||
|
("Input Control", "入力操作"),
|
||||||
|
("Audio Capture", "音声キャプチャ"),
|
||||||
|
("File Connection", "ファイルの接続"),
|
||||||
|
("Screen Connection", "画面の接続"),
|
||||||
|
("Do you accept?", "承諾しますか?"),
|
||||||
|
("Open System Setting", "端末設定を開く"),
|
||||||
|
("How to get Android input permission?", "Androidの入力権限を取得するには?"),
|
||||||
|
("android_input_permission_tip1", "このAndroid端末をリモートの端末からマウスやタッチで操作するには、RustDeskに「アクセシビリティ」サービスの使用を許可する必要があります。"),
|
||||||
|
("android_input_permission_tip2", "次の端末設定ページに進み、「インストール済みアプリ」から「RestDesk Input」をオンにしてください。"),
|
||||||
|
("android_new_connection_tip", "新しい操作リクエストが届きました。この端末を操作しようとしています。"),
|
||||||
|
("android_service_will_start_tip", "「画面キャプチャ」をオンにするとサービスが自動的に開始され、他の端末がこの端末への接続をリクエストできるようになります。"),
|
||||||
|
("android_stop_service_tip", "サービスを停止すると、現在確立されている接続が全て自動的に閉じられます。"),
|
||||||
|
("android_version_audio_tip", "現在のAndroidバージョンでは音声キャプチャはサポートされていません。Android 10以降にアップグレードしてください。"),
|
||||||
|
("android_start_service_tip", "「サービスを開始」をタップするか「画面キャプチャ」を開くと、画面共有サービスが開始されます。"),
|
||||||
|
("Account", "アカウント"),
|
||||||
|
("Overwrite", "上書き"),
|
||||||
|
("This file exists, skip or overwrite this file?", "このファイルは存在しています。スキップするか上書きしますか?"),
|
||||||
|
("Quit", "終了"),
|
||||||
|
("doc_mac_permission", "https://rustdesk.com/docs/en/manual/mac/#enable-permissions"), // @TODO: Update url when someone translates the document
|
||||||
|
("Help", "ヘルプ"),
|
||||||
|
("Failed", "失敗"),
|
||||||
|
("Succeeded", "成功"),
|
||||||
|
("Someone turns on privacy mode, exit", "プライバシーモードがオンになりました。終了します。"),
|
||||||
|
("Unsupported", "サポートされていません"),
|
||||||
|
("Peer denied", "相手が拒否しました"),
|
||||||
|
("Please install plugins", "プラグインをインストールしてください"),
|
||||||
|
("Peer exit", "相手が終了しました"),
|
||||||
|
("Failed to turn off", "オフにできませんでした"),
|
||||||
|
("Turned off", "オフになりました"),
|
||||||
|
("In privacy mode", "プライバシーモード開始"),
|
||||||
|
("Out privacy mode", "プライバシーモード終了"),
|
||||||
|
("Language", "言語"),
|
||||||
|
("Keep RustDesk background service", "RustDesk バックグラウンドサービスを維持"),
|
||||||
|
("Ignore Battery Optimizations", "バッテリーの最適化を無効にする"),
|
||||||
|
("android_open_battery_optimizations_tip", "この機能を使わない場合は、次のRestDeskアプリ設定ページから「バッテリー」に進み、「制限なし」の選択を外してください"),
|
||||||
|
("Connection not allowed", "接続が許可されていません"),
|
||||||
|
("Use temporary password", "使い捨てのパスワードを使用"),
|
||||||
|
("Use permanent password", "固定のパスワードを使用"),
|
||||||
|
("Use both passwords", "どちらのパスワードも使用"),
|
||||||
|
("Set permanent password", "固定のパスワードを設定"),
|
||||||
|
("Set temporary password length", "使い捨てのパスワードの長さを設定"),
|
||||||
|
("Enable Remote Restart", "リモートからの再起動を有効化"),
|
||||||
|
("Allow remote restart", "リモートからの再起動を許可"),
|
||||||
|
("Restart Remote Device", "リモートの端末を再起動"),
|
||||||
|
("Are you sure you want to restart", "本当に再起動しますか"),
|
||||||
|
("Restarting Remote Device", "リモート端末を再起動中"),
|
||||||
|
("remote_restarting_tip", "リモート端末は再起動中です。このメッセージボックスを閉じて、しばらくした後に固定のパスワードを使用して再接続してください。"),
|
||||||
|
].iter().cloned().collect();
|
||||||
|
}
|
304
src/lang/pl.rs
Normal file
304
src/lang/pl.rs
Normal file
@ -0,0 +1,304 @@
|
|||||||
|
lazy_static::lazy_static! {
|
||||||
|
pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||||
|
[
|
||||||
|
("Status", "Status"),
|
||||||
|
("Your Desktop", "Twój pulpit"),
|
||||||
|
("desk_tip", "Podpowiedź"),
|
||||||
|
("Password", "Hasło"),
|
||||||
|
("Ready", "Gotowe"),
|
||||||
|
("Established", "Ustawiony"),
|
||||||
|
("connecting_status", "Status połączenia"),
|
||||||
|
("Enable Service", "Włącz usługę"),
|
||||||
|
("Start Service", "Uruchom usługę"),
|
||||||
|
("Service is running", "Usługa uruchomiona"),
|
||||||
|
("Service is not running", "Usługa nie jest uruchomiona"),
|
||||||
|
("not_ready_status", "Brak gotowości"),
|
||||||
|
("Control Remote Desktop", "Kontroluj zdalny pulpit"),
|
||||||
|
("Transfer File", "Prześlij plik"),
|
||||||
|
("Connect", "Połącz"),
|
||||||
|
("Recent Sessions", "Ostatnie sesje"),
|
||||||
|
("Address Book", "Książka adresowa"),
|
||||||
|
("Confirmation", "Potwierdzenie"),
|
||||||
|
("TCP Tunneling", "Tunelowanie TCP"),
|
||||||
|
("Remove", "Usuń"),
|
||||||
|
("Refresh random password", "Odśwież losowe hasło"),
|
||||||
|
("Set your own password", "Ustaw własne hasło"),
|
||||||
|
("Enable Keyboard/Mouse", "Włącz klawiaturę/mysz"),
|
||||||
|
("Enable Clipboard", "Włącz schowek"),
|
||||||
|
("Enable File Transfer", "Włącz przesyłanie pliku"),
|
||||||
|
("Enable TCP Tunneling", "Włącz tunelowanie TCP"),
|
||||||
|
("IP Whitelisting", "Biała lista IP"),
|
||||||
|
("ID/Relay Server", "ID/Relay Serwer"),
|
||||||
|
("Stop service", "Zatrzymaj usługę"),
|
||||||
|
("Change ID", "Zmień ID"),
|
||||||
|
("Website", "Strona internetowa"),
|
||||||
|
("About", "O"),
|
||||||
|
("Mute", "Wycisz"),
|
||||||
|
("Audio Input", "Wejście audio"),
|
||||||
|
("Enhancements", "Ulepszenia"),
|
||||||
|
("Hardware Codec", "Kodek sprzętowy"),
|
||||||
|
("Adaptive Bitrate", "Adaptacyjny bitrate"),
|
||||||
|
("ID Server", "ID Serwer"),
|
||||||
|
("Relay Server", "Relay serwer"),
|
||||||
|
("API Server", "API Serwer"),
|
||||||
|
("invalid_http", "Nieprawidłowy http"),
|
||||||
|
("Invalid IP", "Nieprawidłowy IP"),
|
||||||
|
("id_change_tip", "Podpowiedź zmiany ID"),
|
||||||
|
("Invalid format", "Nieprawidłowy format"),
|
||||||
|
("server_not_support", "Serwer nie wspiera"),
|
||||||
|
("Not available", "Niedostępne"),
|
||||||
|
("Too frequent", "Zbyt często"),
|
||||||
|
("Cancel", "Anuluj"),
|
||||||
|
("Skip", "Pomiń"),
|
||||||
|
("Close", "Zamknij"),
|
||||||
|
("Retry", "Ponów"),
|
||||||
|
("OK", "OK"),
|
||||||
|
("Password Required", "Wymagane hasło"),
|
||||||
|
("Please enter your password", "Wpisz proszę twoje hasło"),
|
||||||
|
("Remember password", "Zapamiętaj hasło"),
|
||||||
|
("Wrong Password", "Błędne hasło"),
|
||||||
|
("Do you want to enter again?", "Chcesz wprowadzić ponownie?"),
|
||||||
|
("Connection Error", "Błąd połączenia"),
|
||||||
|
("Error", "Błąd"),
|
||||||
|
("Reset by the peer", "Zresetuj przez peer"),
|
||||||
|
("Connecting...", "Łączenie..."),
|
||||||
|
("Connection in progress. Please wait.", "Trwa łączenie. Proszę czekać."),
|
||||||
|
("Please try 1 minute later", "Spróbuj za minutę"),
|
||||||
|
("Login Error", "Błąd logowania"),
|
||||||
|
("Successful", "Sukces"),
|
||||||
|
("Connected, waiting for image...", "Połączono, czekam na obraz..."),
|
||||||
|
("Name", "Nazwa"),
|
||||||
|
("Type", "Typ"),
|
||||||
|
("Modified", "Zmodyfikowany"),
|
||||||
|
("Size", "Rozmiar"),
|
||||||
|
("Show Hidden Files", "Pokaż ukryte pliki"),
|
||||||
|
("Receive", "Odbierz"),
|
||||||
|
("Send", "Wyślij"),
|
||||||
|
("Refresh File", "Odśwież plik"),
|
||||||
|
("Local", "Lokalny"),
|
||||||
|
("Remote", "Zdalny"),
|
||||||
|
("Remote Computer", "Zdalny komputer"),
|
||||||
|
("Local Computer", "Lokalny komputer"),
|
||||||
|
("Confirm Delete", "Potwierdź usunięcie"),
|
||||||
|
("Delete", "Usuń"),
|
||||||
|
("Properties", "Właściwości"),
|
||||||
|
("Multi Select", "Wielokrotny wybór"),
|
||||||
|
("Empty Directory", "Pusty katalog"),
|
||||||
|
("Not an empty directory", "Katalog nie jest pusty"),
|
||||||
|
("Are you sure you want to delete this file?", "Czy na pewno chcesz usunąć ten plik?"),
|
||||||
|
("Are you sure you want to delete this empty directory?", ""),
|
||||||
|
("Are you sure you want to delete the file of this directory?", "Czy na pewno chcesz usunąć ten pusty katalog?"),
|
||||||
|
("Do this for all conflicts", "Zrób to dla wszystkich konfliktów"),
|
||||||
|
("This is irreversible!", "To jest nieodwracalne!"),
|
||||||
|
("Deleting", "Usuwanie"),
|
||||||
|
("files", "pliki"),
|
||||||
|
("Waiting", "Czekanie"),
|
||||||
|
("Finished", "Zakończono"),
|
||||||
|
("Speed", "Prędkość"),
|
||||||
|
("Custom Image Quality", "Domyślna jakość obrazu"),
|
||||||
|
("Privacy mode", "Tryb prywatny"),
|
||||||
|
("Block user input", "Blokuj wprowadzanie"),
|
||||||
|
("Unblock user input", "Odblokuj wprowadzanie"),
|
||||||
|
("Adjust Window", "Dostosuj okno"),
|
||||||
|
("Original", "Oryginał"),
|
||||||
|
("Shrink", "Zmniejsz"),
|
||||||
|
("Stretch", "Zwiększ"),
|
||||||
|
("Good image quality", "Dobra jakość obrazu"),
|
||||||
|
("Balanced", "Zrównoważony"),
|
||||||
|
("Optimize reaction time", "Zoptymalizuj czas reakcji"),
|
||||||
|
("Custom", "Niestandardowy"),
|
||||||
|
("Show remote cursor", "Pokazuj zdalny kursor"),
|
||||||
|
("Show quality monitor", "Pokazuj jakość monitora"),
|
||||||
|
("Disable clipboard", "Wyłącz schowek"),
|
||||||
|
("Lock after session end", "Zablokuj po zakończeniu sesji"),
|
||||||
|
("Insert", "Wstaw"),
|
||||||
|
("Insert Lock", "Wstaw blokadę"),
|
||||||
|
("Refresh", "Odśwież"),
|
||||||
|
("ID does not exist", "ID nie istnieje"),
|
||||||
|
("Failed to connect to rendezvous server", "Nie udało się połączyć z serwerem spotkania"),
|
||||||
|
("Please try later", "Spróbuj później"),
|
||||||
|
("Remote desktop is offline", "Zdalny pulpit jest off-line"),
|
||||||
|
("Key mismatch", "Niezgodność klucza"),
|
||||||
|
("Timeout", "Przekroczenie czasu"),
|
||||||
|
("Failed to connect to relay server", "Nie udało się połączyć z serwerem pośredniczącym"),
|
||||||
|
("Failed to connect via rendezvous server", "Nie udało się połączyć przez serwer spotkań"),
|
||||||
|
("Failed to connect via relay server", "Nie udało się połączyć przez serwer pośredniczący"),
|
||||||
|
("Failed to make direct connection to remote desktop", "Nie udało się nawiązać bezpośredniego połączenia ze zdalnym pulpitem"),
|
||||||
|
("Set Password", "Ustaw hasło"),
|
||||||
|
("OS Password", "Hasło systemu operacyjnego"),
|
||||||
|
("install_tip", "Podpowiedź do instalacji"),
|
||||||
|
("Click to upgrade", "Kliknij, aby uaktualnić (upgrade)"),
|
||||||
|
("Click to download", "Kliknij, aby pobrać"),
|
||||||
|
("Click to update", "Kliknij, aby zaktualizować (update)"),
|
||||||
|
("Configure", "Konfiguruj"),
|
||||||
|
("config_acc", ""),
|
||||||
|
("config_screen", "Konfiguracja konta"),
|
||||||
|
("Installing ...", "Instalowanie..."),
|
||||||
|
("Install", "Instaluj"),
|
||||||
|
("Installation", "Instalacja"),
|
||||||
|
("Installation Path", "Ścieżka instalacji"),
|
||||||
|
("Create start menu shortcuts", "Utwórz skrót w menu"),
|
||||||
|
("Create desktop icon", "Utwórz ikonę na pulpicie"),
|
||||||
|
("agreement_tip", "Podpowiedź do umowy/zgody"),
|
||||||
|
("Accept and Install", "Akceptuj i instaluj"),
|
||||||
|
("End-user license agreement", "Umowa licencyjna użytkownika końcowego"),
|
||||||
|
("Generating ...", "Generowanie..."),
|
||||||
|
("Your installation is lower version.", "Twoja instalacja to niższa wersja"),
|
||||||
|
("not_close_tcp_tip", "Not close TCP tip"),
|
||||||
|
("Listening ...", "Słucham..."),
|
||||||
|
("Remote Host", "Zdalny host"),
|
||||||
|
("Remote Port", "Zdalny port"),
|
||||||
|
("Action", "Akcja"),
|
||||||
|
("Add", "Dodaj"),
|
||||||
|
("Local Port", "Lokalny port"),
|
||||||
|
("setup_server_tip", "Podpowiedź do ustawień serwera"),
|
||||||
|
("Too short, at least 6 characters.", "Za krótkie, min. 6 znaków"),
|
||||||
|
("The confirmation is not identical.", "Potwierdzenie nie jest identyczne."),
|
||||||
|
("Permissions", "Uprawnienia"),
|
||||||
|
("Accept", "Akceptuj"),
|
||||||
|
("Dismiss", "Odrzuć"),
|
||||||
|
("Disconnect", "Rozłącz"),
|
||||||
|
("Allow using keyboard and mouse", "Zezwalaj na używanie klawiatury i myszy"),
|
||||||
|
("Allow using clipboard", "Zezwalaj na używanie schowka"),
|
||||||
|
("Allow hearing sound", "Pozwól słyszeć dźwięk"),
|
||||||
|
("Allow file copy and paste", "Zezwalaj na kopiowanie i wklejanie plików"),
|
||||||
|
("Connected", "Połączony"),
|
||||||
|
("Direct and encrypted connection", "Bezpośrednie i szyfrowane połączenie"),
|
||||||
|
("Relayed and encrypted connection", "Połączenie przekazywane i szyfrowane"),
|
||||||
|
("Direct and unencrypted connection", "Połączenie bezpośrednie i nieszyfrowane"),
|
||||||
|
("Relayed and unencrypted connection", "Połączenie przekazywane i nieszyfrowane"),
|
||||||
|
("Enter Remote ID", "Wprowadź zdalne ID"),
|
||||||
|
("Enter your password", "Wprowadź hasło"),
|
||||||
|
("Logging in...", "Loguję się ..."),
|
||||||
|
("Enable RDP session sharing", "Włącz udostępnianie sesji RDP"),
|
||||||
|
("Auto Login", "Automatyczne logowanie"),
|
||||||
|
("Enable Direct IP Access", "Włącz bezpośredni dostęp IP"),
|
||||||
|
("Rename", "Zmień nazwę"),
|
||||||
|
("Space", "Przestrzeń"),
|
||||||
|
("Create Desktop Shortcut", "Utwórz skrót na pulpicie"),
|
||||||
|
("Change Path", "Zmień ścieżkę"),
|
||||||
|
("Create Folder", "Utwórz folder"),
|
||||||
|
("Please enter the folder name", "Wpisz nazwę folderu"),
|
||||||
|
("Fix it", "Napraw to"),
|
||||||
|
("Warning", "Ostrzeżenie"),
|
||||||
|
("Login screen using Wayland is not supported", "Ekran logowania przy użyciu Wayland nie jest obsługiwany"),
|
||||||
|
("Reboot required", "Wymagany restart"),
|
||||||
|
("Unsupported display server ", "Nieobsługiwany serwer wyświetlania "),
|
||||||
|
("x11 expected", "oczekiwane X11"),
|
||||||
|
("Port", "Port"),
|
||||||
|
("Settings", "Ustawienia"),
|
||||||
|
("Username", "Nazwa użytkownika"),
|
||||||
|
("Invalid port", "Nieprawidłowy port"),
|
||||||
|
("Closed manually by the peer", "Zamykany ręcznie przez peer"),
|
||||||
|
("Enable remote configuration modification", "Włącz zdalną modyfikację konfiguracji"),
|
||||||
|
("Run without install", "Uruchom bez instalacji"),
|
||||||
|
("Always connected via relay", "Zawsze połączony pośrednio"),
|
||||||
|
("Always connect via relay", "Zawsze łącz pośrednio"),
|
||||||
|
("whitelist_tip", "Podpowiedź do białej listy"),
|
||||||
|
("Login", "Zaloguj"),
|
||||||
|
("Logout", "Wyloguj"),
|
||||||
|
("Tags", "Tagi"),
|
||||||
|
("Search ID", "Szukaj ID"),
|
||||||
|
("Current Wayland display server is not supported", "Obecny serwer wyświetlania Wayland nie jest obsługiwany"),
|
||||||
|
("whitelist_sep", "whitelist_sep"),
|
||||||
|
("Add ID", "Dodaj ID"),
|
||||||
|
("Add Tag", "Dodaj Tag"),
|
||||||
|
("Unselect all tags", "Odznacz wszystkie tagi"),
|
||||||
|
("Network error", "Błąd sieci"),
|
||||||
|
("Username missed", "Brak użytkownika"),
|
||||||
|
("Password missed", "Brak hasła"),
|
||||||
|
("Wrong credentials", "Błędne dane uwierzytelniające"),
|
||||||
|
("Edit Tag", "Edytuj tag"),
|
||||||
|
("Unremember Password", "Zapomnij hasło"),
|
||||||
|
("Favorites", "Ulubione"),
|
||||||
|
("Add to Favorites", "Dodaj do ulubionych"),
|
||||||
|
("Remove from Favorites", "Usuń z ulubionych"),
|
||||||
|
("Empty", "Pusty"),
|
||||||
|
("Invalid folder name", "Błędna nazwa folderu"),
|
||||||
|
("Socks5 Proxy", "Socks5 Proxy"),
|
||||||
|
("Hostname", "Nazwa hosta"),
|
||||||
|
("Discovered", "Discovered"),
|
||||||
|
("install_daemon_tip", "Podpowiedź instalacji daemona"),
|
||||||
|
("Remote ID", "Zdalne ID"),
|
||||||
|
("Paste", "Wklej"),
|
||||||
|
("Paste here?", "Wkleić tu?"),
|
||||||
|
("Are you sure to close the connection?", "Czy na pewno chcesz zamknąć połączenie?"),
|
||||||
|
("Download new version", "Pobierz nową wersję"),
|
||||||
|
("Touch mode", "Tryb dotykowy"),
|
||||||
|
("Mouse mode", "Tryb myszy"),
|
||||||
|
("One-Finger Tap", "Dotknij jednym palcem"),
|
||||||
|
("Left Mouse", "Mysz dla leworęcznych"),
|
||||||
|
("One-Long Tap", "Przytrzymaj jednym palcem"),
|
||||||
|
("Two-Finger Tap", "Dotknij dwoma palcami"),
|
||||||
|
("Right Mouse", "Mysz dla praworęcznych"),
|
||||||
|
("One-Finger Move", "Ruch jednym palcem"),
|
||||||
|
("Double Tap & Move", "Dotknij dwukrotnie i przesuń"),
|
||||||
|
("Mouse Drag", "Przeciągnij myszą"),
|
||||||
|
("Three-Finger vertically", "Trzy palce wertykalnie"),
|
||||||
|
("Mouse Wheel", "Kółko myszy"),
|
||||||
|
("Two-Finger Move", "Ruch dwoma palcami"),
|
||||||
|
("Canvas Move", "Canvas Move"),
|
||||||
|
("Pinch to Zoom", "Uszczypnij, aby powiększyć"),
|
||||||
|
("Canvas Zoom", "Canvas Zoom"),
|
||||||
|
("Reset canvas", "Reset canvas"),
|
||||||
|
("No permission of file transfer", "Brak uprawnień na przesyłanie plików"),
|
||||||
|
("Note", "Notatka"),
|
||||||
|
("Connection", "Połączenie"),
|
||||||
|
("Share Screen", "Udostępnij ekran"),
|
||||||
|
("CLOSE", "Zamknij"),
|
||||||
|
("OPEN", "Otwórz"),
|
||||||
|
("Chat", "Czat"),
|
||||||
|
("Total", "Total"),
|
||||||
|
("items", "przedmioty"),
|
||||||
|
("Selected", "Zaznaczone"),
|
||||||
|
("Screen Capture", "Przechwyć ekran"),
|
||||||
|
("Input Control", "Kontrola wejścia"),
|
||||||
|
("Audio Capture", "Przechwyć dźwięk"),
|
||||||
|
("File Connection", "File Connection"),
|
||||||
|
("Screen Connection", "Screen Connection"),
|
||||||
|
("Do you accept?", "Akceptujesz?"),
|
||||||
|
("Open System Setting", "Otwórz ustawienia systemowe"),
|
||||||
|
("How to get Android input permission?", "Jak uzyskać uprawnienia do wprowadzania danych w systemie Android?"),
|
||||||
|
("android_input_permission_tip1", "android_input_permission_tip1"),
|
||||||
|
("android_input_permission_tip2", "android_input_permission_tip2"),
|
||||||
|
("android_new_connection_tip", "android_new_connection_tip"),
|
||||||
|
("android_service_will_start_tip", "android_service_will_start_tip"),
|
||||||
|
("android_stop_service_tip", "android_stop_service_tip"),
|
||||||
|
("android_version_audio_tip", "android_version_audio_tip"),
|
||||||
|
("android_start_service_tip", "android_start_service_tip"),
|
||||||
|
("Account", "Konto"),
|
||||||
|
("Overwrite", "Nadpisz"),
|
||||||
|
("This file exists, skip or overwrite this file?", "Ten plik istnieje, pominąć czy nadpisać ten plik?"),
|
||||||
|
("Quit", "Zrezygnuj"),
|
||||||
|
("doc_mac_permission", "doc_mac_permission"),
|
||||||
|
("Help", "Pomoc"),
|
||||||
|
("Failed", "Niepowodzenie"),
|
||||||
|
("Succeeded", "Udało się"),
|
||||||
|
("Someone turns on privacy mode, exit", "Ktoś włącza tryb prywatności, wyjdź"),
|
||||||
|
("Unsupported", "NIewspierane"),
|
||||||
|
("Peer denied", "Odmowa peer"),
|
||||||
|
("Please install plugins", "Zainstaluj plugin"),
|
||||||
|
("Peer exit", "Wyjście peer"),
|
||||||
|
("Failed to turn off", "Nie udało się wyłączyć"),
|
||||||
|
("Turned off", "Wyłączony"),
|
||||||
|
("In privacy mode", "Wejdź w tryb prywatności"),
|
||||||
|
("Out privacy mode", "Opuść tryb prywatności"),
|
||||||
|
("Language", "Język"),
|
||||||
|
("Keep RustDesk background service", "Zachowaj usługę w tle RustDesk"),
|
||||||
|
("Ignore Battery Optimizations", "Ignoruj optymalizację baterii"),
|
||||||
|
("android_open_battery_optimizations_tip", "android_open_battery_optimizations_tip"),
|
||||||
|
("Random Password After Session", "Losowe hasło po sesji"),
|
||||||
|
("Keep", "Zachowaj"),
|
||||||
|
("Update", "Aktualizacja"),
|
||||||
|
("Disable", "Wyłącz"),
|
||||||
|
("Onetime Password", "Hasło jednorazowe"),
|
||||||
|
("Verification Method", "Metoda weryfikacji"),
|
||||||
|
("Enable security password", "Włącz hasło zabezpieczające"),
|
||||||
|
("Enable random password", "Włącz losowe hasło"),
|
||||||
|
("Enable onetime password", "Włącz hasło jednorazowe"),
|
||||||
|
("Disable onetime password", "Wyłącz hasło jednorazowe"),
|
||||||
|
("Activate onetime password", "Aktywuj hasło jednorazowe"),
|
||||||
|
("Set security password", "Ustaw hasło zabezpieczające"),
|
||||||
|
("Connection not allowed", "Połączenie niedozwolone"),
|
||||||
|
].iter().cloned().collect();
|
||||||
|
}
|
@ -287,20 +287,19 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Keep RustDesk background service", ""),
|
("Keep RustDesk background service", ""),
|
||||||
("Ignore Battery Optimizations", ""),
|
("Ignore Battery Optimizations", ""),
|
||||||
("android_open_battery_optimizations_tip", ""),
|
("android_open_battery_optimizations_tip", ""),
|
||||||
("Random Password After Session", ""),
|
|
||||||
("Keep", ""),
|
|
||||||
("Update", ""),
|
|
||||||
("Disable", ""),
|
|
||||||
("Onetime Password", ""),
|
|
||||||
("Verification Method", ""),
|
|
||||||
("Enable security password", ""),
|
|
||||||
("Enable random password", ""),
|
|
||||||
("Enable onetime password", ""),
|
|
||||||
("Disable onetime password", ""),
|
|
||||||
("Activate onetime password", ""),
|
|
||||||
("Set security password", ""),
|
|
||||||
("Connection not allowed", ""),
|
("Connection not allowed", ""),
|
||||||
("Legacy mode", ""),
|
("Legacy mode", ""),
|
||||||
("Map mode", ""),
|
("Map mode", ""),
|
||||||
|
("Use temporary password", ""),
|
||||||
|
("Use permanent password", ""),
|
||||||
|
("Use both passwords", ""),
|
||||||
|
("Set permanent password", ""),
|
||||||
|
("Set temporary password length", ""),
|
||||||
|
("Enable Remote Restart", ""),
|
||||||
|
("Allow remote restart", ""),
|
||||||
|
("Restart Remote Device", ""),
|
||||||
|
("Are you sure you want to restart", ""),
|
||||||
|
("Restarting Remote Device", ""),
|
||||||
|
("remote_restarting_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -35,9 +35,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("About", "О RustDesk"),
|
("About", "О RustDesk"),
|
||||||
("Mute", "Отключить звук"),
|
("Mute", "Отключить звук"),
|
||||||
("Audio Input", "Аудиовход"),
|
("Audio Input", "Аудиовход"),
|
||||||
("Enhancements", ""),
|
("Enhancements", "Улучшения"),
|
||||||
("Hardware Codec", ""),
|
("Hardware Codec", "Аппаратный кодек"),
|
||||||
("Adaptive Bitrate", ""),
|
("Adaptive Bitrate", "Адаптивная скорость потока"),
|
||||||
("ID Server", "ID-сервер"),
|
("ID Server", "ID-сервер"),
|
||||||
("Relay Server", "Сервер ретрансляции"),
|
("Relay Server", "Сервер ретрансляции"),
|
||||||
("API Server", "API-сервер"),
|
("API Server", "API-сервер"),
|
||||||
@ -139,7 +139,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Installation Path", "Путь установки"),
|
("Installation Path", "Путь установки"),
|
||||||
("Create start menu shortcuts", "Создать ярлыки меню \"Пуск\""),
|
("Create start menu shortcuts", "Создать ярлыки меню \"Пуск\""),
|
||||||
("Create desktop icon", "Создать значок на рабочем столе"),
|
("Create desktop icon", "Создать значок на рабочем столе"),
|
||||||
("agreement_tip", "Если вы начнете установку, примите лицензионное соглашение"),
|
("agreement_tip", "Начиная установку, вы принимаете условия лицензионного соглашения"),
|
||||||
("Accept and Install", "Принять и установить"),
|
("Accept and Install", "Принять и установить"),
|
||||||
("End-user license agreement", "Лицензионное соглашение с конечным пользователем"),
|
("End-user license agreement", "Лицензионное соглашение с конечным пользователем"),
|
||||||
("Generating ...", "Генерация..."),
|
("Generating ...", "Генерация..."),
|
||||||
@ -274,11 +274,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Help", "Помощь"),
|
("Help", "Помощь"),
|
||||||
("Failed", "Не удалось"),
|
("Failed", "Не удалось"),
|
||||||
("Succeeded", "Успешно"),
|
("Succeeded", "Успешно"),
|
||||||
("Someone turns on privacy mode, exit", "Кто-то включает режим конфиденциальности, выйдите"),
|
("Someone turns on privacy mode, exit", "Кто-то включает режим конфиденциальности, выход"),
|
||||||
("Unsupported", "Не поддерживается"),
|
("Unsupported", "Не поддерживается"),
|
||||||
("Peer denied", "Отказано в пире"),
|
("Peer denied", "Отклонено удалённым компьютером"),
|
||||||
("Please install plugins", "Пожалуйста, установите плагины"),
|
("Please install plugins", "Пожалуйста, установите плагины"),
|
||||||
("Peer exit", "Одноранговый выход"),
|
("Peer exit", "Отключен удалённым компьютером"),
|
||||||
("Failed to turn off", "Не удалось выключить"),
|
("Failed to turn off", "Не удалось выключить"),
|
||||||
("Turned off", "Выключен"),
|
("Turned off", "Выключен"),
|
||||||
("In privacy mode", "В режиме конфиденциальности"),
|
("In privacy mode", "В режиме конфиденциальности"),
|
||||||
@ -287,20 +287,19 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Keep RustDesk background service", "Сохранить фоновый службу RustDesk"),
|
("Keep RustDesk background service", "Сохранить фоновый службу RustDesk"),
|
||||||
("Ignore Battery Optimizations", "Игнорировать оптимизацию батареи"),
|
("Ignore Battery Optimizations", "Игнорировать оптимизацию батареи"),
|
||||||
("android_open_battery_optimizations_tip", "Перейдите на следующую страницу настроек "),
|
("android_open_battery_optimizations_tip", "Перейдите на следующую страницу настроек "),
|
||||||
("Random Password After Session", "Случайный пароль после сеанса"),
|
|
||||||
("Keep", "Оставить"),
|
|
||||||
("Update", "Обновить"),
|
|
||||||
("Disable", "Отключить"),
|
|
||||||
("Onetime Password", "Одноразовый пароль"),
|
|
||||||
("Verification Method", "Метод верификации"),
|
|
||||||
("Enable security password", "Включить пароль безопасности"),
|
|
||||||
("Enable random password", "Включить случайный пароль"),
|
|
||||||
("Enable onetime password", "Включить одноразовый пароль"),
|
|
||||||
("Disable onetime password", "Отключить одноразовый пароль"),
|
|
||||||
("Activate onetime password", "Активировать одноразовый пароль"),
|
|
||||||
("Set security password", "Задать пароль безопасности"),
|
|
||||||
("Connection not allowed", "Подключение не разрешено"),
|
("Connection not allowed", "Подключение не разрешено"),
|
||||||
("Legacy mode", ""),
|
("Legacy mode", ""),
|
||||||
("Map mode", ""),
|
("Map mode", ""),
|
||||||
|
("Use temporary password", "Использовать временный пароль"),
|
||||||
|
("Use permanent password", "Использовать постоянный пароль"),
|
||||||
|
("Use both passwords", "Использовать оба пароля"),
|
||||||
|
("Set permanent password", "Установить постоянный пароль"),
|
||||||
|
("Set temporary password length", "Длина временного пароля"),
|
||||||
|
("Enable Remote Restart", "Включить удаленный перезапуск"),
|
||||||
|
("Allow remote restart", "Разрешить удаленный перезапуск"),
|
||||||
|
("Restart Remote Device", "Перезапустить удаленное устройство"),
|
||||||
|
("Are you sure you want to restart", "Вы уверены, что хотите выполнить перезапуск?"),
|
||||||
|
("Restarting Remote Device", "Перезагрузка удаленного устройства"),
|
||||||
|
("remote_restarting_tip", "Удаленное устройство перезапускается. Пожалуйста, закройте это сообщение и через некоторое время переподключитесь, используя постоянный пароль."),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -287,20 +287,19 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Keep RustDesk background service", ""),
|
("Keep RustDesk background service", ""),
|
||||||
("Ignore Battery Optimizations", ""),
|
("Ignore Battery Optimizations", ""),
|
||||||
("android_open_battery_optimizations_tip", ""),
|
("android_open_battery_optimizations_tip", ""),
|
||||||
("Random Password After Session", ""),
|
|
||||||
("Keep", ""),
|
|
||||||
("Update", ""),
|
|
||||||
("Disable", ""),
|
|
||||||
("Onetime Password", ""),
|
|
||||||
("Verification Method", ""),
|
|
||||||
("Enable security password", ""),
|
|
||||||
("Enable random password", ""),
|
|
||||||
("Enable onetime password", ""),
|
|
||||||
("Disable onetime password", ""),
|
|
||||||
("Activate onetime password", ""),
|
|
||||||
("Set security password", ""),
|
|
||||||
("Connection not allowed", ""),
|
("Connection not allowed", ""),
|
||||||
("Legacy mode", ""),
|
("Legacy mode", ""),
|
||||||
("Map mode", ""),
|
("Map mode", ""),
|
||||||
|
("Use temporary password", ""),
|
||||||
|
("Use permanent password", ""),
|
||||||
|
("Use both passwords", ""),
|
||||||
|
("Set permanent password", ""),
|
||||||
|
("Set temporary password length", ""),
|
||||||
|
("Enable Remote Restart", ""),
|
||||||
|
("Allow remote restart", ""),
|
||||||
|
("Restart Remote Device", ""),
|
||||||
|
("Are you sure you want to restart", ""),
|
||||||
|
("Restarting Remote Device", ""),
|
||||||
|
("remote_restarting_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -287,20 +287,19 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Keep RustDesk background service", ""),
|
("Keep RustDesk background service", ""),
|
||||||
("Ignore Battery Optimizations", ""),
|
("Ignore Battery Optimizations", ""),
|
||||||
("android_open_battery_optimizations_tip", ""),
|
("android_open_battery_optimizations_tip", ""),
|
||||||
("Random Password After Session", ""),
|
|
||||||
("Keep", ""),
|
|
||||||
("Update", ""),
|
|
||||||
("Disable", ""),
|
|
||||||
("Onetime Password", ""),
|
|
||||||
("Verification Method", ""),
|
|
||||||
("Enable security password", ""),
|
|
||||||
("Enable random password", ""),
|
|
||||||
("Enable onetime password", ""),
|
|
||||||
("Disable onetime password", ""),
|
|
||||||
("Activate onetime password", ""),
|
|
||||||
("Set security password", ""),
|
|
||||||
("Connection not allowed", ""),
|
("Connection not allowed", ""),
|
||||||
("Legacy mode", ""),
|
("Legacy mode", ""),
|
||||||
("Map mode", ""),
|
("Map mode", ""),
|
||||||
|
("Use temporary password", ""),
|
||||||
|
("Use permanent password", ""),
|
||||||
|
("Use both passwords", ""),
|
||||||
|
("Set permanent password", ""),
|
||||||
|
("Set temporary password length", ""),
|
||||||
|
("Enable Remote Restart", ""),
|
||||||
|
("Allow remote restart", ""),
|
||||||
|
("Restart Remote Device", ""),
|
||||||
|
("Are you sure you want to restart", ""),
|
||||||
|
("Restarting Remote Device", ""),
|
||||||
|
("remote_restarting_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -11,12 +11,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Enable Service", "Servisi aktif et"),
|
("Enable Service", "Servisi aktif et"),
|
||||||
("Start Service", "Servisi başlat"),
|
("Start Service", "Servisi başlat"),
|
||||||
("Service is running", "Servis çalışıyor"),
|
("Service is running", "Servis çalışıyor"),
|
||||||
("Service is not running", "Servis durduruldu"),
|
("Service is not running", "Servis çalışmıyor"),
|
||||||
("not_ready_status", "Hazır değil. Bağlantınızı kontrol edin"),
|
("not_ready_status", "Hazır değil. Bağlantınızı kontrol edin"),
|
||||||
("Control Remote Desktop", "Bağlanılacak Uzak Bağlantı ID"),
|
("Control Remote Desktop", "Bağlanılacak Uzak Bağlantı ID"),
|
||||||
("Transfer File", "Dosya transferi"),
|
("Transfer File", "Dosya transferi"),
|
||||||
("Connect", "Bağlan"),
|
("Connect", "Bağlan"),
|
||||||
("Recent Sessions", "Sıklıkla Bağlanılanlar"),
|
("Recent Sessions", "Son Bağlanılanlar"),
|
||||||
("Address Book", "Adres Defteri"),
|
("Address Book", "Adres Defteri"),
|
||||||
("Confirmation", "Onayla"),
|
("Confirmation", "Onayla"),
|
||||||
("TCP Tunneling", "TCP Tünelleri"),
|
("TCP Tunneling", "TCP Tünelleri"),
|
||||||
@ -33,7 +33,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Change ID", "ID Değiştir"),
|
("Change ID", "ID Değiştir"),
|
||||||
("Website", "Website"),
|
("Website", "Website"),
|
||||||
("About", "Hakkında"),
|
("About", "Hakkında"),
|
||||||
("Mute", "Sesi Kapat"),
|
("Mute", "Sustur"),
|
||||||
("Audio Input", "Ses Girişi"),
|
("Audio Input", "Ses Girişi"),
|
||||||
("Enhancements", ""),
|
("Enhancements", ""),
|
||||||
("Hardware Codec", ""),
|
("Hardware Codec", ""),
|
||||||
@ -101,7 +101,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Unblock user input", "Kullanı girişine izin ver"),
|
("Unblock user input", "Kullanı girişine izin ver"),
|
||||||
("Adjust Window", "Pencereyi Ayarla"),
|
("Adjust Window", "Pencereyi Ayarla"),
|
||||||
("Original", "Orjinal"),
|
("Original", "Orjinal"),
|
||||||
("Shrink", "Sığdır"),
|
("Shrink", "Küçült"),
|
||||||
("Stretch", "Uzat"),
|
("Stretch", "Uzat"),
|
||||||
("Good image quality", "İyi görüntü kalitesi"),
|
("Good image quality", "İyi görüntü kalitesi"),
|
||||||
("Balanced", "Dengelenmiş"),
|
("Balanced", "Dengelenmiş"),
|
||||||
@ -114,7 +114,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Insert", "Ekle"),
|
("Insert", "Ekle"),
|
||||||
("Insert Lock", "Kilit Ekle"),
|
("Insert Lock", "Kilit Ekle"),
|
||||||
("Refresh", "Yenile"),
|
("Refresh", "Yenile"),
|
||||||
("ID does not exist", "ID hatalı"),
|
("ID does not exist", "ID bulunamadı"),
|
||||||
("Failed to connect to rendezvous server", "ID oluşturma sunucusuna bağlanılamadı"),
|
("Failed to connect to rendezvous server", "ID oluşturma sunucusuna bağlanılamadı"),
|
||||||
("Please try later", "Dağa sonra tekrar deneyiniz"),
|
("Please try later", "Dağa sonra tekrar deneyiniz"),
|
||||||
("Remote desktop is offline", "Uzak masaüstü kapalı"),
|
("Remote desktop is offline", "Uzak masaüstü kapalı"),
|
||||||
@ -188,7 +188,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Port", "Port"),
|
("Port", "Port"),
|
||||||
("Settings", "Ayarlar"),
|
("Settings", "Ayarlar"),
|
||||||
("Username", "Kullanıcı Adı"),
|
("Username", "Kullanıcı Adı"),
|
||||||
("Invalid port", "Geçersiz bağlantı noktası"),
|
("Invalid port", "Geçersiz port"),
|
||||||
("Closed manually by the peer", "Eş tarafından manuel olarak kapatıldı"),
|
("Closed manually by the peer", "Eş tarafından manuel olarak kapatıldı"),
|
||||||
("Enable remote configuration modification", "Uzaktan yapılandırma değişikliğini etkinleştir"),
|
("Enable remote configuration modification", "Uzaktan yapılandırma değişikliğini etkinleştir"),
|
||||||
("Run without install", "Yüklemeden çalıştır"),
|
("Run without install", "Yüklemeden çalıştır"),
|
||||||
@ -204,7 +204,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Add ID", "ID Ekle"),
|
("Add ID", "ID Ekle"),
|
||||||
("Add Tag", "Etiket Ekle"),
|
("Add Tag", "Etiket Ekle"),
|
||||||
("Unselect all tags", "Tüm etiketlerin seçimini kaldır"),
|
("Unselect all tags", "Tüm etiketlerin seçimini kaldır"),
|
||||||
("Network error", "Network error"),
|
("Network error", "Bağlantı hatası"),
|
||||||
("Username missed", "Kullanıcı adı boş"),
|
("Username missed", "Kullanıcı adı boş"),
|
||||||
("Password missed", "Şifre boş"),
|
("Password missed", "Şifre boş"),
|
||||||
("Wrong credentials", "Yanlış kimlik bilgileri"),
|
("Wrong credentials", "Yanlış kimlik bilgileri"),
|
||||||
@ -274,16 +274,16 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Help", "Yardım"),
|
("Help", "Yardım"),
|
||||||
("Failed", "Arızalı"),
|
("Failed", "Arızalı"),
|
||||||
("Succeeded", "başarılı"),
|
("Succeeded", "başarılı"),
|
||||||
("Someone turns on privacy mode, exit", "Birisi gizlilik modunu açar, çık"),
|
("Someone turns on privacy mode, exit", "Birisi gizlilik modunu açarsa, çık"),
|
||||||
("Unsupported", "desteklenmiyor"),
|
("Unsupported", "desteklenmiyor"),
|
||||||
("Peer denied", "akran reddedildi"),
|
("Peer denied", "eş reddedildi"),
|
||||||
("Please install plugins", "Lütfen eklentileri yükleyin"),
|
("Please install plugins", "Lütfen eklentileri yükleyin"),
|
||||||
("Peer exit", "akran çıkışı"),
|
("Peer exit", "eş çıkışı"),
|
||||||
("Failed to turn off", "kapatılamadı"),
|
("Failed to turn off", "kapatılamadı"),
|
||||||
("Turned off", "Kapalı"),
|
("Turned off", "Kapatıldı"),
|
||||||
("In privacy mode", "Gizlilik modunda"),
|
("In privacy mode", "Gizlilik modunda"),
|
||||||
("Out privacy mode", "Gizlilik modu dışında"),
|
("Out privacy mode", "Gizlilik modu dışında"),
|
||||||
("Language", ""),
|
("Language", "Dil"),
|
||||||
("Keep RustDesk background service", ""),
|
("Keep RustDesk background service", ""),
|
||||||
("Ignore Battery Optimizations", ""),
|
("Ignore Battery Optimizations", ""),
|
||||||
("android_open_battery_optimizations_tip", ""),
|
("android_open_battery_optimizations_tip", ""),
|
||||||
@ -302,5 +302,17 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Connection not allowed", ""),
|
("Connection not allowed", ""),
|
||||||
("Legacy mode", ""),
|
("Legacy mode", ""),
|
||||||
("Map mode", ""),
|
("Map mode", ""),
|
||||||
|
("Connection not allowed", "bağlantıya izin verilmedi"),
|
||||||
|
("Use temporary password", "Geçici şifre kullan"),
|
||||||
|
("Use permanent password", "Kalıcı şifre kullan"),
|
||||||
|
("Use both passwords", "İki şifreyide kullan"),
|
||||||
|
("Set permanent password", "Kalıcı şifre oluştur"),
|
||||||
|
("Set temporary password length", ""),
|
||||||
|
("Enable Remote Restart", "Uzaktan yeniden başlatmayı aktif et"),
|
||||||
|
("Allow remote restart", "Uzaktan yeniden başlatmaya izin ver"),
|
||||||
|
("Restart Remote Device", "Uzaktaki cihazı yeniden başlat"),
|
||||||
|
("Are you sure you want to restart", "Yeniden başlatmak istediğinize emin misin?"),
|
||||||
|
("Restarting Remote Device", "Uzaktan yeniden başlatılıyor"),
|
||||||
|
("remote_restarting_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -287,20 +287,19 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Keep RustDesk background service", "保持RustDesk後台服務"),
|
("Keep RustDesk background service", "保持RustDesk後台服務"),
|
||||||
("Ignore Battery Optimizations", "忽略電池優化"),
|
("Ignore Battery Optimizations", "忽略電池優化"),
|
||||||
("android_open_battery_optimizations_tip", "如需關閉此功能,請在接下來的RustDesk應用設置頁面中,找到並進入 [電源] 頁面,取消勾選 [不受限制]"),
|
("android_open_battery_optimizations_tip", "如需關閉此功能,請在接下來的RustDesk應用設置頁面中,找到並進入 [電源] 頁面,取消勾選 [不受限制]"),
|
||||||
("Random Password After Session", "會話結束更新隨機密碼"),
|
|
||||||
("Keep", "保持"),
|
|
||||||
("Update", "更新"),
|
|
||||||
("Disable", "禁用"),
|
|
||||||
("Onetime Password", "一次性口令"),
|
|
||||||
("Verification Method", "密碼驗證方式"),
|
|
||||||
("Enable security password", "啟用安全密碼"),
|
|
||||||
("Enable random password", "啟用隨機密碼"),
|
|
||||||
("Enable onetime password", "啟用一次性訪問功能"),
|
|
||||||
("Disable onetime password", "禁用一次性訪問功能"),
|
|
||||||
("Activate onetime password", "激活一次性訪問功能"),
|
|
||||||
("Set security password", "設置安全密碼"),
|
|
||||||
("Connection not allowed", "對方不允許連接"),
|
("Connection not allowed", "對方不允許連接"),
|
||||||
("Legacy mode", "傳統模式"),
|
("Legacy mode", "傳統模式"),
|
||||||
("Map mode", "1:1傳輸"),
|
("Map mode", "1:1傳輸"),
|
||||||
|
("Use temporary password", "使用臨時密碼"),
|
||||||
|
("Use permanent password", "使用固定密碼"),
|
||||||
|
("Use both passwords", "同時使用兩種密碼"),
|
||||||
|
("Set permanent password", "設定固定密碼"),
|
||||||
|
("Set temporary password length", "設定臨時密碼長度"),
|
||||||
|
("Enable Remote Restart", "允許遠程重啓"),
|
||||||
|
("Allow remote restart", "允許遠程重啓"),
|
||||||
|
("Restart Remote Device", "重啓遠程電腦"),
|
||||||
|
("Are you sure you want to restart", "确定要重启"),
|
||||||
|
("Restarting Remote Device", "正在重啓遠程設備"),
|
||||||
|
("remote_restarting_tip", "遠程設備正在重啓,請關閉當前提示框,並在一段時間後使用永久密碼重新連接"),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
303
src/lang/vn.rs
Normal file
303
src/lang/vn.rs
Normal file
@ -0,0 +1,303 @@
|
|||||||
|
lazy_static::lazy_static! {
|
||||||
|
pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||||
|
[
|
||||||
|
("Status", "Trạng thái hiện tại"),
|
||||||
|
("Your Desktop", "Desktop của bạn"),
|
||||||
|
("desk_tip", "Desktop của bạn có thể đuợc truy cập bằng ID và mật khẩu này."),
|
||||||
|
("Password", "Mật khẩu"),
|
||||||
|
("Ready", "Sẵn sàng"),
|
||||||
|
("Established", "Đã đuợc thiết lập"),
|
||||||
|
("connecting_status", "Đang kết nối đến mạng lưới RustDesk..."),
|
||||||
|
("Enable Service", "Bật dịch vụ"),
|
||||||
|
("Start Service", "Bắt đầu dịch vụ"),
|
||||||
|
("Service is running", "Dịch vụ hiện đang chạy"),
|
||||||
|
("Service is not running", "Dịch vụ hiện đang dừng"),
|
||||||
|
("not_ready_status", "Hiện chưa sẵn sàng. Hãy kiểm tra kết nối của bạn"),
|
||||||
|
("Control Remote Desktop", "Điều khiển Desktop Từ Xa"),
|
||||||
|
("Transfer File", "Truyền Tệp Tin"),
|
||||||
|
("Connect", "Kết nối"),
|
||||||
|
("Recent Sessions", "Các session gần đây"),
|
||||||
|
("Address Book", "Quyển địa chỉ"),
|
||||||
|
("Confirmation", "Xác nhận"),
|
||||||
|
("TCP Tunneling", "TCP Tunneling"),
|
||||||
|
("Remove", "Loại bỏ"),
|
||||||
|
("Refresh random password", "Làm mới mật khẩu ngẫu nhiên"),
|
||||||
|
("Set your own password", "Đặt mật khẩu riêng"),
|
||||||
|
("Enable Keyboard/Mouse", "Cho phép sử dụng bàn phím/chuột"),
|
||||||
|
("Enable Clipboard", "Cho phép sử dụng clipboard"),
|
||||||
|
("Enable File Transfer", "Cho phép truyền tệp tin"),
|
||||||
|
("Enable TCP Tunneling", "Cho phép TCP Tunneling"),
|
||||||
|
("IP Whitelisting", "Cho phép IP"),
|
||||||
|
("ID/Relay Server", "Máy chủ ID/Relay"),
|
||||||
|
("Stop service", "Dừng dịch vụ"),
|
||||||
|
("Change ID", "Thay đổi ID"),
|
||||||
|
("Website", "Trang web"),
|
||||||
|
("About", "About"),
|
||||||
|
("Mute", "Tắt tiếng"),
|
||||||
|
("Audio Input", "Đầu vào âm thanh"),
|
||||||
|
("Enhancements", "Các tiện itchs"),
|
||||||
|
("Hardware Codec", "Codec phần cứng"),
|
||||||
|
("Adaptive Bitrate", "Adaptive Bitrate"),
|
||||||
|
("ID Server", "Máy chủ ID"),
|
||||||
|
("Relay Server", "Máy chủ Relay"),
|
||||||
|
("API Server", "Máy chủ API"),
|
||||||
|
("invalid_http", "phải bắt đầu bằng http:// hoặc https://"),
|
||||||
|
("Invalid IP", "IP không hợp lệ"),
|
||||||
|
("id_change_tip", "Các kí tự đuợc phép là: từ a-z, A-Z, 0-9 và _ (dấu gạch dưới). Kí tự đầu tiên phải bắt đầu từ a-z, A-Z. Độ dài kí tự từ 6 đến 16"),
|
||||||
|
("Invalid format", "Định dạng không hợp lệnh"),
|
||||||
|
("server_not_support", "Chưa đuợc hỗ trợ bới server"),
|
||||||
|
("Not available", "Chưa có mặt"),
|
||||||
|
("Too frequent", "Quá thường xuyên"),
|
||||||
|
("Cancel", "Hủy"),
|
||||||
|
("Skip", "Bỏ qua"),
|
||||||
|
("Close", "Đóng"),
|
||||||
|
("Retry", "Thử lại"),
|
||||||
|
("OK", "OK"),
|
||||||
|
("Password Required", "Yêu cầu mật khẩu"),
|
||||||
|
("Please enter your password", "Mời nhập mật khẩu"),
|
||||||
|
("Remember password", "Nhớ mật khẩu"),
|
||||||
|
("Wrong Password", "Sai mật khẩu"),
|
||||||
|
("Do you want to enter again?", "Bạn có muốn nhập lại không"),
|
||||||
|
("Connection Error", "Kết nối bị lỗi"),
|
||||||
|
("Error", "Lỗi"),
|
||||||
|
("Reset by the peer", "Đựoc cài đặt lại với peer"),
|
||||||
|
("Connecting...", "Đang kết nối..."),
|
||||||
|
("Connection in progress. Please wait.", "Đang kết nối. Xin chờ."),
|
||||||
|
("Please try 1 minute later", "Hãy thử lại sau 1 phút"),
|
||||||
|
("Login Error", "Đăng nhập bị lỗi"),
|
||||||
|
("Successful", "Thành công"),
|
||||||
|
("Connected, waiting for image...", "Đã kết nối, đang đợi hình ảnh..."),
|
||||||
|
("Name", "Tên"),
|
||||||
|
("Type", "Loại"),
|
||||||
|
("Modified", "Chỉnh sửa"),
|
||||||
|
("Size", "Kích cỡ"),
|
||||||
|
("Show Hidden Files", "Hiển thị tệp tin bị ẩn"),
|
||||||
|
("Receive", "Nhận"),
|
||||||
|
("Send", "Gửi"),
|
||||||
|
("Refresh File", "Làm mới tệp tin"),
|
||||||
|
("Local", "Cục bộ"),
|
||||||
|
("Remote", "Từ xa"),
|
||||||
|
("Remote Computer", "Máy tính từ xa"),
|
||||||
|
("Local Computer", "Máy tính cục bộ"),
|
||||||
|
("Confirm Delete", "Xác nhận xóa"),
|
||||||
|
("Delete", "Xóa"),
|
||||||
|
("Properties", "Thuộc tính"),
|
||||||
|
("Multi Select", "Chọn nhiều"),
|
||||||
|
("Empty Directory", "Thư mục rỗng"),
|
||||||
|
("Not an empty directory", "Không phải thư mục rỗng"),
|
||||||
|
("Are you sure you want to delete this file?", "Bạn chắc bạn có muốn xóa tệp tin này không?"),
|
||||||
|
("Are you sure you want to delete this empty directory?", "Bạn chắc bạn có muốn xóa thư mục rỗng này không?"),
|
||||||
|
("Are you sure you want to delete the file of this directory?", "Bạn chắc bạn có muốn xóa những tệp tin trong thư mục này không?"),
|
||||||
|
("Do this for all conflicts", "Xác nhận đối với tất cả các trùng lặp"),
|
||||||
|
("This is irreversible!", "Không thể hoàn tác!"),
|
||||||
|
("Deleting", "Đang xóa"),
|
||||||
|
("files", "các tệp tin"),
|
||||||
|
("Waiting", "Đang chờ"),
|
||||||
|
("Finished", "Hoàn thành"),
|
||||||
|
("Speed", "Tốc độ"),
|
||||||
|
("Custom Image Quality", "Chất lượng hình ảnh"),
|
||||||
|
("Privacy mode", "Chế độ riêng tư"),
|
||||||
|
("Block user input", "Chặn các tương tác từ người dùng"),
|
||||||
|
("Unblock user input", "Hủy chặn các tương tác từ người dùng"),
|
||||||
|
("Adjust Window", "Điều chỉnh cửa sổ"),
|
||||||
|
("Original", "Gốc"),
|
||||||
|
("Shrink", "Thu nhỏ"),
|
||||||
|
("Stretch", "Kéo dãn"),
|
||||||
|
("Good image quality", "Chất lượng hình ảnh tốt"),
|
||||||
|
("Balanced", "Cân bằng"),
|
||||||
|
("Optimize reaction time", "Thời gian phản ứng tối ưu"),
|
||||||
|
("Custom", "Custom"),
|
||||||
|
("Show remote cursor", "Hiển thị con trỏ từ máy từ xa"),
|
||||||
|
("Show quality monitor", "Hiện thị chất lượng của màn hình"),
|
||||||
|
("Disable clipboard", "Tắt clipboard"),
|
||||||
|
("Lock after session end", "Khóa sau khi kết thúc session"),
|
||||||
|
("Insert", "Cài"),
|
||||||
|
("Insert Lock", "Cài khóa"),
|
||||||
|
("Refresh", "Làm mới"),
|
||||||
|
("ID does not exist", "ID không tồn tại"),
|
||||||
|
("Failed to connect to rendezvous server", "Không thể kết nối đến máy chủ rendezvous"),
|
||||||
|
("Please try later", "Thử lại sau"),
|
||||||
|
("Remote desktop is offline", "Máy tính từ xa hiện đang offline"),
|
||||||
|
("Key mismatch", "Chìa không khớp"),
|
||||||
|
("Timeout", "Quá thời gian"),
|
||||||
|
("Failed to connect to relay server", "Không thể kết nối tới máy chủ relay"),
|
||||||
|
("Failed to connect via rendezvous server", "Không thể kết nối qua máy chủ rendezvous"),
|
||||||
|
("Failed to connect via relay server", "Không thể kết nối qua máy chủ relay"),
|
||||||
|
("Failed to make direct connection to remote desktop", "Không thể kết nối thẳng tới máy tính từ xa"),
|
||||||
|
("Set Password", "Cài đặt mật khẩu"),
|
||||||
|
("OS Password", "Mật khẩu hệ điều hành"),
|
||||||
|
("install_tip", "Do UAC, RustDesk sẽ không thể hoạt động đúng cách là bên từ xa trong vài trường hợp. Để tránh UAC, hãy nhấn cái nút dưới đây để cài RustDesk vào hệ thống."),
|
||||||
|
("Click to upgrade", "Nhấn để nâng cấp"),
|
||||||
|
("Click to download", "Nhấn để tải xuống"),
|
||||||
|
("Click to update", "Nhấn để cập nhật"),
|
||||||
|
("Configure", "Cài đặt"),
|
||||||
|
("config_acc", "Để có thể điều khiển máy tính từ xa, bạn cần phải cung cấp quyền \"Trợ năng\" cho RustDesk"),
|
||||||
|
("config_screen", "Để có thể truy cập máy tính từ xa, bạn cần phải cung cấp quyền \"Ghi Màn Hình\" cho RustDesk."),
|
||||||
|
("Installing ...", "Đang cài ..."),
|
||||||
|
("Install", "Cài"),
|
||||||
|
("Installation", "Cài"),
|
||||||
|
("Installation Path", "Địa điểm cài"),
|
||||||
|
("Create start menu shortcuts", "Tạo shortcut tại start menu"),
|
||||||
|
("Create desktop icon", "Tạo biểu tượng trên desktop"),
|
||||||
|
("agreement_tip", "Bằng cách bắt đầu cài đặt, bạn chấp nhận thỏa thuận cấp phép."),
|
||||||
|
("Accept and Install", "Chấp nhận và cài"),
|
||||||
|
("End-user license agreement", "Thỏa thuận cấp phép dành cho người dùng"),
|
||||||
|
("Generating ...", "Đang tạo ..."),
|
||||||
|
("Your installation is lower version.", "Phiên bản của bạn là phiên bản cũ"),
|
||||||
|
("not_close_tcp_tip", "Đừng đóng cửa sổ này khi bạn đang sử dụng tunnel"),
|
||||||
|
("Listening ...", "Đang nghe ..."),
|
||||||
|
("Remote Host", "Máy từ xa"),
|
||||||
|
("Remote Port", "Cổng từ xa"),
|
||||||
|
("Action", "Hành động"),
|
||||||
|
("Add", "Thêm"),
|
||||||
|
("Local Port", "Cổng nội bộ"),
|
||||||
|
("setup_server_tip", "Để kết nối nhanh hơn, hãy tự tạo máy chủ riêng"),
|
||||||
|
("Too short, at least 6 characters.", "Quá ngắn, độ dài phải ít nhất là 6."),
|
||||||
|
("The confirmation is not identical.", "Xác minh không khớp"),
|
||||||
|
("Permissions", "Quyền"),
|
||||||
|
("Accept", "Chấp nhận"),
|
||||||
|
("Dismiss", "Bỏ qua"),
|
||||||
|
("Disconnect", "Ngắt kết nối"),
|
||||||
|
("Allow using keyboard and mouse", "Cho phép sử dụng bàn phím và chuột"),
|
||||||
|
("Allow using clipboard", "Cho phép sử dụng clipboard"),
|
||||||
|
("Allow hearing sound", "Cho phép nghe âm thanh"),
|
||||||
|
("Allow file copy and paste", "Cho phép sao chép và dán tệp tin"),
|
||||||
|
("Connected", "Đã kết nối"),
|
||||||
|
("Direct and encrypted connection", "Kết nối trực tiếp và đuợc mã hóa"),
|
||||||
|
("Relayed and encrypted connection", "Kết nối relay và đuợc mã hóa"),
|
||||||
|
("Direct and unencrypted connection", "Kết nối trực tiếp và không đuợc mã hóa"),
|
||||||
|
("Relayed and unencrypted connection", "Kết nối relay và không đuợc mã hóa"),
|
||||||
|
("Enter Remote ID", "Nhập ID từ xa"),
|
||||||
|
("Enter your password", "Nhập mật khẩu"),
|
||||||
|
("Logging in...", "Đang đăng nhập"),
|
||||||
|
("Enable RDP session sharing", "Cho phép chia sẻ session RDP"),
|
||||||
|
("Auto Login", "Tự động đăng nhập"),
|
||||||
|
("Enable Direct IP Access", "Cho phép truy cập trực tiếp qua IP"),
|
||||||
|
("Rename", "Đổi tên"),
|
||||||
|
("Space", "Space"),
|
||||||
|
("Create Desktop Shortcut", "Tạo shortcut trên desktop"),
|
||||||
|
("Change Path", "Đổi địa điểm"),
|
||||||
|
("Create Folder", "Tạo thư mục"),
|
||||||
|
("Please enter the folder name", "Hãy nhập tên thư mục"),
|
||||||
|
("Fix it", "Sửa nó"),
|
||||||
|
("Warning", "Cảnh báo"),
|
||||||
|
("Login screen using Wayland is not supported", "Màn hình đăng nhập sử dụng Wayland không đựoc hỗ trợ"),
|
||||||
|
("Reboot required", "Yêu cầu khởi động lại"),
|
||||||
|
("Unsupported display server ", "Máy chủ hiển thị không đuợc hỗ trọ"),
|
||||||
|
("x11 expected", "Cần x11"),
|
||||||
|
("Port", "Cổng"),
|
||||||
|
("Settings", "Cài đặt"),
|
||||||
|
("Username", "Tên người dùng"),
|
||||||
|
("Invalid port", "Cổng không hợp lệ"),
|
||||||
|
("Closed manually by the peer", "Đóng thủ công bởi peer"),
|
||||||
|
("Enable remote configuration modification", "Cho phép thay đổi cấu hình bên từ xa"),
|
||||||
|
("Run without install", "Chạy mà không cần cài"),
|
||||||
|
("Always connected via relay", "Luôn đuợc kết nối qua relay"),
|
||||||
|
("Always connect via relay", "Luôn kết nối qua relay"),
|
||||||
|
("whitelist_tip", "Chỉ có những IP đựoc cho phép mới có thể truy cập"),
|
||||||
|
("Login", "Đăng nhập"),
|
||||||
|
("Logout", "Đăng xuất"),
|
||||||
|
("Tags", "Tags"),
|
||||||
|
("Search ID", "Tìm ID"),
|
||||||
|
("Current Wayland display server is not supported", "Máy chủ hình ảnh Wayland hiện không đuợc hỗ trợ"),
|
||||||
|
("whitelist_sep", "Đuợc cách nhau bởi dấu phẩy, dấu chấm phẩy, dấu cách hay dòng mới"),
|
||||||
|
("Add ID", "Thêm ID"),
|
||||||
|
("Add Tag", "Thêm Tag"),
|
||||||
|
("Unselect all tags", "Hủy chọn tất cả các tag"),
|
||||||
|
("Network error", "Lỗi mạng"),
|
||||||
|
("Username missed", "Mất tên người dùng"),
|
||||||
|
("Password missed", "Mất mật khẩu"),
|
||||||
|
("Wrong credentials", "Chứng danh bị sai"),
|
||||||
|
("Edit Tag", "Chỉnh sửa Tag"),
|
||||||
|
("Unremember Password", "Quên mật khẩu"),
|
||||||
|
("Favorites", "Favorites"),
|
||||||
|
("Add to Favorites", "Thêm vào mục Favorites"),
|
||||||
|
("Remove from Favorites", "Xóa khỏi mục Favorites"),
|
||||||
|
("Empty", "Trống"),
|
||||||
|
("Invalid folder name", "Tên thư mục không hợp lệ"),
|
||||||
|
("Socks5 Proxy", "Proxy Socks5"),
|
||||||
|
("Hostname", "Tên host"),
|
||||||
|
("Discovered", "Đuợc phát hiện"),
|
||||||
|
("install_daemon_tip", "Để chạy lúc khởi động máy, bạn cần phải cài dịch vụ hệ thống."),
|
||||||
|
("Remote ID", "ID từ xa"),
|
||||||
|
("Paste", "Dán"),
|
||||||
|
("Paste here?", "Dán ở đây?"),
|
||||||
|
("Are you sure to close the connection?", "Bạn có chắc muốn đóng kết nối không"),
|
||||||
|
("Download new version", "Tải về phiên bản mới"),
|
||||||
|
("Touch mode", "Chế độ chạm"),
|
||||||
|
("Mouse mode", "Chế độ dùng chuột"),
|
||||||
|
("One-Finger Tap", "Chạm bằng một ngón tay"),
|
||||||
|
("Left Mouse", "Chuột trái"),
|
||||||
|
("One-Long Tap", "Chạm lâu bằng một ngón tay"),
|
||||||
|
("Two-Finger Tap", "Chạm bằng hai ngón tay"),
|
||||||
|
("Right Mouse", "Chuột phải"),
|
||||||
|
("One-Finger Move", "Di chuyển bằng một ngón tay"),
|
||||||
|
("Double Tap & Move", "Chạm hai lần và di chuyển"),
|
||||||
|
("Mouse Drag", "Di chuyển bằng chuột"),
|
||||||
|
("Three-Finger vertically", "Ba ngón tay theo chiều dọc"),
|
||||||
|
("Mouse Wheel", "Bánh xe lăn trê con chuột"),
|
||||||
|
("Two-Finger Move", "Di chuyển bằng hai ngón tay"),
|
||||||
|
("Canvas Move", "Di chuyển canvas"),
|
||||||
|
("Pinch to Zoom", "Véo để phóng to/nhỏ"),
|
||||||
|
("Canvas Zoom", "Phóng to/nhỏ canvas"),
|
||||||
|
("Reset canvas", "Cài đặt lại canvas"),
|
||||||
|
("No permission of file transfer", "Không có quyền truyền tệp tin"),
|
||||||
|
("Note", "Ghi nhớ"),
|
||||||
|
("Connection", "Kết nối"),
|
||||||
|
("Share Screen", "Chia sẻ màn hình"),
|
||||||
|
("CLOSE", "ĐÓNG"),
|
||||||
|
("OPEN", "MỞ"),
|
||||||
|
("Chat", "Chat"),
|
||||||
|
("Total", "Tổng"),
|
||||||
|
("items", "items"),
|
||||||
|
("Selected", "Đã đuợc chọn"),
|
||||||
|
("Screen Capture", "Ghi màn hình"),
|
||||||
|
("Input Control", "Điều khiển đầu vào"),
|
||||||
|
("Audio Capture", "Ghi âm thanh"),
|
||||||
|
("File Connection", "Kết nối tệp tin"),
|
||||||
|
("Screen Connection", "Kết nối màn hình"),
|
||||||
|
("Do you accept?", "Bạn có chấp nhận không?"),
|
||||||
|
("Open System Setting", "Mở cài đặt hệ thống"),
|
||||||
|
("How to get Android input permission?", "Cách để có quyền nhập trên Android?"),
|
||||||
|
("android_input_permission_tip1", "Để thiết bị từ xa điều khiển thiết bị Android của bạn bằng chuột hoặc chạm, bạn cần cho phép RustDesk sử dụng dịch vụ \"Trợ năng\"."),
|
||||||
|
("android_input_permission_tip2", "Vui lòng chuyển đến trang cài đặt hệ thống tiếp theo, tìm và nhập [Dịch vụ đã cài đặt], bật dịch vụ [RustDesk Input]."),
|
||||||
|
("android_new_connection_tip", "Yêu cầu kiểm soát mới đã được nhận, yêu cầu này muốn kiểm soát thiết bị hiện tại của bạn."),
|
||||||
|
("android_service_will_start_tip", "Bật \"Ghi màn hình\" sẽ tự động khởi động dịch vụ, cho phép các thiết bị khác yêu cầu kết nối với thiết bị của bạn."),
|
||||||
|
("android_stop_service_tip", "Đóng dịch vụ sẽ tự động đóng tất cả các kết nối đã thiết lập."),
|
||||||
|
("android_version_audio_tip", "Phiên bản Android hiện tại không hỗ trợ ghi âm, vui lòng nâng cấp lên Android 10 trở lên."),
|
||||||
|
("android_start_service_tip", "Nhấn vào [Bắt đầu dịch vụ] hoặc MỞ quyền [Ghi màn hình] để bắt đầu dịch vụ chia sẻ màn hình."),
|
||||||
|
("Account", "Tài khoản"),
|
||||||
|
("Overwrite", "Ghi đè"),
|
||||||
|
("This file exists, skip or overwrite this file?", "Tệp tin này đã tồn tại, bạn có muốn bỏ qua hay ghi đè lên tệp tin này?"),
|
||||||
|
("Quit", "Thoát"),
|
||||||
|
("doc_mac_permission", "https://rustdesk.com/docs/en/manual/mac/#enable-permissions"),
|
||||||
|
("Help", "Giúp đỡ"),
|
||||||
|
("Failed", "Thất bại"),
|
||||||
|
("Succeeded", "Thành công"),
|
||||||
|
("Someone turns on privacy mode, exit", "Ai đó đã bật chế độ riêng tư, thoát"),
|
||||||
|
("Unsupported", "Không hỗ trợ"),
|
||||||
|
("Peer denied", "Peer đã từ chối"),
|
||||||
|
("Please install plugins", "Hãy cài plugins"),
|
||||||
|
("Peer exit", "Peer đã thoát"),
|
||||||
|
("Failed to turn off", "Không thể tắt"),
|
||||||
|
("Turned off", "Đã tắt"),
|
||||||
|
("In privacy mode", "Vào chế độ riêng tư"),
|
||||||
|
("Out privacy mode", "Thoát chế độ riêng tư"),
|
||||||
|
("Language", "Ngôn ngữ"),
|
||||||
|
("Keep RustDesk background service", "Giữ dịch vụ nền RustDesk"),
|
||||||
|
("Ignore Battery Optimizations", "Bỏ qua các tối ưu pin"),
|
||||||
|
("android_open_battery_optimizations_tip", "Nếu bạn muốn tắt tính năng này, vui lòng chuyển đến trang cài đặt ứng dụng RustDesk tiếp theo, tìm và nhập [Pin], Bỏ chọn [Không hạn chế]"),
|
||||||
|
("Connection not allowed", "Kết nối không đuợc phép"),
|
||||||
|
("Use temporary password", "Sử dụng mật khẩu tạm thời"),
|
||||||
|
("Use permanent password", "Sử dụng mật khẩu vĩnh viễn"),
|
||||||
|
("Use both passwords", "Sử dụng cả hai mật khẩu"),
|
||||||
|
("Set permanent password", "Đặt mật khẩu vĩnh viễn"),
|
||||||
|
("Set temporary password length", "Đặt chiều dài của mật khẩu tạm thời"),
|
||||||
|
("Enable Remote Restart", "Bật khởi động lại từ xa"),
|
||||||
|
("Allow remote restart", "Cho phép khởi động lại từ xa"),
|
||||||
|
("Restart Remote Device", "Khởi động lại thiết bị từ xa"),
|
||||||
|
("Are you sure you want to restart", "Bạn có chắc bạn muốn khởi động lại không"),
|
||||||
|
("Restarting Remote Device", "Đang khởi động lại thiết bị từ xa"),
|
||||||
|
("remote_restarting_tip", "Thiết bị từ xa đang khởi động lại, hãy đóng cửa sổ tin nhắn này và kết nối lại với mật khẩu vĩnh viễn sau một khoảng thời gian"),
|
||||||
|
].iter().cloned().collect();
|
||||||
|
}
|
@ -152,7 +152,7 @@ fn main() {
|
|||||||
return;
|
return;
|
||||||
} else if args[0] == "--password" {
|
} else if args[0] == "--password" {
|
||||||
if args.len() == 2 {
|
if args.len() == 2 {
|
||||||
ipc::set_security_password(args[1].to_owned()).unwrap();
|
ipc::set_permanent_password(args[1].to_owned()).unwrap();
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
} else if args[0] == "--check-hwcodec-config" {
|
} else if args[0] == "--check-hwcodec-config" {
|
||||||
@ -172,18 +172,18 @@ fn import_config(path: &str) {
|
|||||||
let path = std::path::Path::new(path);
|
let path = std::path::Path::new(path);
|
||||||
log::info!("import config from {:?} and {:?}", path, path2);
|
log::info!("import config from {:?} and {:?}", path, path2);
|
||||||
let config: Config = load_path(path.into());
|
let config: Config = load_path(path.into());
|
||||||
if config.id.is_empty() || config.key_pair.0.is_empty() {
|
if config.is_empty() {
|
||||||
log::info!("Empty source config, skipped");
|
log::info!("Empty source config, skipped");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if get_modified_time(&path) > get_modified_time(&Config::file()) {
|
if get_modified_time(&path) > get_modified_time(&Config::file()) {
|
||||||
if Config::set(config) {
|
if store_path(Config::file(), config).is_err() {
|
||||||
log::info!("config written");
|
log::info!("config written");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let config2: Config2 = load_path(path2.into());
|
let config2: Config2 = load_path(path2.into());
|
||||||
if get_modified_time(&path2) > get_modified_time(&Config2::file()) {
|
if get_modified_time(&path2) > get_modified_time(&Config2::file()) {
|
||||||
if Config2::set(config2) {
|
if store_path(Config2::file(), config2).is_err() {
|
||||||
log::info!("config2 written");
|
log::info!("config2 written");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
322
src/mobile.rs
322
src/mobile.rs
@ -1,12 +1,16 @@
|
|||||||
use crate::client::*;
|
use crate::client::*;
|
||||||
use crate::common::{make_fd_to_json};
|
use crate::common::make_fd_to_json;
|
||||||
use flutter_rust_bridge::{StreamSink, ZeroCopyBuffer};
|
use flutter_rust_bridge::{StreamSink, ZeroCopyBuffer};
|
||||||
use hbb_common::{
|
use hbb_common::{
|
||||||
allow_err,
|
allow_err,
|
||||||
compress::decompress,
|
compress::decompress,
|
||||||
config::{Config, LocalConfig},
|
config::{Config, LocalConfig},
|
||||||
fs, log,
|
fs,
|
||||||
fs::{can_enable_overwrite_detection, new_send_confirm, DigestCheckResult, get_string, transform_windows_path},
|
fs::{
|
||||||
|
can_enable_overwrite_detection, get_string, new_send_confirm, transform_windows_path,
|
||||||
|
DigestCheckResult,
|
||||||
|
},
|
||||||
|
log,
|
||||||
message_proto::*,
|
message_proto::*,
|
||||||
protobuf::Message as _,
|
protobuf::Message as _,
|
||||||
rendezvous_proto::ConnType,
|
rendezvous_proto::ConnType,
|
||||||
@ -17,6 +21,7 @@ use hbb_common::{
|
|||||||
},
|
},
|
||||||
Stream,
|
Stream,
|
||||||
};
|
};
|
||||||
|
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||||
use std::{
|
use std::{
|
||||||
collections::{HashMap, VecDeque},
|
collections::{HashMap, VecDeque},
|
||||||
sync::{Arc, Mutex, RwLock},
|
sync::{Arc, Mutex, RwLock},
|
||||||
@ -83,6 +88,15 @@ impl Session {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn restart_remote_device() {
|
||||||
|
if let Some(session) = SESSION.write().unwrap().as_ref() {
|
||||||
|
let mut lc = session.lc.write().unwrap();
|
||||||
|
lc.restarting_remote_device = true;
|
||||||
|
let msg = lc.restart_remote_device();
|
||||||
|
session.send(Data::Message(msg));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn send(data: Data) {
|
fn send(data: Data) {
|
||||||
if let Some(session) = SESSION.read().unwrap().as_ref() {
|
if let Some(session) = SESSION.read().unwrap().as_ref() {
|
||||||
session.send(data);
|
session.send(data);
|
||||||
@ -397,6 +411,26 @@ impl Session {
|
|||||||
log::debug!("{:?}", msg_out);
|
log::debug!("{:?}", msg_out);
|
||||||
self.send_msg(msg_out);
|
self.send_msg(msg_out);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn update_quality_status(&self, status: QualityStatus) {
|
||||||
|
const NULL: String = String::new();
|
||||||
|
self.push_event(
|
||||||
|
"update_quality_status",
|
||||||
|
vec![
|
||||||
|
("speed", &status.speed.map_or(NULL, |it| it)),
|
||||||
|
("fps", &status.fps.map_or(NULL, |it| it.to_string())),
|
||||||
|
("delay", &status.delay.map_or(NULL, |it| it.to_string())),
|
||||||
|
(
|
||||||
|
"target_bitrate",
|
||||||
|
&status.target_bitrate.map_or(NULL, |it| it.to_string()),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"codec_format",
|
||||||
|
&status.codec_format.map_or(NULL, |it| it.to_string()),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FileManager for Session {}
|
impl FileManager for Session {}
|
||||||
@ -438,7 +472,11 @@ impl Interface for Session {
|
|||||||
|
|
||||||
if lc.is_file_transfer {
|
if lc.is_file_transfer {
|
||||||
if pi.username.is_empty() {
|
if pi.username.is_empty() {
|
||||||
self.msgbox("error", "Error", "No active console user logged on, please connect and logon first.");
|
self.msgbox(
|
||||||
|
"error",
|
||||||
|
"Error",
|
||||||
|
"No active console user logged on, please connect and logon first.",
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -478,8 +516,8 @@ impl Interface for Session {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_hash(&mut self, hash: Hash, peer: &mut Stream) {
|
async fn handle_hash(&mut self, pass: &str, hash: Hash, peer: &mut Stream) {
|
||||||
handle_hash(self.lc.clone(), hash, self, peer).await;
|
handle_hash(self.lc.clone(), pass, hash, self, peer).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_login_from_ui(&mut self, password: String, remember: bool, peer: &mut Stream) {
|
async fn handle_login_from_ui(&mut self, password: String, remember: bool, peer: &mut Stream) {
|
||||||
@ -487,7 +525,14 @@ impl Interface for Session {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_test_delay(&mut self, t: TestDelay, peer: &mut Stream) {
|
async fn handle_test_delay(&mut self, t: TestDelay, peer: &mut Stream) {
|
||||||
handle_test_delay(t, peer).await;
|
if !t.from_client {
|
||||||
|
self.update_quality_status(QualityStatus {
|
||||||
|
delay: Some(t.last_delay as _),
|
||||||
|
target_bitrate: Some(t.target_bitrate as _),
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
handle_test_delay(t, peer).await;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -502,6 +547,9 @@ struct Connection {
|
|||||||
write_jobs: Vec<fs::TransferJob>,
|
write_jobs: Vec<fs::TransferJob>,
|
||||||
timer: Interval,
|
timer: Interval,
|
||||||
last_update_jobs_status: (Instant, HashMap<i32, u64>),
|
last_update_jobs_status: (Instant, HashMap<i32, u64>),
|
||||||
|
data_count: Arc<AtomicUsize>,
|
||||||
|
frame_count: Arc<AtomicUsize>,
|
||||||
|
video_format: CodecFormat,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Connection {
|
impl Connection {
|
||||||
@ -528,6 +576,9 @@ impl Connection {
|
|||||||
write_jobs: Vec::new(),
|
write_jobs: Vec::new(),
|
||||||
timer: time::interval(SEC30),
|
timer: time::interval(SEC30),
|
||||||
last_update_jobs_status: (Instant::now(), Default::default()),
|
last_update_jobs_status: (Instant::now(), Default::default()),
|
||||||
|
data_count: Arc::new(AtomicUsize::new(0)),
|
||||||
|
frame_count: Arc::new(AtomicUsize::new(0)),
|
||||||
|
video_format: CodecFormat::Unknown,
|
||||||
};
|
};
|
||||||
let key = Config::get_option("key");
|
let key = Config::get_option("key");
|
||||||
let token = Config::get_option("access_token");
|
let token = Config::get_option("access_token");
|
||||||
@ -541,6 +592,9 @@ impl Connection {
|
|||||||
("direct", &direct.to_string()),
|
("direct", &direct.to_string()),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let mut status_timer = time::interval(Duration::new(1, 0));
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
tokio::select! {
|
tokio::select! {
|
||||||
res = peer.next() => {
|
res = peer.next() => {
|
||||||
@ -553,14 +607,20 @@ impl Connection {
|
|||||||
}
|
}
|
||||||
Ok(ref bytes) => {
|
Ok(ref bytes) => {
|
||||||
last_recv_time = Instant::now();
|
last_recv_time = Instant::now();
|
||||||
|
conn.data_count.fetch_add(bytes.len(), Ordering::Relaxed);
|
||||||
if !conn.handle_msg_from_peer(bytes, &mut peer).await {
|
if !conn.handle_msg_from_peer(bytes, &mut peer).await {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
log::info!("Reset by the peer");
|
if session.lc.read().unwrap().restarting_remote_device {
|
||||||
session.msgbox("error", "Connection Error", "Reset by the peer");
|
log::info!("Restart remote device");
|
||||||
|
session.msgbox("restarting", "Restarting Remote Device", "remote_restarting_tip");
|
||||||
|
} else {
|
||||||
|
log::info!("Reset by the peer");
|
||||||
|
session.msgbox("error", "Connection Error", "Reset by the peer");
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -586,6 +646,16 @@ impl Connection {
|
|||||||
conn.timer = time::interval_at(Instant::now() + SEC30, SEC30);
|
conn.timer = time::interval_at(Instant::now() + SEC30, SEC30);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
_ = status_timer.tick() => {
|
||||||
|
let speed = conn.data_count.swap(0, Ordering::Relaxed);
|
||||||
|
let speed = format!("{:.2}kB/s", speed as f32 / 1024 as f32);
|
||||||
|
let fps = conn.frame_count.swap(0, Ordering::Relaxed) as _;
|
||||||
|
conn.session.update_quality_status(QualityStatus {
|
||||||
|
speed:Some(speed),
|
||||||
|
fps:Some(fps),
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
log::debug!("Exit io_loop of id={}", session.id);
|
log::debug!("Exit io_loop of id={}", session.id);
|
||||||
@ -603,15 +673,24 @@ impl Connection {
|
|||||||
if !self.first_frame {
|
if !self.first_frame {
|
||||||
self.first_frame = true;
|
self.first_frame = true;
|
||||||
}
|
}
|
||||||
|
let incomming_format = CodecFormat::from(&vf);
|
||||||
|
if self.video_format != incomming_format {
|
||||||
|
self.video_format = incomming_format.clone();
|
||||||
|
self.session.update_quality_status(QualityStatus {
|
||||||
|
codec_format: Some(incomming_format),
|
||||||
|
..Default::default()
|
||||||
|
})
|
||||||
|
};
|
||||||
if let (Ok(true), Some(s)) = (
|
if let (Ok(true), Some(s)) = (
|
||||||
self.video_handler.handle_frame(vf),
|
self.video_handler.handle_frame(vf),
|
||||||
RGBA_STREAM.read().unwrap().as_ref(),
|
RGBA_STREAM.read().unwrap().as_ref(),
|
||||||
) {
|
) {
|
||||||
|
self.frame_count.fetch_add(1, Ordering::Relaxed);
|
||||||
s.add(ZeroCopyBuffer(self.video_handler.rgb.clone()));
|
s.add(ZeroCopyBuffer(self.video_handler.rgb.clone()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some(message::Union::Hash(hash)) => {
|
Some(message::Union::Hash(hash)) => {
|
||||||
self.session.handle_hash(hash, peer).await;
|
self.session.handle_hash("", hash, peer).await;
|
||||||
}
|
}
|
||||||
Some(message::Union::LoginResponse(lr)) => match lr.union {
|
Some(message::Union::LoginResponse(lr)) => match lr.union {
|
||||||
Some(login_response::Union::Error(err)) => {
|
Some(login_response::Union::Error(err)) => {
|
||||||
@ -629,7 +708,7 @@ impl Connection {
|
|||||||
let content = if cb.compress {
|
let content = if cb.compress {
|
||||||
decompress(&cb.content)
|
decompress(&cb.content)
|
||||||
} else {
|
} else {
|
||||||
cb.content
|
cb.content.into()
|
||||||
};
|
};
|
||||||
if let Ok(content) = String::from_utf8(content) {
|
if let Ok(content) = String::from_utf8(content) {
|
||||||
self.session
|
self.session
|
||||||
@ -664,113 +743,114 @@ impl Connection {
|
|||||||
vec![("x", &cp.x.to_string()), ("y", &cp.y.to_string())],
|
vec![("x", &cp.x.to_string()), ("y", &cp.y.to_string())],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Some(message::Union::FileResponse(fr)) => match fr.union {
|
Some(message::Union::FileResponse(fr)) => {
|
||||||
Some(file_response::Union::Dir(fd)) => {
|
match fr.union {
|
||||||
let mut entries = fd.entries.to_vec();
|
Some(file_response::Union::Dir(fd)) => {
|
||||||
if self.session.peer_platform() == "Windows" {
|
let mut entries = fd.entries.to_vec();
|
||||||
fs::transform_windows_path(&mut entries);
|
if self.session.peer_platform() == "Windows" {
|
||||||
}
|
fs::transform_windows_path(&mut entries);
|
||||||
let id = fd.id;
|
}
|
||||||
self.session.push_event(
|
let id = fd.id;
|
||||||
"file_dir",
|
self.session.push_event(
|
||||||
vec![("value", &make_fd_to_json(fd)), ("is_local", "false")],
|
"file_dir",
|
||||||
);
|
vec![("value", &make_fd_to_json(fd)), ("is_local", "false")],
|
||||||
if let Some(job) = fs::get_job(id, &mut self.write_jobs) {
|
);
|
||||||
job.set_files(entries);
|
if let Some(job) = fs::get_job(id, &mut self.write_jobs) {
|
||||||
}
|
job.set_files(entries);
|
||||||
}
|
|
||||||
Some(file_response::Union::Block(block)) => {
|
|
||||||
if let Some(job) = fs::get_job(block.id, &mut self.write_jobs) {
|
|
||||||
if let Err(_err) = job.write(block, None).await {
|
|
||||||
// to-do: add "skip" for writing job
|
|
||||||
}
|
}
|
||||||
self.update_jobs_status();
|
|
||||||
}
|
}
|
||||||
}
|
Some(file_response::Union::Block(block)) => {
|
||||||
Some(file_response::Union::Done(d)) => {
|
if let Some(job) = fs::get_job(block.id, &mut self.write_jobs) {
|
||||||
if let Some(job) = fs::get_job(d.id, &mut self.write_jobs) {
|
if let Err(_err) = job.write(block, None).await {
|
||||||
job.modify_time();
|
// to-do: add "skip" for writing job
|
||||||
fs::remove_job(d.id, &mut self.write_jobs);
|
}
|
||||||
|
self.update_jobs_status();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
self.handle_job_status(d.id, d.file_num, None);
|
Some(file_response::Union::Done(d)) => {
|
||||||
}
|
if let Some(job) = fs::get_job(d.id, &mut self.write_jobs) {
|
||||||
Some(file_response::Union::Error(e)) => {
|
job.modify_time();
|
||||||
self.handle_job_status(e.id, e.file_num, Some(e.error));
|
fs::remove_job(d.id, &mut self.write_jobs);
|
||||||
}
|
}
|
||||||
Some(file_response::Union::Digest(digest)) => {
|
self.handle_job_status(d.id, d.file_num, None);
|
||||||
if digest.is_upload {
|
}
|
||||||
if let Some(job) = fs::get_job(digest.id, &mut self.read_jobs) {
|
Some(file_response::Union::Error(e)) => {
|
||||||
if let Some(file) = job.files().get(digest.file_num as usize) {
|
self.handle_job_status(e.id, e.file_num, Some(e.error));
|
||||||
let read_path = get_string(&job.join(&file.name));
|
}
|
||||||
let overwrite_strategy = job.default_overwrite_strategy();
|
Some(file_response::Union::Digest(digest)) => {
|
||||||
if let Some(overwrite) = overwrite_strategy {
|
if digest.is_upload {
|
||||||
let req = FileTransferSendConfirmRequest {
|
if let Some(job) = fs::get_job(digest.id, &mut self.read_jobs) {
|
||||||
id: digest.id,
|
if let Some(file) = job.files().get(digest.file_num as usize) {
|
||||||
file_num: digest.file_num,
|
let read_path = get_string(&job.join(&file.name));
|
||||||
union: Some(if overwrite {
|
let overwrite_strategy = job.default_overwrite_strategy();
|
||||||
file_transfer_send_confirm_request::Union::OffsetBlk(0)
|
if let Some(overwrite) = overwrite_strategy {
|
||||||
} else {
|
let req = FileTransferSendConfirmRequest {
|
||||||
file_transfer_send_confirm_request::Union::Skip(
|
id: digest.id,
|
||||||
true,
|
file_num: digest.file_num,
|
||||||
)
|
union: Some(if overwrite {
|
||||||
}),
|
file_transfer_send_confirm_request::Union::OffsetBlk(0)
|
||||||
..Default::default()
|
} else {
|
||||||
};
|
file_transfer_send_confirm_request::Union::Skip(
|
||||||
job.confirm(&req);
|
true,
|
||||||
let msg = new_send_confirm(req);
|
)
|
||||||
allow_err!(peer.send(&msg).await);
|
}),
|
||||||
} else {
|
..Default::default()
|
||||||
self.handle_override_file_confirm(
|
};
|
||||||
digest.id,
|
job.confirm(&req);
|
||||||
digest.file_num,
|
let msg = new_send_confirm(req);
|
||||||
read_path,
|
allow_err!(peer.send(&msg).await);
|
||||||
true,
|
} else {
|
||||||
);
|
self.handle_override_file_confirm(
|
||||||
|
digest.id,
|
||||||
|
digest.file_num,
|
||||||
|
read_path,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
} else {
|
if let Some(job) = fs::get_job(digest.id, &mut self.write_jobs) {
|
||||||
if let Some(job) = fs::get_job(digest.id, &mut self.write_jobs) {
|
if let Some(file) = job.files().get(digest.file_num as usize) {
|
||||||
if let Some(file) = job.files().get(digest.file_num as usize) {
|
let write_path = get_string(&job.join(&file.name));
|
||||||
let write_path = get_string(&job.join(&file.name));
|
let overwrite_strategy = job.default_overwrite_strategy();
|
||||||
let overwrite_strategy = job.default_overwrite_strategy();
|
match fs::is_write_need_confirmation(&write_path, &digest) {
|
||||||
match fs::is_write_need_confirmation(&write_path, &digest) {
|
Ok(res) => match res {
|
||||||
Ok(res) => match res {
|
DigestCheckResult::IsSame => {
|
||||||
DigestCheckResult::IsSame => {
|
let msg= new_send_confirm(FileTransferSendConfirmRequest {
|
||||||
let msg= new_send_confirm(FileTransferSendConfirmRequest {
|
|
||||||
id: digest.id,
|
id: digest.id,
|
||||||
file_num: digest.file_num,
|
file_num: digest.file_num,
|
||||||
union: Some(file_transfer_send_confirm_request::Union::Skip(true)),
|
union: Some(file_transfer_send_confirm_request::Union::Skip(true)),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
});
|
});
|
||||||
self.session.send_msg(msg);
|
|
||||||
}
|
|
||||||
DigestCheckResult::NeedConfirm(digest) => {
|
|
||||||
if let Some(overwrite) = overwrite_strategy {
|
|
||||||
let msg = new_send_confirm(
|
|
||||||
FileTransferSendConfirmRequest {
|
|
||||||
id: digest.id,
|
|
||||||
file_num: digest.file_num,
|
|
||||||
union: Some(if overwrite {
|
|
||||||
file_transfer_send_confirm_request::Union::OffsetBlk(0)
|
|
||||||
} else {
|
|
||||||
file_transfer_send_confirm_request::Union::Skip(true)
|
|
||||||
}),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
);
|
|
||||||
self.session.send_msg(msg);
|
self.session.send_msg(msg);
|
||||||
} else {
|
|
||||||
self.handle_override_file_confirm(
|
|
||||||
digest.id,
|
|
||||||
digest.file_num,
|
|
||||||
write_path.to_string(),
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
DigestCheckResult::NeedConfirm(digest) => {
|
||||||
DigestCheckResult::NoSuchFile => {
|
if let Some(overwrite) = overwrite_strategy {
|
||||||
let msg = new_send_confirm(
|
let msg = new_send_confirm(
|
||||||
|
FileTransferSendConfirmRequest {
|
||||||
|
id: digest.id,
|
||||||
|
file_num: digest.file_num,
|
||||||
|
union: Some(if overwrite {
|
||||||
|
file_transfer_send_confirm_request::Union::OffsetBlk(0)
|
||||||
|
} else {
|
||||||
|
file_transfer_send_confirm_request::Union::Skip(true)
|
||||||
|
}),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
);
|
||||||
|
self.session.send_msg(msg);
|
||||||
|
} else {
|
||||||
|
self.handle_override_file_confirm(
|
||||||
|
digest.id,
|
||||||
|
digest.file_num,
|
||||||
|
write_path.to_string(),
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DigestCheckResult::NoSuchFile => {
|
||||||
|
let msg = new_send_confirm(
|
||||||
FileTransferSendConfirmRequest {
|
FileTransferSendConfirmRequest {
|
||||||
id: digest.id,
|
id: digest.id,
|
||||||
file_num: digest.file_num,
|
file_num: digest.file_num,
|
||||||
@ -778,19 +858,20 @@ impl Connection {
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
self.session.send_msg(msg);
|
self.session.send_msg(msg);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(err) => {
|
||||||
|
println!("error recving digest: {}", err);
|
||||||
}
|
}
|
||||||
},
|
|
||||||
Err(err) => {
|
|
||||||
println!("error recving digest: {}", err);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
_ => {}
|
}
|
||||||
},
|
|
||||||
Some(message::Union::Misc(misc)) => match misc.union {
|
Some(message::Union::Misc(misc)) => match misc.union {
|
||||||
Some(misc::Union::AudioFormat(f)) => {
|
Some(misc::Union::AudioFormat(f)) => {
|
||||||
self.audio_handler.handle_format(f); //
|
self.audio_handler.handle_format(f); //
|
||||||
@ -809,6 +890,7 @@ impl Connection {
|
|||||||
Permission::Keyboard => "keyboard",
|
Permission::Keyboard => "keyboard",
|
||||||
Permission::Clipboard => "clipboard",
|
Permission::Clipboard => "clipboard",
|
||||||
Permission::Audio => "audio",
|
Permission::Audio => "audio",
|
||||||
|
Permission::Restart => "restart",
|
||||||
_ => "",
|
_ => "",
|
||||||
},
|
},
|
||||||
&p.enabled.to_string(),
|
&p.enabled.to_string(),
|
||||||
@ -870,8 +952,7 @@ impl Connection {
|
|||||||
allow_err!(peer.send(&msg).await);
|
allow_err!(peer.send(&msg).await);
|
||||||
}
|
}
|
||||||
Data::SendFiles((id, path, to, file_num, include_hidden, is_remote)) => {
|
Data::SendFiles((id, path, to, file_num, include_hidden, is_remote)) => {
|
||||||
// in mobile, can_enable_override_detection is always true
|
let od = can_enable_overwrite_detection(self.session.lc.read().unwrap().version);
|
||||||
let od = true;
|
|
||||||
if is_remote {
|
if is_remote {
|
||||||
log::debug!("New job {}, write to {} from remote {}", id, to, path);
|
log::debug!("New job {}, write to {} from remote {}", id, to, path);
|
||||||
self.write_jobs.push(fs::TransferJob::new_write(
|
self.write_jobs.push(fs::TransferJob::new_write(
|
||||||
@ -882,7 +963,7 @@ impl Connection {
|
|||||||
include_hidden,
|
include_hidden,
|
||||||
is_remote,
|
is_remote,
|
||||||
Vec::new(),
|
Vec::new(),
|
||||||
true,
|
od,
|
||||||
));
|
));
|
||||||
allow_err!(
|
allow_err!(
|
||||||
peer.send(&fs::new_send(id, path, file_num, include_hidden))
|
peer.send(&fs::new_send(id, path, file_num, include_hidden))
|
||||||
@ -896,7 +977,7 @@ impl Connection {
|
|||||||
file_num,
|
file_num,
|
||||||
include_hidden,
|
include_hidden,
|
||||||
is_remote,
|
is_remote,
|
||||||
true,
|
od,
|
||||||
) {
|
) {
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
self.handle_job_status(id, -1, Some(err.to_string()));
|
self.handle_job_status(id, -1, Some(err.to_string()));
|
||||||
@ -1213,15 +1294,13 @@ pub mod connection_manager {
|
|||||||
Some(Data::Login {
|
Some(Data::Login {
|
||||||
id,
|
id,
|
||||||
is_file_transfer,
|
is_file_transfer,
|
||||||
port_forward,
|
|
||||||
peer_id,
|
peer_id,
|
||||||
name,
|
name,
|
||||||
authorized,
|
authorized,
|
||||||
keyboard,
|
keyboard,
|
||||||
clipboard,
|
clipboard,
|
||||||
audio,
|
audio,
|
||||||
file,
|
..
|
||||||
file_transfer_enabled,
|
|
||||||
}) => {
|
}) => {
|
||||||
current_id = id;
|
current_id = id;
|
||||||
let mut client = Client {
|
let mut client = Client {
|
||||||
@ -1380,9 +1459,8 @@ pub mod connection_manager {
|
|||||||
id,
|
id,
|
||||||
file_num,
|
file_num,
|
||||||
mut files,
|
mut files,
|
||||||
|
overwrite_detection
|
||||||
} => {
|
} => {
|
||||||
// in mobile, can_enable_override_detection is always true
|
|
||||||
let od = true;
|
|
||||||
WRITE_JOBS.lock().unwrap().push(fs::TransferJob::new_write(
|
WRITE_JOBS.lock().unwrap().push(fs::TransferJob::new_write(
|
||||||
id,
|
id,
|
||||||
"".to_string(),
|
"".to_string(),
|
||||||
@ -1398,7 +1476,7 @@ pub mod connection_manager {
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
})
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
true,
|
overwrite_detection
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
ipc::FS::CancelWrite { id } => {
|
ipc::FS::CancelWrite { id } => {
|
||||||
|
@ -1,12 +1,11 @@
|
|||||||
use crate::client::file_trait::FileManager;
|
use crate::client::file_trait::FileManager;
|
||||||
|
use crate::common::make_fd_to_json;
|
||||||
use crate::mobile::connection_manager::{self, get_clients_length, get_clients_state};
|
use crate::mobile::connection_manager::{self, get_clients_length, get_clients_state};
|
||||||
use crate::mobile::{self, Session};
|
use crate::mobile::{self, Session};
|
||||||
use crate::common::{make_fd_to_json};
|
|
||||||
use flutter_rust_bridge::{StreamSink, ZeroCopyBuffer};
|
use flutter_rust_bridge::{StreamSink, ZeroCopyBuffer};
|
||||||
use hbb_common::ResultType;
|
|
||||||
use hbb_common::{
|
use hbb_common::{
|
||||||
config::{self, Config, LocalConfig, PeerConfig, ONLINE},
|
config::{self, Config, LocalConfig, PeerConfig, ONLINE},
|
||||||
fs, log,
|
fs, log, password_security as password, ResultType,
|
||||||
};
|
};
|
||||||
use serde_json::{Number, Value};
|
use serde_json::{Number, Value};
|
||||||
use std::{
|
use std::{
|
||||||
@ -16,7 +15,6 @@ use std::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
fn initialize(app_dir: &str) {
|
fn initialize(app_dir: &str) {
|
||||||
*config::APP_DIR.write().unwrap() = app_dir.to_owned();
|
|
||||||
#[cfg(target_os = "android")]
|
#[cfg(target_os = "android")]
|
||||||
{
|
{
|
||||||
android_logger::init_once(
|
android_logger::init_once(
|
||||||
@ -30,6 +28,7 @@ fn initialize(app_dir: &str) {
|
|||||||
use hbb_common::env_logger::*;
|
use hbb_common::env_logger::*;
|
||||||
init_from_env(Env::default().filter_or(DEFAULT_FILTER_ENV, "debug"));
|
init_from_env(Env::default().filter_or(DEFAULT_FILTER_ENV, "debug"));
|
||||||
}
|
}
|
||||||
|
*config::APP_DIR.write().unwrap() = app_dir.to_owned();
|
||||||
crate::common::test_rendezvous_server();
|
crate::common::test_rendezvous_server();
|
||||||
crate::common::test_nat_type();
|
crate::common::test_nat_type();
|
||||||
#[cfg(target_os = "android")]
|
#[cfg(target_os = "android")]
|
||||||
@ -114,21 +113,13 @@ unsafe extern "C" fn get_by_name(name: *const c_char, arg: *const c_char) -> *co
|
|||||||
res = Session::get_option(arg);
|
res = Session::get_option(arg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"server_id" => {
|
"local_option" => {
|
||||||
res = Config::get_id();
|
if let Ok(arg) = arg.to_str() {
|
||||||
|
res = LocalConfig::get_option(arg);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
"server_password" => {
|
"langs" => {
|
||||||
res = Config::get_password();
|
res = crate::lang::LANGS.to_string();
|
||||||
}
|
|
||||||
"connect_statue" => {
|
|
||||||
res = ONLINE
|
|
||||||
.lock()
|
|
||||||
.unwrap()
|
|
||||||
.values()
|
|
||||||
.max()
|
|
||||||
.unwrap_or(&0)
|
|
||||||
.clone()
|
|
||||||
.to_string();
|
|
||||||
}
|
}
|
||||||
// File Action
|
// File Action
|
||||||
"get_home_dir" => {
|
"get_home_dir" => {
|
||||||
@ -150,6 +141,25 @@ unsafe extern "C" fn get_by_name(name: *const c_char, arg: *const c_char) -> *co
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Server Side
|
// Server Side
|
||||||
|
"server_id" => {
|
||||||
|
res = Config::get_id();
|
||||||
|
}
|
||||||
|
"permanent_password" => {
|
||||||
|
res = Config::get_permanent_password();
|
||||||
|
}
|
||||||
|
"temporary_password" => {
|
||||||
|
res = password::temporary_password();
|
||||||
|
}
|
||||||
|
"connect_statue" => {
|
||||||
|
res = ONLINE
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.values()
|
||||||
|
.max()
|
||||||
|
.unwrap_or(&0)
|
||||||
|
.clone()
|
||||||
|
.to_string();
|
||||||
|
}
|
||||||
#[cfg(target_os = "android")]
|
#[cfg(target_os = "android")]
|
||||||
"clients_state" => {
|
"clients_state" => {
|
||||||
res = get_clients_state();
|
res = get_clients_state();
|
||||||
@ -163,7 +173,7 @@ unsafe extern "C" fn get_by_name(name: *const c_char, arg: *const c_char) -> *co
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
"uuid" => {
|
"uuid" => {
|
||||||
res = base64::encode(crate::get_uuid());
|
res = base64::encode(hbb_common::get_uuid());
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
log::error!("Unknown name of get_by_name: {}", name);
|
log::error!("Unknown name of get_by_name: {}", name);
|
||||||
@ -309,9 +319,21 @@ unsafe extern "C" fn set_by_name(name: *const c_char, value: *const c_char) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
"local_option" => {
|
||||||
|
if let Ok(m) = serde_json::from_str::<HashMap<String, String>>(value) {
|
||||||
|
if let Some(name) = m.get("name") {
|
||||||
|
if let Some(value) = m.get("value") {
|
||||||
|
LocalConfig::set_option(name.to_owned(), value.to_owned());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
"input_os_password" => {
|
"input_os_password" => {
|
||||||
Session::input_os_password(value.to_owned(), true);
|
Session::input_os_password(value.to_owned(), true);
|
||||||
}
|
}
|
||||||
|
"restart_remote_device" => {
|
||||||
|
Session::restart_remote_device();
|
||||||
|
}
|
||||||
// File Action
|
// File Action
|
||||||
"read_remote_dir" => {
|
"read_remote_dir" => {
|
||||||
if let Ok(m) = serde_json::from_str::<HashMap<String, String>>(value) {
|
if let Ok(m) = serde_json::from_str::<HashMap<String, String>>(value) {
|
||||||
@ -457,12 +479,9 @@ unsafe extern "C" fn set_by_name(name: *const c_char, value: *const c_char) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Server Side
|
// Server Side
|
||||||
"update_password" => {
|
"permanent_password" => Config::set_permanent_password(value),
|
||||||
if value.is_empty() {
|
"temporary_password" => {
|
||||||
Config::set_password(&Config::get_auto_password());
|
password::update_temporary_password();
|
||||||
} else {
|
|
||||||
Config::set_password(value);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
#[cfg(target_os = "android")]
|
#[cfg(target_os = "android")]
|
||||||
"chat_server_mode" => {
|
"chat_server_mode" => {
|
||||||
|
@ -42,6 +42,7 @@ fn run_rdp(port: u16) {
|
|||||||
|
|
||||||
pub async fn listen(
|
pub async fn listen(
|
||||||
id: String,
|
id: String,
|
||||||
|
password: String,
|
||||||
port: i32,
|
port: i32,
|
||||||
interface: impl Interface,
|
interface: impl Interface,
|
||||||
ui_receiver: mpsc::UnboundedReceiver<Data>,
|
ui_receiver: mpsc::UnboundedReceiver<Data>,
|
||||||
@ -61,8 +62,9 @@ pub async fn listen(
|
|||||||
Ok((forward, addr)) = listener.accept() => {
|
Ok((forward, addr)) = listener.accept() => {
|
||||||
log::info!("new connection from {:?}", addr);
|
log::info!("new connection from {:?}", addr);
|
||||||
let id = id.clone();
|
let id = id.clone();
|
||||||
|
let password = password.clone();
|
||||||
let mut forward = Framed::new(forward, BytesCodec::new());
|
let mut forward = Framed::new(forward, BytesCodec::new());
|
||||||
match connect_and_login(&id, &mut ui_receiver, interface.clone(), &mut forward, key, token, is_rdp).await {
|
match connect_and_login(&id, &password, &mut ui_receiver, interface.clone(), &mut forward, key, token, is_rdp).await {
|
||||||
Ok(Some(stream)) => {
|
Ok(Some(stream)) => {
|
||||||
let interface = interface.clone();
|
let interface = interface.clone();
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
@ -96,6 +98,7 @@ pub async fn listen(
|
|||||||
|
|
||||||
async fn connect_and_login(
|
async fn connect_and_login(
|
||||||
id: &str,
|
id: &str,
|
||||||
|
password: &str,
|
||||||
ui_receiver: &mut mpsc::UnboundedReceiver<Data>,
|
ui_receiver: &mut mpsc::UnboundedReceiver<Data>,
|
||||||
interface: impl Interface,
|
interface: impl Interface,
|
||||||
forward: &mut Framed<TcpStream, BytesCodec>,
|
forward: &mut Framed<TcpStream, BytesCodec>,
|
||||||
@ -121,7 +124,7 @@ async fn connect_and_login(
|
|||||||
let msg_in = Message::parse_from_bytes(&bytes)?;
|
let msg_in = Message::parse_from_bytes(&bytes)?;
|
||||||
match msg_in.union {
|
match msg_in.union {
|
||||||
Some(message::Union::Hash(hash)) => {
|
Some(message::Union::Hash(hash)) => {
|
||||||
interface.handle_hash(hash, &mut stream).await;
|
interface.handle_hash(password, hash, &mut stream).await;
|
||||||
}
|
}
|
||||||
Some(message::Union::LoginResponse(lr)) => match lr.union {
|
Some(message::Union::LoginResponse(lr)) => match lr.union {
|
||||||
Some(login_response::Union::Error(err)) => {
|
Some(login_response::Union::Error(err)) => {
|
||||||
|
@ -101,7 +101,7 @@ async fn accept_connection_(server: ServerPtr, socket: Stream, secure: bool) ->
|
|||||||
async fn check_privacy_mode_on(stream: &mut Stream) -> ResultType<()> {
|
async fn check_privacy_mode_on(stream: &mut Stream) -> ResultType<()> {
|
||||||
if video_service::get_privacy_mode_conn_id() > 0 {
|
if video_service::get_privacy_mode_conn_id() > 0 {
|
||||||
let msg_out =
|
let msg_out =
|
||||||
crate::common::make_privacy_mode_msg(back_notification::PrivacyModeState::OnByOther);
|
crate::common::make_privacy_mode_msg(back_notification::PrivacyModeState::PrvOnByOther);
|
||||||
timeout(CONNECT_TIMEOUT, stream.send(&msg_out)).await??;
|
timeout(CONNECT_TIMEOUT, stream.send(&msg_out)).await??;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -347,7 +347,7 @@ fn send_f32(data: &[f32], encoder: &mut Encoder, sp: &GenericService) {
|
|||||||
Ok(data) => {
|
Ok(data) => {
|
||||||
let mut msg_out = Message::new();
|
let mut msg_out = Message::new();
|
||||||
msg_out.set_audio_frame(AudioFrame {
|
msg_out.set_audio_frame(AudioFrame {
|
||||||
data,
|
data: data.into(),
|
||||||
timestamp: crate::common::get_time(),
|
timestamp: crate::common::get_time(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
});
|
});
|
||||||
|
@ -7,15 +7,14 @@ use crate::video_service;
|
|||||||
#[cfg(any(target_os = "android", target_os = "ios"))]
|
#[cfg(any(target_os = "android", target_os = "ios"))]
|
||||||
use crate::{common::MOBILE_INFO2, mobile::connection_manager::start_channel};
|
use crate::{common::MOBILE_INFO2, mobile::connection_manager::start_channel};
|
||||||
use crate::{ipc, VERSION};
|
use crate::{ipc, VERSION};
|
||||||
use hbb_common::fs::can_enable_overwrite_detection;
|
|
||||||
use hbb_common::password_security::password;
|
|
||||||
use hbb_common::{
|
use hbb_common::{
|
||||||
config::Config,
|
config::Config,
|
||||||
fs,
|
fs,
|
||||||
|
fs::can_enable_overwrite_detection,
|
||||||
futures::{SinkExt, StreamExt},
|
futures::{SinkExt, StreamExt},
|
||||||
get_version_number,
|
get_version_number,
|
||||||
message_proto::{option_message::BoolOption, permission_info::Permission},
|
message_proto::{option_message::BoolOption, permission_info::Permission},
|
||||||
sleep, timeout,
|
password_security as password, sleep, timeout,
|
||||||
tokio::{
|
tokio::{
|
||||||
net::TcpStream,
|
net::TcpStream,
|
||||||
sync::mpsc,
|
sync::mpsc,
|
||||||
@ -31,6 +30,8 @@ use std::sync::{
|
|||||||
atomic::{AtomicI64, Ordering},
|
atomic::{AtomicI64, Ordering},
|
||||||
mpsc as std_mpsc,
|
mpsc as std_mpsc,
|
||||||
};
|
};
|
||||||
|
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||||
|
use system_shutdown;
|
||||||
|
|
||||||
pub type Sender = mpsc::UnboundedSender<(Instant, Arc<Message>)>;
|
pub type Sender = mpsc::UnboundedSender<(Instant, Arc<Message>)>;
|
||||||
|
|
||||||
@ -79,6 +80,7 @@ pub struct Connection {
|
|||||||
clipboard: bool,
|
clipboard: bool,
|
||||||
audio: bool,
|
audio: bool,
|
||||||
file: bool,
|
file: bool,
|
||||||
|
restart: bool,
|
||||||
last_test_delay: i64,
|
last_test_delay: i64,
|
||||||
lock_after_session_end: bool,
|
lock_after_session_end: bool,
|
||||||
show_remote_cursor: bool, // by peer
|
show_remote_cursor: bool, // by peer
|
||||||
@ -134,7 +136,7 @@ impl Connection {
|
|||||||
) {
|
) {
|
||||||
let hash = Hash {
|
let hash = Hash {
|
||||||
salt: Config::get_salt(),
|
salt: Config::get_salt(),
|
||||||
challenge: Config::get_auto_password(),
|
challenge: Config::get_auto_password(6),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
let (tx_from_cm_holder, mut rx_from_cm) = mpsc::unbounded_channel::<ipc::Data>();
|
let (tx_from_cm_holder, mut rx_from_cm) = mpsc::unbounded_channel::<ipc::Data>();
|
||||||
@ -166,6 +168,7 @@ impl Connection {
|
|||||||
clipboard: Config::get_option("enable-clipboard").is_empty(),
|
clipboard: Config::get_option("enable-clipboard").is_empty(),
|
||||||
audio: Config::get_option("enable-audio").is_empty(),
|
audio: Config::get_option("enable-audio").is_empty(),
|
||||||
file: Config::get_option("enable-file-transfer").is_empty(),
|
file: Config::get_option("enable-file-transfer").is_empty(),
|
||||||
|
restart: Config::get_option("enable-remote-restart").is_empty(),
|
||||||
last_test_delay: 0,
|
last_test_delay: 0,
|
||||||
lock_after_session_end: false,
|
lock_after_session_end: false,
|
||||||
show_remote_cursor: false,
|
show_remote_cursor: false,
|
||||||
@ -204,6 +207,9 @@ impl Connection {
|
|||||||
if !conn.file {
|
if !conn.file {
|
||||||
conn.send_permission(Permission::File, false).await;
|
conn.send_permission(Permission::File, false).await;
|
||||||
}
|
}
|
||||||
|
if !conn.restart {
|
||||||
|
conn.send_permission(Permission::Restart, false).await;
|
||||||
|
}
|
||||||
let mut test_delay_timer =
|
let mut test_delay_timer =
|
||||||
time::interval_at(Instant::now() + TEST_DELAY_TIMEOUT, TEST_DELAY_TIMEOUT);
|
time::interval_at(Instant::now() + TEST_DELAY_TIMEOUT, TEST_DELAY_TIMEOUT);
|
||||||
let mut last_recv_time = Instant::now();
|
let mut last_recv_time = Instant::now();
|
||||||
@ -281,6 +287,9 @@ impl Connection {
|
|||||||
conn.file = enabled;
|
conn.file = enabled;
|
||||||
conn.send_permission(Permission::File, enabled).await;
|
conn.send_permission(Permission::File, enabled).await;
|
||||||
conn.send_to_cm(ipc::Data::ClipboardFileEnabled(conn.file_transfer_enabled()));
|
conn.send_to_cm(ipc::Data::ClipboardFileEnabled(conn.file_transfer_enabled()));
|
||||||
|
} else if &name == "restart" {
|
||||||
|
conn.restart = enabled;
|
||||||
|
conn.send_permission(Permission::Restart, enabled).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ipc::Data::RawMessage(bytes) => {
|
ipc::Data::RawMessage(bytes) => {
|
||||||
@ -297,24 +306,24 @@ impl Connection {
|
|||||||
ipc::PrivacyModeState::OffSucceeded => {
|
ipc::PrivacyModeState::OffSucceeded => {
|
||||||
video_service::set_privacy_mode_conn_id(0);
|
video_service::set_privacy_mode_conn_id(0);
|
||||||
crate::common::make_privacy_mode_msg(
|
crate::common::make_privacy_mode_msg(
|
||||||
back_notification::PrivacyModeState::OffSucceeded,
|
back_notification::PrivacyModeState::PrvOffSucceeded,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
ipc::PrivacyModeState::OffFailed => {
|
ipc::PrivacyModeState::OffFailed => {
|
||||||
crate::common::make_privacy_mode_msg(
|
crate::common::make_privacy_mode_msg(
|
||||||
back_notification::PrivacyModeState::OffFailed,
|
back_notification::PrivacyModeState::PrvOffFailed,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
ipc::PrivacyModeState::OffByPeer => {
|
ipc::PrivacyModeState::OffByPeer => {
|
||||||
video_service::set_privacy_mode_conn_id(0);
|
video_service::set_privacy_mode_conn_id(0);
|
||||||
crate::common::make_privacy_mode_msg(
|
crate::common::make_privacy_mode_msg(
|
||||||
back_notification::PrivacyModeState::OffByPeer,
|
back_notification::PrivacyModeState::PrvOffByPeer,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
ipc::PrivacyModeState::OffUnknown => {
|
ipc::PrivacyModeState::OffUnknown => {
|
||||||
video_service::set_privacy_mode_conn_id(0);
|
video_service::set_privacy_mode_conn_id(0);
|
||||||
crate::common::make_privacy_mode_msg(
|
crate::common::make_privacy_mode_msg(
|
||||||
back_notification::PrivacyModeState::OffUnknown,
|
back_notification::PrivacyModeState::PrvOffUnknown,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -415,7 +424,9 @@ impl Connection {
|
|||||||
video_service::notify_video_frame_feched(id, None);
|
video_service::notify_video_frame_feched(id, None);
|
||||||
scrap::codec::Encoder::update_video_encoder(id, scrap::codec::EncoderUpdate::Remove);
|
scrap::codec::Encoder::update_video_encoder(id, scrap::codec::EncoderUpdate::Remove);
|
||||||
video_service::VIDEO_QOS.lock().unwrap().reset();
|
video_service::VIDEO_QOS.lock().unwrap().reset();
|
||||||
password::after_session(conn.authorized);
|
if conn.authorized {
|
||||||
|
password::update_temporary_password();
|
||||||
|
}
|
||||||
if let Err(err) = conn.try_port_forward_loop(&mut rx_from_cm).await {
|
if let Err(err) = conn.try_port_forward_loop(&mut rx_from_cm).await {
|
||||||
conn.on_close(&err.to_string(), false).await;
|
conn.on_close(&err.to_string(), false).await;
|
||||||
}
|
}
|
||||||
@ -453,7 +464,7 @@ impl Connection {
|
|||||||
} else {
|
} else {
|
||||||
Self::send_block_input_error(
|
Self::send_block_input_error(
|
||||||
&tx,
|
&tx,
|
||||||
back_notification::BlockInputState::OnFailed,
|
back_notification::BlockInputState::BlkOnFailed,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -463,7 +474,7 @@ impl Connection {
|
|||||||
} else {
|
} else {
|
||||||
Self::send_block_input_error(
|
Self::send_block_input_error(
|
||||||
&tx,
|
&tx,
|
||||||
back_notification::BlockInputState::OffFailed,
|
back_notification::BlockInputState::BlkOffFailed,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -765,6 +776,7 @@ impl Connection {
|
|||||||
audio: self.audio,
|
audio: self.audio,
|
||||||
file: self.file,
|
file: self.file,
|
||||||
file_transfer_enabled: self.file_transfer_enabled(),
|
file_transfer_enabled: self.file_transfer_enabled(),
|
||||||
|
restart: self.restart,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -821,17 +833,9 @@ impl Connection {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn validate_password(&mut self) -> bool {
|
fn validate_password(&mut self) -> bool {
|
||||||
if password::security_enabled() {
|
if password::temporary_enabled() {
|
||||||
if self.validate_one_password(Config::get_security_password()) {
|
let password = password::temporary_password();
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if password::random_password_valid() {
|
|
||||||
let password = password::random_password();
|
|
||||||
if self.validate_one_password(password.clone()) {
|
if self.validate_one_password(password.clone()) {
|
||||||
if password::onetime_password_activated() {
|
|
||||||
password::set_onetime_password_activated(false);
|
|
||||||
}
|
|
||||||
SESSIONS.lock().unwrap().insert(
|
SESSIONS.lock().unwrap().insert(
|
||||||
self.lr.my_id.clone(),
|
self.lr.my_id.clone(),
|
||||||
Session {
|
Session {
|
||||||
@ -844,6 +848,11 @@ impl Connection {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if password::permanent_enabled() {
|
||||||
|
if self.validate_one_password(Config::get_permanent_password()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -957,7 +966,7 @@ impl Connection {
|
|||||||
} else if lr.password.is_empty() {
|
} else if lr.password.is_empty() {
|
||||||
self.try_start_cm(lr.my_id, lr.my_name, false);
|
self.try_start_cm(lr.my_id, lr.my_name, false);
|
||||||
} else {
|
} else {
|
||||||
if password::passwords().len() == 0 {
|
if !password::has_valid_password() {
|
||||||
self.send_login_error("Connection not allowed").await;
|
self.send_login_error("Connection not allowed").await;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -1091,8 +1100,9 @@ impl Connection {
|
|||||||
}
|
}
|
||||||
Some(file_action::Union::Send(s)) => {
|
Some(file_action::Union::Send(s)) => {
|
||||||
let id = s.id;
|
let id = s.id;
|
||||||
let od =
|
let od = can_enable_overwrite_detection(get_version_number(
|
||||||
can_enable_overwrite_detection(get_version_number(VERSION));
|
&self.lr.version,
|
||||||
|
));
|
||||||
let path = s.path.clone();
|
let path = s.path.clone();
|
||||||
match fs::TransferJob::new_read(
|
match fs::TransferJob::new_read(
|
||||||
id,
|
id,
|
||||||
@ -1115,6 +1125,11 @@ impl Connection {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some(file_action::Union::Receive(r)) => {
|
Some(file_action::Union::Receive(r)) => {
|
||||||
|
// note: 1.1.10 introduced identical file detection, which breaks original logic of send/recv files
|
||||||
|
// whenever got send/recv request, check peer version to ensure old version of rustdesk
|
||||||
|
let od = can_enable_overwrite_detection(get_version_number(
|
||||||
|
&self.lr.version,
|
||||||
|
));
|
||||||
self.send_fs(ipc::FS::NewWrite {
|
self.send_fs(ipc::FS::NewWrite {
|
||||||
path: r.path,
|
path: r.path,
|
||||||
id: r.id,
|
id: r.id,
|
||||||
@ -1125,6 +1140,7 @@ impl Connection {
|
|||||||
.drain(..)
|
.drain(..)
|
||||||
.map(|f| (f.name, f.modified_time))
|
.map(|f| (f.name, f.modified_time))
|
||||||
.collect(),
|
.collect(),
|
||||||
|
overwrite_detection: od,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
Some(file_action::Union::RemoveDir(d)) => {
|
Some(file_action::Union::RemoveDir(d)) => {
|
||||||
@ -1210,6 +1226,17 @@ impl Connection {
|
|||||||
SESSIONS.lock().unwrap().remove(&self.lr.my_id);
|
SESSIONS.lock().unwrap().remove(&self.lr.my_id);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Some(misc::Union::RestartRemoteDevice(_)) =>
|
||||||
|
{
|
||||||
|
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||||
|
if self.restart {
|
||||||
|
match system_shutdown::reboot() {
|
||||||
|
Ok(_) => log::info!("Restart by the peer"),
|
||||||
|
Err(e) => log::error!("Failed to restart:{}", e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
},
|
},
|
||||||
_ => {}
|
_ => {}
|
||||||
@ -1297,7 +1324,7 @@ impl Connection {
|
|||||||
BoolOption::Yes => {
|
BoolOption::Yes => {
|
||||||
let msg_out = if !video_service::is_privacy_mode_supported() {
|
let msg_out = if !video_service::is_privacy_mode_supported() {
|
||||||
crate::common::make_privacy_mode_msg(
|
crate::common::make_privacy_mode_msg(
|
||||||
back_notification::PrivacyModeState::NotSupported,
|
back_notification::PrivacyModeState::PrvNotSupported,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
match privacy_mode::turn_on_privacy(self.inner.id) {
|
match privacy_mode::turn_on_privacy(self.inner.id) {
|
||||||
@ -1305,7 +1332,7 @@ impl Connection {
|
|||||||
if video_service::test_create_capturer(self.inner.id, 5_000) {
|
if video_service::test_create_capturer(self.inner.id, 5_000) {
|
||||||
video_service::set_privacy_mode_conn_id(self.inner.id);
|
video_service::set_privacy_mode_conn_id(self.inner.id);
|
||||||
crate::common::make_privacy_mode_msg(
|
crate::common::make_privacy_mode_msg(
|
||||||
back_notification::PrivacyModeState::OnSucceeded,
|
back_notification::PrivacyModeState::PrvOnSucceeded,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
log::error!(
|
log::error!(
|
||||||
@ -1314,12 +1341,12 @@ impl Connection {
|
|||||||
video_service::set_privacy_mode_conn_id(0);
|
video_service::set_privacy_mode_conn_id(0);
|
||||||
let _ = privacy_mode::turn_off_privacy(self.inner.id);
|
let _ = privacy_mode::turn_off_privacy(self.inner.id);
|
||||||
crate::common::make_privacy_mode_msg(
|
crate::common::make_privacy_mode_msg(
|
||||||
back_notification::PrivacyModeState::OnFailed,
|
back_notification::PrivacyModeState::PrvOnFailed,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(false) => crate::common::make_privacy_mode_msg(
|
Ok(false) => crate::common::make_privacy_mode_msg(
|
||||||
back_notification::PrivacyModeState::OnFailedPlugin,
|
back_notification::PrivacyModeState::PrvOnFailedPlugin,
|
||||||
),
|
),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
log::error!("Failed to turn on privacy mode. {}", e);
|
log::error!("Failed to turn on privacy mode. {}", e);
|
||||||
@ -1327,7 +1354,7 @@ impl Connection {
|
|||||||
let _ = privacy_mode::turn_off_privacy(0);
|
let _ = privacy_mode::turn_off_privacy(0);
|
||||||
}
|
}
|
||||||
crate::common::make_privacy_mode_msg(
|
crate::common::make_privacy_mode_msg(
|
||||||
back_notification::PrivacyModeState::OnFailed,
|
back_notification::PrivacyModeState::PrvOnFailed,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1337,7 +1364,7 @@ impl Connection {
|
|||||||
BoolOption::No => {
|
BoolOption::No => {
|
||||||
let msg_out = if !video_service::is_privacy_mode_supported() {
|
let msg_out = if !video_service::is_privacy_mode_supported() {
|
||||||
crate::common::make_privacy_mode_msg(
|
crate::common::make_privacy_mode_msg(
|
||||||
back_notification::PrivacyModeState::NotSupported,
|
back_notification::PrivacyModeState::PrvNotSupported,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
video_service::set_privacy_mode_conn_id(0);
|
video_service::set_privacy_mode_conn_id(0);
|
||||||
@ -1512,19 +1539,19 @@ mod privacy_mode {
|
|||||||
let res = turn_off_privacy(_conn_id, None);
|
let res = turn_off_privacy(_conn_id, None);
|
||||||
match res {
|
match res {
|
||||||
Ok(_) => crate::common::make_privacy_mode_msg(
|
Ok(_) => crate::common::make_privacy_mode_msg(
|
||||||
back_notification::PrivacyModeState::OffSucceeded,
|
back_notification::PrivacyModeState::PrvOffSucceeded,
|
||||||
),
|
),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
log::error!("Failed to turn off privacy mode {}", e);
|
log::error!("Failed to turn off privacy mode {}", e);
|
||||||
crate::common::make_privacy_mode_msg(
|
crate::common::make_privacy_mode_msg(
|
||||||
back_notification::PrivacyModeState::OffFailed,
|
back_notification::PrivacyModeState::PrvOffFailed,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[cfg(not(windows))]
|
#[cfg(not(windows))]
|
||||||
{
|
{
|
||||||
crate::common::make_privacy_mode_msg(back_notification::PrivacyModeState::OffFailed)
|
crate::common::make_privacy_mode_msg(back_notification::PrivacyModeState::PrvOffFailed)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -378,7 +378,6 @@ fn run(sp: GenericService) -> ResultType<()> {
|
|||||||
let mut c = get_capturer(true)?;
|
let mut c = get_capturer(true)?;
|
||||||
|
|
||||||
let mut video_qos = VIDEO_QOS.lock().unwrap();
|
let mut video_qos = VIDEO_QOS.lock().unwrap();
|
||||||
|
|
||||||
video_qos.set_size(c.width as _, c.height as _);
|
video_qos.set_size(c.width as _, c.height as _);
|
||||||
let mut spf = video_qos.spf();
|
let mut spf = video_qos.spf();
|
||||||
let bitrate = video_qos.generate_bitrate()?;
|
let bitrate = video_qos.generate_bitrate()?;
|
||||||
@ -441,18 +440,17 @@ fn run(sp: GenericService) -> ResultType<()> {
|
|||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
check_uac_switch(c.privacy_mode_id, c._captuerer_privacy_mode_id)?;
|
check_uac_switch(c.privacy_mode_id, c._captuerer_privacy_mode_id)?;
|
||||||
|
|
||||||
{
|
let mut video_qos = VIDEO_QOS.lock().unwrap();
|
||||||
let mut video_qos = VIDEO_QOS.lock().unwrap();
|
if video_qos.check_if_updated() {
|
||||||
if video_qos.check_if_updated() {
|
log::debug!(
|
||||||
log::debug!(
|
"qos is updated, target_bitrate:{}, fps:{}",
|
||||||
"qos is updated, target_bitrate:{}, fps:{}",
|
video_qos.target_bitrate,
|
||||||
video_qos.target_bitrate,
|
video_qos.fps
|
||||||
video_qos.fps
|
);
|
||||||
);
|
encoder.set_bitrate(video_qos.target_bitrate).unwrap();
|
||||||
encoder.set_bitrate(video_qos.target_bitrate).unwrap();
|
spf = video_qos.spf();
|
||||||
spf = video_qos.spf();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
drop(video_qos);
|
||||||
|
|
||||||
if *SWITCH.lock().unwrap() {
|
if *SWITCH.lock().unwrap() {
|
||||||
bail!("SWITCH");
|
bail!("SWITCH");
|
||||||
@ -585,7 +583,7 @@ fn check_privacy_mode_changed(sp: &GenericService, privacy_mode_id: i32) -> Resu
|
|||||||
if privacy_mode_id != privacy_mode_id_2 {
|
if privacy_mode_id != privacy_mode_id_2 {
|
||||||
if privacy_mode_id_2 != 0 {
|
if privacy_mode_id_2 != 0 {
|
||||||
let msg_out = crate::common::make_privacy_mode_msg(
|
let msg_out = crate::common::make_privacy_mode_msg(
|
||||||
back_notification::PrivacyModeState::OnByOther,
|
back_notification::PrivacyModeState::PrvOnByOther,
|
||||||
);
|
);
|
||||||
sp.send_to_others(msg_out, privacy_mode_id_2);
|
sp.send_to_others(msg_out, privacy_mode_id_2);
|
||||||
}
|
}
|
||||||
@ -646,7 +644,7 @@ pub fn handle_one_frame_encoded(
|
|||||||
})?;
|
})?;
|
||||||
let mut send_conn_ids: HashSet<i32> = Default::default();
|
let mut send_conn_ids: HashSet<i32> = Default::default();
|
||||||
let vp9_frame = EncodedVideoFrame {
|
let vp9_frame = EncodedVideoFrame {
|
||||||
data: frame.to_vec(),
|
data: frame.to_vec().into(),
|
||||||
key: true,
|
key: true,
|
||||||
pts: ms,
|
pts: ms,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
|
95
src/ui.rs
95
src/ui.rs
@ -133,10 +133,16 @@ pub fn start(args: &mut [String]) {
|
|||||||
let mut iter = args.iter();
|
let mut iter = args.iter();
|
||||||
let cmd = iter.next().unwrap().clone();
|
let cmd = iter.next().unwrap().clone();
|
||||||
let id = iter.next().unwrap().clone();
|
let id = iter.next().unwrap().clone();
|
||||||
|
let pass = iter.next().unwrap_or(&"".to_owned()).clone();
|
||||||
let args: Vec<String> = iter.map(|x| x.clone()).collect();
|
let args: Vec<String> = iter.map(|x| x.clone()).collect();
|
||||||
frame.set_title(&id);
|
frame.set_title(&id);
|
||||||
frame.register_behavior("native-remote", move || {
|
frame.register_behavior("native-remote", move || {
|
||||||
Box::new(remote::Handler::new(cmd.clone(), id.clone(), args.clone()))
|
Box::new(remote::Handler::new(
|
||||||
|
cmd.clone(),
|
||||||
|
id.clone(),
|
||||||
|
pass.clone(),
|
||||||
|
args.clone(),
|
||||||
|
))
|
||||||
});
|
});
|
||||||
page = "remote.html";
|
page = "remote.html";
|
||||||
} else {
|
} else {
|
||||||
@ -187,16 +193,20 @@ impl UI {
|
|||||||
ipc::get_id()
|
ipc::get_id()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_random_password(&self) -> String {
|
fn temporary_password(&mut self) -> String {
|
||||||
ipc::get_random_password()
|
self.5.lock().unwrap().clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_random_password(&self) {
|
fn update_temporary_password(&self) {
|
||||||
allow_err!(ipc::set_random_password(Config::get_auto_password()));
|
allow_err!(ipc::update_temporary_password());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_security_password(&self, password: String) {
|
fn permanent_password(&self) -> String {
|
||||||
allow_err!(ipc::set_security_password(password));
|
ipc::get_permanent_password()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_permanent_password(&self, password: String) {
|
||||||
|
allow_err!(ipc::set_permanent_password(password));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_remote_id(&mut self) -> String {
|
fn get_remote_id(&mut self) -> String {
|
||||||
@ -775,54 +785,6 @@ impl UI {
|
|||||||
fn get_langs(&self) -> String {
|
fn get_langs(&self) -> String {
|
||||||
crate::lang::LANGS.to_string()
|
crate::lang::LANGS.to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn random_password_update_method(&self) -> String {
|
|
||||||
ipc::random_password_update_method()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_random_password_update_method(&self, method: String) {
|
|
||||||
allow_err!(ipc::set_random_password_update_method(method));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_random_password_enabled(&self) -> bool {
|
|
||||||
ipc::is_random_password_enabled()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_random_password_enabled(&self, enabled: bool) {
|
|
||||||
allow_err!(ipc::set_random_password_enabled(enabled));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_security_password_enabled(&self) -> bool {
|
|
||||||
ipc::is_security_password_enabled()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_security_password_enabled(&self, enabled: bool) {
|
|
||||||
allow_err!(ipc::set_security_password_enabled(enabled));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_onetime_password_enabled(&self) -> bool {
|
|
||||||
ipc::is_onetime_password_enabled()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_onetime_password_enabled(&self, enabled: bool) {
|
|
||||||
allow_err!(ipc::set_onetime_password_enabled(enabled));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_onetime_password_activated(&self) -> bool {
|
|
||||||
ipc::is_onetime_password_activated()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_onetime_password_activated(&self, activated: bool) {
|
|
||||||
allow_err!(ipc::set_onetime_password_activated(activated));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_random_password_valid(&self) -> bool {
|
|
||||||
ipc::is_random_password_valid()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn password_description(&mut self) -> String {
|
|
||||||
self.5.lock().unwrap().clone()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl sciter::EventHandler for UI {
|
impl sciter::EventHandler for UI {
|
||||||
@ -832,9 +794,10 @@ impl sciter::EventHandler for UI {
|
|||||||
fn is_xfce();
|
fn is_xfce();
|
||||||
fn using_public_server();
|
fn using_public_server();
|
||||||
fn get_id();
|
fn get_id();
|
||||||
fn get_random_password();
|
fn temporary_password();
|
||||||
fn update_random_password();
|
fn update_temporary_password();
|
||||||
fn set_security_password(String);
|
fn permanent_password();
|
||||||
|
fn set_permanent_password(String);
|
||||||
fn get_remote_id();
|
fn get_remote_id();
|
||||||
fn set_remote_id(String);
|
fn set_remote_id(String);
|
||||||
fn closing(i32, i32, i32, i32);
|
fn closing(i32, i32, i32, i32);
|
||||||
@ -904,18 +867,6 @@ impl sciter::EventHandler for UI {
|
|||||||
fn get_uuid();
|
fn get_uuid();
|
||||||
fn has_hwcodec();
|
fn has_hwcodec();
|
||||||
fn get_langs();
|
fn get_langs();
|
||||||
fn random_password_update_method();
|
|
||||||
fn set_random_password_update_method(String);
|
|
||||||
fn is_random_password_enabled();
|
|
||||||
fn set_random_password_enabled(bool);
|
|
||||||
fn is_security_password_enabled();
|
|
||||||
fn set_security_password_enabled(bool);
|
|
||||||
fn is_onetime_password_enabled();
|
|
||||||
fn set_onetime_password_enabled(bool);
|
|
||||||
fn is_onetime_password_activated();
|
|
||||||
fn set_onetime_password_activated(bool);
|
|
||||||
fn is_random_password_valid();
|
|
||||||
fn password_description();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -982,7 +933,7 @@ async fn check_connect_status_(
|
|||||||
Ok(Some(ipc::Data::Config((name, Some(value))))) => {
|
Ok(Some(ipc::Data::Config((name, Some(value))))) => {
|
||||||
if name == "id" {
|
if name == "id" {
|
||||||
id = value;
|
id = value;
|
||||||
} else if name == ipc::STR_PASSWORD_DESCRIPTION {
|
} else if name == "temporary-password" {
|
||||||
*password.lock().unwrap() = value;
|
*password.lock().unwrap() = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1003,7 +954,7 @@ async fn check_connect_status_(
|
|||||||
c.send(&ipc::Data::OnlineStatus(None)).await.ok();
|
c.send(&ipc::Data::OnlineStatus(None)).await.ok();
|
||||||
c.send(&ipc::Data::Options(None)).await.ok();
|
c.send(&ipc::Data::Options(None)).await.ok();
|
||||||
c.send(&ipc::Data::Config(("id".to_owned(), None))).await.ok();
|
c.send(&ipc::Data::Config(("id".to_owned(), None))).await.ok();
|
||||||
c.send(&ipc::Data::Config((ipc::STR_PASSWORD_DESCRIPTION.to_owned(), None))).await.ok();
|
c.send(&ipc::Data::Config(("temporary-password".to_owned(), None))).await.ok();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -104,6 +104,10 @@ icon.file {
|
|||||||
background:url('data: image/png;base64, iVBORw0KGgoAAAANSUhEUgAAAGAAAABgCAMAAADVRocKAAAAUVBMVEUAAAD///////////////////////////////////////////////////////////////////////////////////////////////////////8IN+deAAAAGnRSTlMAH+CAESEN8jyZkcIb5N/ONy3vmHhmiGjUm7UwS+YAAAHZSURBVGje7dnbboMwDIBhBwgQoFAO7Ta//4NOqCAXYZQstatq4r+r5ubrgQSpg8iyC4ZURa+PlIpQYGiwrzyeHtYZjAL8T05O4H8BbbKvFgRa4NoBU8pXeYEkDDgaaLQBcwJrmeErJQB/7wes3QBWGnCIX0+AQycL1PO6BMwPa0nA4ZxbgTvOjUYMGPHRnZkQAY4mxPZBjmy53E7ukSkFKYB/D4XsWZQx64sCeYebOogGsoOBYvv6/UCb8F0IOBZ0TlP6lEYdANY350AJqB9/qPVuOI5evw4A1hgLigAlepnyxW80bcCcwN++A2s82Vcu02ta+ceq9BoL5KGTTRwQPlpqA3gCnwWU2kCDgeWRQPj2jAPCDxgCMjhI6uZnToDpvd/BJeFrJQB/fsAa02gCt3mi1wNuy8GgBNDZlysBNNSrADVSjcJl6vCpUn6jOdx0kz0q6PMhQRa4465SFKhx35cgUCBTwj2/NHwZAb71qR8GEP2H1XcmAtBPTEO67GP6FUUAIKGABbDLQ0EArhN2sAIGesRO+iyy+RMAjckVTlMCKFVAbh/4Af9OPgG61SkDVco3BQGT3GXaDAnTIAcYZDuBTwGsAGDxuBFeAQqIqwoFMlAVLrHr/wId5MPt0nilGgAAAABJRU5ErkJggg==');
|
background:url('data: image/png;base64, iVBORw0KGgoAAAANSUhEUgAAAGAAAABgCAMAAADVRocKAAAAUVBMVEUAAAD///////////////////////////////////////////////////////////////////////////////////////////////////////8IN+deAAAAGnRSTlMAH+CAESEN8jyZkcIb5N/ONy3vmHhmiGjUm7UwS+YAAAHZSURBVGje7dnbboMwDIBhBwgQoFAO7Ta//4NOqCAXYZQstatq4r+r5ubrgQSpg8iyC4ZURa+PlIpQYGiwrzyeHtYZjAL8T05O4H8BbbKvFgRa4NoBU8pXeYEkDDgaaLQBcwJrmeErJQB/7wes3QBWGnCIX0+AQycL1PO6BMwPa0nA4ZxbgTvOjUYMGPHRnZkQAY4mxPZBjmy53E7ukSkFKYB/D4XsWZQx64sCeYebOogGsoOBYvv6/UCb8F0IOBZ0TlP6lEYdANY350AJqB9/qPVuOI5evw4A1hgLigAlepnyxW80bcCcwN++A2s82Vcu02ta+ceq9BoL5KGTTRwQPlpqA3gCnwWU2kCDgeWRQPj2jAPCDxgCMjhI6uZnToDpvd/BJeFrJQB/fsAa02gCt3mi1wNuy8GgBNDZlysBNNSrADVSjcJl6vCpUn6jOdx0kz0q6PMhQRa4465SFKhx35cgUCBTwj2/NHwZAb71qR8GEP2H1XcmAtBPTEO67GP6FUUAIKGABbDLQ0EArhN2sAIGesRO+iyy+RMAjckVTlMCKFVAbh/4Af9OPgG61SkDVco3BQGT3GXaDAnTIAcYZDuBTwGsAGDxuBFeAQqIqwoFMlAVLrHr/wId5MPt0nilGgAAAABJRU5ErkJggg==');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
icon.restart {
|
||||||
|
background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAACXBIWXMAAB7BAAAewQHDaVRTAAAAGXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAAAbhJREFUWIXVlrFqFGEUhb+7UYxaWCQKlrKKxaZSQVGDJih2tj6MD2DnMwiWvoAIRnENIpZiYxEro6IooiS7SPwsMgNLkk3mjmYmnmb45/73nMNwz/x/qH3gMu2gH6rAU+Blw+Lngau4jpmGxVF7qp1iPWjaQKnZ2WnXbuP/NqAeUPc3ZkA9XDwvqc+BVWCgPlJ7tRwUKThZce819b46VH+pfXVRXVO/q2cSul3VOgZUl0ejq86r39TXI8mqZKDuDEwCw3IREQvAbWAGmMsQZQ0sAl3gHPB1Q+0e8BuYzRDuy2yOiFVgaUxtRf0ETGc4syk4rc6PqU0Cx9j8Zf6dAeAK8Fi9sUXtFjABvEgxJlNwRP2svlNPjbw/q35U36oTFbnyMSwabxb/gB/qA3VBHagrauV7RW0DRfP1IvMlXqkXkhz1DYyQTKtHa/Z2VVMx3IiI+PI3/bCHjuOpFrSnAMpL6QfgTcMGesDx0kBr2BMzsNyi/vtQu8CJlgwsRbZDnWP90NkKaxHxJMOXMqAeAn5u0ydwMCKGY+qbkB3C2W3EKWoXk5zVoHbUZ+6Mh7tl4G4F8RJ3qvL+AfV3r5Vdpj70AAAAAElFTkSuQmCC');
|
||||||
|
}
|
||||||
|
|
||||||
div.buttons {
|
div.buttons {
|
||||||
width: *;
|
width: *;
|
||||||
border-spacing: 0.5em;
|
border-spacing: 0.5em;
|
||||||
|
14
src/ui/cm.rs
14
src/ui/cm.rs
@ -14,7 +14,7 @@ use hbb_common::{
|
|||||||
fs, get_version_number, log,
|
fs, get_version_number, log,
|
||||||
message_proto::*,
|
message_proto::*,
|
||||||
protobuf::Message as _,
|
protobuf::Message as _,
|
||||||
tokio::{self, sync::mpsc, task::spawn_blocking}
|
tokio::{self, sync::mpsc, task::spawn_blocking},
|
||||||
};
|
};
|
||||||
use sciter::{make_args, Element, Value, HELEMENT};
|
use sciter::{make_args, Element, Value, HELEMENT};
|
||||||
use std::{
|
use std::{
|
||||||
@ -90,6 +90,7 @@ impl ConnectionManager {
|
|||||||
clipboard: bool,
|
clipboard: bool,
|
||||||
audio: bool,
|
audio: bool,
|
||||||
file: bool,
|
file: bool,
|
||||||
|
restart: bool,
|
||||||
tx: mpsc::UnboundedSender<Data>,
|
tx: mpsc::UnboundedSender<Data>,
|
||||||
) {
|
) {
|
||||||
self.call(
|
self.call(
|
||||||
@ -104,7 +105,8 @@ impl ConnectionManager {
|
|||||||
keyboard,
|
keyboard,
|
||||||
clipboard,
|
clipboard,
|
||||||
audio,
|
audio,
|
||||||
file
|
file,
|
||||||
|
restart
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
self.write().unwrap().senders.insert(id, tx);
|
self.write().unwrap().senders.insert(id, tx);
|
||||||
@ -158,8 +160,8 @@ impl ConnectionManager {
|
|||||||
id,
|
id,
|
||||||
file_num,
|
file_num,
|
||||||
mut files,
|
mut files,
|
||||||
|
overwrite_detection
|
||||||
} => {
|
} => {
|
||||||
let od = can_enable_overwrite_detection(get_version_number(VERSION));
|
|
||||||
// cm has no show_hidden context
|
// cm has no show_hidden context
|
||||||
// dummy remote, show_hidden, is_remote
|
// dummy remote, show_hidden, is_remote
|
||||||
write_jobs.push(fs::TransferJob::new_write(
|
write_jobs.push(fs::TransferJob::new_write(
|
||||||
@ -177,7 +179,7 @@ impl ConnectionManager {
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
})
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
od,
|
overwrite_detection,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
ipc::FS::CancelWrite { id } => {
|
ipc::FS::CancelWrite { id } => {
|
||||||
@ -489,11 +491,11 @@ async fn start_ipc(cm: ConnectionManager) {
|
|||||||
}
|
}
|
||||||
Ok(Some(data)) => {
|
Ok(Some(data)) => {
|
||||||
match data {
|
match data {
|
||||||
Data::Login{id, is_file_transfer, port_forward, peer_id, name, authorized, keyboard, clipboard, audio, file, file_transfer_enabled} => {
|
Data::Login{id, is_file_transfer, port_forward, peer_id, name, authorized, keyboard, clipboard, audio, file, file_transfer_enabled, restart} => {
|
||||||
log::debug!("conn_id: {}", id);
|
log::debug!("conn_id: {}", id);
|
||||||
conn_id = id;
|
conn_id = id;
|
||||||
tx_file.send(ClipboardFileData::Enable((id, file_transfer_enabled))).ok();
|
tx_file.send(ClipboardFileData::Enable((id, file_transfer_enabled))).ok();
|
||||||
cm.add_connection(id, is_file_transfer, port_forward, peer_id, name, authorized, keyboard, clipboard, audio, file, tx.clone());
|
cm.add_connection(id, is_file_transfer, port_forward, peer_id, name, authorized, keyboard, clipboard, audio, file, restart, tx.clone());
|
||||||
}
|
}
|
||||||
Data::Close => {
|
Data::Close => {
|
||||||
tx_file.send(ClipboardFileData::Enable((conn_id, false))).ok();
|
tx_file.send(ClipboardFileData::Enable((conn_id, false))).ok();
|
||||||
|
@ -46,6 +46,7 @@ class Body: Reactor.Component
|
|||||||
<div class={!c.clipboard ? "disabled" : ""} title={translate('Allow using clipboard')}><icon .clipboard /></div>
|
<div class={!c.clipboard ? "disabled" : ""} title={translate('Allow using clipboard')}><icon .clipboard /></div>
|
||||||
<div class={!c.audio ? "disabled" : ""} title={translate('Allow hearing sound')}><icon .audio /></div>
|
<div class={!c.audio ? "disabled" : ""} title={translate('Allow hearing sound')}><icon .audio /></div>
|
||||||
<div class={!c.file ? "disabled" : ""} title={translate('Allow file copy and paste')}><icon .file /></div>
|
<div class={!c.file ? "disabled" : ""} title={translate('Allow file copy and paste')}><icon .file /></div>
|
||||||
|
<div class={!c.restart ? "disabled" : ""} title={translate('Allow remote restart')}><icon .restart /></div>
|
||||||
</div>}
|
</div>}
|
||||||
{c.port_forward ? <div>Port Forwarding: {c.port_forward}</div> : ""}
|
{c.port_forward ? <div>Port Forwarding: {c.port_forward}</div> : ""}
|
||||||
<div style="size:*"/>
|
<div style="size:*"/>
|
||||||
@ -108,6 +109,15 @@ class Body: Reactor.Component
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
event click $(icon.restart) {
|
||||||
|
var { cid, connection } = this;
|
||||||
|
checkClickTime(function() {
|
||||||
|
connection.restart = !connection.restart;
|
||||||
|
body.update();
|
||||||
|
handler.switch_permission(cid, "restart", connection.restart);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
event click $(button#accept) {
|
event click $(button#accept) {
|
||||||
var { cid, connection } = this;
|
var { cid, connection } = this;
|
||||||
checkClickTime(function() {
|
checkClickTime(function() {
|
||||||
@ -266,7 +276,7 @@ function bring_to_top(idx=-1) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handler.addConnection = function(id, is_file_transfer, port_forward, peer_id, name, authorized, keyboard, clipboard, audio, file) {
|
handler.addConnection = function(id, is_file_transfer, port_forward, peer_id, name, authorized, keyboard, clipboard, audio, file, restart) {
|
||||||
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) {
|
||||||
@ -283,7 +293,7 @@ handler.addConnection = function(id, is_file_transfer, port_forward, peer_id, na
|
|||||||
port_forward: port_forward,
|
port_forward: port_forward,
|
||||||
name: name, authorized: authorized, time: new Date(),
|
name: name, authorized: authorized, time: new Date(),
|
||||||
keyboard: keyboard, clipboard: clipboard, msgs: [], unreaded: 0,
|
keyboard: keyboard, clipboard: clipboard, msgs: [], unreaded: 0,
|
||||||
audio: audio, file: file
|
audio: audio, file: file, restart: restart
|
||||||
});
|
});
|
||||||
body.cur = connections.length - 1;
|
body.cur = connections.length - 1;
|
||||||
bring_to_top();
|
bring_to_top();
|
||||||
|
@ -199,6 +199,7 @@ class Header: Reactor.Component {
|
|||||||
{handler.get_audit_server() && <li #note>{translate('Note')}</li>}
|
{handler.get_audit_server() && <li #note>{translate('Note')}</li>}
|
||||||
<div .separator />
|
<div .separator />
|
||||||
{keyboard_enabled && (pi.platform == "Linux" || pi.sas_enabled) ? <li #ctrl-alt-del>{translate('Insert')} Ctrl + Alt + Del</li> : ""}
|
{keyboard_enabled && (pi.platform == "Linux" || pi.sas_enabled) ? <li #ctrl-alt-del>{translate('Insert')} Ctrl + Alt + Del</li> : ""}
|
||||||
|
{restart_enabled && (pi.platform == "Linux" || pi.platform == "Windows" || pi.platform == "Mac OS") ? <li #restart_remote_device>{translate('Restart Remote Device')}</li> : ""}
|
||||||
{keyboard_enabled ? <li #lock-screen>{translate('Insert Lock')}</li> : ""}
|
{keyboard_enabled ? <li #lock-screen>{translate('Insert Lock')}</li> : ""}
|
||||||
{keyboard_enabled && pi.platform == "Windows" && pi.sas_enabled ? <li #block-input>{translate("Block user input")}</li> : ""}
|
{keyboard_enabled && pi.platform == "Windows" && pi.sas_enabled ? <li #block-input>{translate("Block user input")}</li> : ""}
|
||||||
<li #refresh>{translate('Refresh')}</li>
|
<li #refresh>{translate('Refresh')}</li>
|
||||||
@ -317,6 +318,12 @@ class Header: Reactor.Component {
|
|||||||
handler.ctrl_alt_del();
|
handler.ctrl_alt_del();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
event click $(#restart_remote_device) {
|
||||||
|
msgbox("restart-confirmation", translate("Restart Remote Device"), translate("Are you sure you want to restart") + " " + pi.username + "@" + pi.hostname + "(" + get_id() + ") ?", function(res=null) {
|
||||||
|
if (res != null) handler.restart_remote_device();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
event click $(#lock-screen) {
|
event click $(#lock-screen) {
|
||||||
handler.lock_screen();
|
handler.lock_screen();
|
||||||
}
|
}
|
||||||
|
190
src/ui/index.tis
190
src/ui/index.tis
@ -3,7 +3,7 @@ stdout.println("current platform:", OS);
|
|||||||
stdout.println("is_xfce: ", is_xfce);
|
stdout.println("is_xfce: ", is_xfce);
|
||||||
|
|
||||||
// html min-width, min-height not working on mac, below works for all
|
// html min-width, min-height not working on mac, below works for all
|
||||||
view.windowMinSize = (scaleIt(500), scaleIt(300));
|
view.windowMinSize = (scaleIt(560), scaleIt(300));
|
||||||
|
|
||||||
var app;
|
var app;
|
||||||
var tmp = handler.get_connect_status();
|
var tmp = handler.get_connect_status();
|
||||||
@ -212,8 +212,8 @@ class Enhancements: Reactor.Component {
|
|||||||
self.timer(1ms, function() { me.toggleMenuState() });
|
self.timer(1ms, function() { me.toggleMenuState() });
|
||||||
return <li>{translate('Enhancements')}
|
return <li>{translate('Enhancements')}
|
||||||
<menu #enhancements-menu>
|
<menu #enhancements-menu>
|
||||||
{has_hwcodec ? <li #enable-hwcodec><span>{svg_checkmark}</span>{translate("Hardware Codec")}{"(beta)"}</li> : ""}
|
{has_hwcodec ? <li #enable-hwcodec><span>{svg_checkmark}</span>{translate("Hardware Codec")} (beta)</li> : ""}
|
||||||
<li #enable-abr><span>{svg_checkmark}</span>{translate("Adaptive Bitrate")}{"(beta)"}</li>
|
<li #enable-abr><span>{svg_checkmark}</span>{translate("Adaptive Bitrate")} (beta)</li>
|
||||||
</menu>
|
</menu>
|
||||||
</li>;
|
</li>;
|
||||||
}
|
}
|
||||||
@ -274,6 +274,7 @@ class MyIdMenu: Reactor.Component {
|
|||||||
<li #enable-keyboard><span>{svg_checkmark}</span>{translate('Enable Keyboard/Mouse')}</li>
|
<li #enable-keyboard><span>{svg_checkmark}</span>{translate('Enable Keyboard/Mouse')}</li>
|
||||||
<li #enable-clipboard><span>{svg_checkmark}</span>{translate('Enable Clipboard')}</li>
|
<li #enable-clipboard><span>{svg_checkmark}</span>{translate('Enable Clipboard')}</li>
|
||||||
<li #enable-file-transfer><span>{svg_checkmark}</span>{translate('Enable File Transfer')}</li>
|
<li #enable-file-transfer><span>{svg_checkmark}</span>{translate('Enable File Transfer')}</li>
|
||||||
|
<li #enable-remote-restart><span>{svg_checkmark}</span>{translate('Enable Remote Restart')}</li>
|
||||||
<li #enable-tunnel><span>{svg_checkmark}</span>{translate('Enable TCP Tunneling')}</li>
|
<li #enable-tunnel><span>{svg_checkmark}</span>{translate('Enable TCP Tunneling')}</li>
|
||||||
<AudioInputs />
|
<AudioInputs />
|
||||||
<Enhancements />
|
<Enhancements />
|
||||||
@ -529,9 +530,7 @@ class App: Reactor.Component
|
|||||||
<MyIdMenu />
|
<MyIdMenu />
|
||||||
{key_confirmed ? <input type="text" readonly value={formatId(get_id())}/> : translate("Generating ...")}
|
{key_confirmed ? <input type="text" readonly value={formatId(get_id())}/> : translate("Generating ...")}
|
||||||
</div>
|
</div>
|
||||||
<div .your-desktop>
|
<PasswordArea />
|
||||||
<PasswordArea />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
{!is_win || handler.is_installed() ? "": <InstallMe />}
|
{!is_win || handler.is_installed() ? "": <InstallMe />}
|
||||||
{software_update_url ? <UpdateMe /> : ""}
|
{software_update_url ? <UpdateMe /> : ""}
|
||||||
@ -802,8 +801,8 @@ function watch_screen_recording() {
|
|||||||
|
|
||||||
class PasswordEyeArea : Reactor.Component {
|
class PasswordEyeArea : Reactor.Component {
|
||||||
render() {
|
render() {
|
||||||
var show = handler.is_random_password_valid();
|
var method = handler.get_option('verification-method');
|
||||||
var value = show ? handler.get_random_password() : "-";
|
var value = method != 'use-permanent-password' ? password_cache[0] : "-";
|
||||||
return
|
return
|
||||||
<div .eye-area style="width: *">
|
<div .eye-area style="width: *">
|
||||||
<input|text @{this.input} readonly value={value} />
|
<input|text @{this.input} readonly value={value} />
|
||||||
@ -812,95 +811,49 @@ class PasswordEyeArea : Reactor.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
event click $(svg#refresh-password) (_, me) {
|
event click $(svg#refresh-password) (_, me) {
|
||||||
if (handler.is_random_password_valid()) handler.update_random_password();
|
handler.update_temporary_password();
|
||||||
this.update();
|
this.update();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var verificationMethodMenu;
|
var temporaryPasswordLengthMenu;
|
||||||
class VerificationMethodMenu: Reactor.Component {
|
class TemporaryPasswordLengthMenu: Reactor.Component {
|
||||||
function this() {
|
function this() {
|
||||||
verificationMethodMenu = this;
|
temporaryPasswordLengthMenu = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
function render() {
|
function render() {
|
||||||
if (!this.show) return <li />;
|
if (!this.show) return <li />;
|
||||||
var me = this;
|
var me = this;
|
||||||
|
var method = handler.get_option('verification-method');
|
||||||
self.timer(1ms, function() { me.toggleMenuState() });
|
self.timer(1ms, function() { me.toggleMenuState() });
|
||||||
return <li>{translate('Verification Method')}
|
return <li disabled={ method == 'use-permanent-password' ? "true" : "false" }>{translate("Set temporary password length")}
|
||||||
<menu #verification-method>
|
<menu #temporary-password-length>
|
||||||
<li #verification-method-security><span>{svg_checkmark}</span>{translate('Enable security password')}</li>
|
<li #temporary-password-length-6><span>{svg_checkmark}</span>6</li>
|
||||||
<li #verification-method-random><span>{svg_checkmark}</span>{translate('Enable random password')}</li>
|
<li #temporary-password-length-8><span>{svg_checkmark}</span>8</li>
|
||||||
|
<li #temporary-password-length-10><span>{svg_checkmark}</span>10</li>
|
||||||
</menu>
|
</menu>
|
||||||
</li>;
|
</li>;
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggleMenuState() {
|
function toggleMenuState() {
|
||||||
var security_enabled = handler.is_security_password_enabled();
|
var length = handler.get_option("temporary-password-length");
|
||||||
var random_enabled = handler.is_random_password_enabled();
|
var index = ['6', '8', '10'].indexOf(length);
|
||||||
var onetime_enabled = handler.is_onetime_password_enabled();
|
if (index < 0) index = 0;
|
||||||
for (var (index, el) in this.$$(menu#verification-method>li)) {
|
for (var (i, el) in this.$$(menu#temporary-password-length>li)) {
|
||||||
if (index == 0) el.attributes.toggleClass("selected", security_enabled);
|
el.attributes.toggleClass("selected", i == index);
|
||||||
if (index == 1) el.attributes.toggleClass("selected", random_enabled);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
event click $(menu#verification-method>li) (_, me) {
|
event click $(menu#temporary-password-length>li) (_, me) {
|
||||||
switch (me.id.substring('verification-method-'.length)) {
|
var length = me.id.substring('temporary-password-length-'.length);
|
||||||
case 'security':
|
var old_length = handler.get_option('temporary-password-length');
|
||||||
{
|
if (length != old_length) {
|
||||||
var security_enabled = handler.is_security_password_enabled();
|
handler.set_option('temporary-password-length', length);
|
||||||
handler.set_security_password_enabled(!security_enabled);
|
handler.update_temporary_password();
|
||||||
}
|
this.toggleMenuState();
|
||||||
break;
|
passwordArea.update();
|
||||||
case 'random':
|
|
||||||
{
|
|
||||||
var random_enabled = handler.is_random_password_enabled();
|
|
||||||
handler.set_random_password_enabled(!random_enabled);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.toggleMenuState();
|
|
||||||
passwordArea.update();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var randomPasswordUpdateMethodMenu;
|
|
||||||
class RandomPasswordUpdateMethodMenu: Reactor.Component {
|
|
||||||
function this() {
|
|
||||||
randomPasswordUpdateMethodMenu = this;
|
|
||||||
}
|
|
||||||
|
|
||||||
function render() {
|
|
||||||
if (!this.show) return <li />;
|
|
||||||
var me = this;
|
|
||||||
var random_enabled = handler.is_random_password_enabled();
|
|
||||||
self.timer(1ms, function() { me.toggleMenuState() });
|
|
||||||
return <li disabled={ random_enabled ? "false" : "true" }>{translate('Random Password After Session')}
|
|
||||||
<menu #random-password-update-method>
|
|
||||||
<li #random-password-update-method-keep><span>{svg_checkmark}</span>{translate('Keep')}</li>
|
|
||||||
<li #random-password-update-method-update><span>{svg_checkmark}</span>{translate('Update')}</li>
|
|
||||||
<li #random-password-update-method-disable><span>{svg_checkmark}</span>{translate('Disable')}</li>
|
|
||||||
</menu>
|
|
||||||
</li>;
|
|
||||||
}
|
|
||||||
|
|
||||||
function toggleMenuState() {
|
|
||||||
var method = handler.random_password_update_method();
|
|
||||||
for (var (index, el) in this.$$(menu#random-password-update-method>li)) {
|
|
||||||
if (index == 0) el.attributes.toggleClass("selected", method == "KEEP");
|
|
||||||
if (index == 1) el.attributes.toggleClass("selected", method == "UPDATE");
|
|
||||||
if (index == 2) el.attributes.toggleClass("selected", method == "DISABLE");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
event click $(menu#random-password-update-method>li) (_, me) {
|
|
||||||
if (me.id === 'random-password-update-method-keep') handler.set_random_password_update_method("KEEP");
|
|
||||||
if (me.id === 'random-password-update-method-update') handler.set_random_password_update_method("UPDATE");
|
|
||||||
if (me.id === 'random-password-update-method-disable') handler.set_random_password_update_method("DISABLE");
|
|
||||||
this.toggleMenuState();
|
|
||||||
passwordArea.update();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -911,11 +864,11 @@ class PasswordArea: Reactor.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function render() {
|
function render() {
|
||||||
var onetime_enabled = handler.is_onetime_password_enabled();
|
var me = this;
|
||||||
|
self.timer(1ms, function() { me.toggleMenuState() });
|
||||||
return
|
return
|
||||||
<div>
|
<div .your-desktop>
|
||||||
<div>{translate(onetime_enabled ? 'Onetime Password' : 'Password')}</div>
|
<div>{translate('Password')}</div>
|
||||||
<div .password style="flow:horizontal">
|
<div .password style="flow:horizontal">
|
||||||
{this.renderPop()}
|
{this.renderPop()}
|
||||||
<PasswordEyeArea />
|
<PasswordEyeArea />
|
||||||
@ -925,35 +878,39 @@ class PasswordArea: Reactor.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function renderPop() {
|
function renderPop() {
|
||||||
var security_enabled = handler.is_security_password_enabled();
|
var method = handler.get_option('verification-method');
|
||||||
var random_enabled = handler.is_random_password_enabled();
|
|
||||||
var onetime_enabled = handler.is_onetime_password_enabled();
|
|
||||||
var onetime_activated = handler.is_onetime_password_activated();
|
|
||||||
|
|
||||||
return <popup><menu.context #edit-password-context>
|
return <popup><menu.context #edit-password-context>
|
||||||
<li #enable-onetime-password disabled={ random_enabled ? "false" : "true" }>{translate(onetime_enabled ? "Disable onetime password" : "Enable onetime password")}</li>
|
<li #use-temporary-password><span>{svg_checkmark}</span>{translate('Use temporary password')}</li>
|
||||||
<li #activate-onetime-password disabled={ !random_enabled || !onetime_enabled || onetime_activated ? "true" : "false" }>{translate('Activate onetime password')}</li>
|
<li #use-permanent-password><span>{svg_checkmark}</span>{translate('Use permanent password')}</li>
|
||||||
|
<li #use-both-passwords><span>{svg_checkmark}</span>{translate('Use both passwords')}</li>
|
||||||
<div .separator />
|
<div .separator />
|
||||||
<VerificationMethodMenu />
|
<li #set-password disabled={ method == 'use-temporary-password' ? "true" : "false" }>{translate('Set permanent password')}</li>
|
||||||
<div .separator />
|
<TemporaryPasswordLengthMenu />
|
||||||
<li #set-password disabled={ security_enabled ? "false" : "true" }>{translate('Set security password')}</li>
|
|
||||||
<div .separator />
|
|
||||||
<RandomPasswordUpdateMethodMenu />
|
|
||||||
</menu></popup>;
|
</menu></popup>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function toggleMenuState() {
|
||||||
|
var id = handler.get_option('verification-method');
|
||||||
|
if (id != 'use-temporary-password' && id != 'use-permanent-password')
|
||||||
|
id = 'use-both-passwords';
|
||||||
|
for (var el in [this.$(li#use-temporary-password), this.$(li#use-permanent-password), this.$(li#use-both-passwords)]) {
|
||||||
|
el.attributes.toggleClass("selected", el.id == id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
event click $(svg#edit) (_, me) {
|
event click $(svg#edit) (_, me) {
|
||||||
randomPasswordUpdateMethodMenu.update({show: true });
|
temporaryPasswordLengthMenu.update({show: true });
|
||||||
verificationMethodMenu.update({show: true });
|
|
||||||
var menu = $(menu#edit-password-context);
|
var menu = $(menu#edit-password-context);
|
||||||
me.popup(menu);
|
me.popup(menu);
|
||||||
}
|
}
|
||||||
|
|
||||||
event click $(li#set-password) {
|
event click $(li#set-password) {
|
||||||
var me = this;
|
var me = this;
|
||||||
|
var password = handler.permanent_password();
|
||||||
|
var value_field = password.length == 0 ? "" : "value=" + password;
|
||||||
msgbox("custom-password", translate("Set Password"), "<div .form .set-password> \
|
msgbox("custom-password", translate("Set Password"), "<div .form .set-password> \
|
||||||
<div><span>" + translate('Password') + ":</span><input|password(password) .outline-focus /></div> \
|
<div><span>" + translate('Password') + ":</span><input|password(password) .outline-focus " + value_field + " /></div> \
|
||||||
<div><span>" + translate('Confirmation') + ":</span><input|password(confirmation) /></div> \
|
<div><span>" + translate('Confirmation') + ":</span><input|password(confirmation) " + value_field + " /></div> \
|
||||||
</div> \
|
</div> \
|
||||||
", function(res=null) {
|
", function(res=null) {
|
||||||
if (!res) return;
|
if (!res) return;
|
||||||
@ -965,31 +922,40 @@ class PasswordArea: Reactor.Component {
|
|||||||
if (p0 != p1) {
|
if (p0 != p1) {
|
||||||
return translate("The confirmation is not identical.");
|
return translate("The confirmation is not identical.");
|
||||||
}
|
}
|
||||||
handler.set_security_password(p0);
|
handler.set_permanent_password(p0);
|
||||||
me.update();
|
me.update();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
event click $(li#enable-onetime-password) {
|
event click $(menu#edit-password-context>li) (_, me) {
|
||||||
var onetime_enabled = handler.is_onetime_password_enabled();
|
if (me.id.indexOf('use-') == 0) {
|
||||||
handler.set_onetime_password_enabled(!onetime_enabled);
|
handler.set_option('verification-method', me.id);
|
||||||
passwordArea.update();
|
this.toggleMenuState();
|
||||||
}
|
passwordArea.update();
|
||||||
|
}
|
||||||
event click $(li#activate-onetime-password) {
|
|
||||||
handler.set_onetime_password_activated(true);
|
|
||||||
passwordArea.update();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var last_password_description = "";
|
var password_cache = ["","",""];
|
||||||
function updatePasswordArea() {
|
function updatePasswordArea() {
|
||||||
self.timer(1s, function() {
|
self.timer(1s, function() {
|
||||||
var description = handler.password_description();
|
var temporary_password = handler.temporary_password();
|
||||||
if (last_password_description != description) {
|
var verification_method = handler.get_option('verification-method');
|
||||||
last_password_description = description
|
var temporary_password_length = handler.get_option('temporary-password-length');
|
||||||
passwordArea.update();
|
var update = false;
|
||||||
|
if (password_cache[0] != temporary_password) {
|
||||||
|
password_cache[0] = temporary_password;
|
||||||
|
update = true;
|
||||||
}
|
}
|
||||||
|
if (password_cache[1] != verification_method) {
|
||||||
|
password_cache[1] = verification_method;
|
||||||
|
update = true;
|
||||||
|
}
|
||||||
|
if (password_cache[2] != temporary_password_length) {
|
||||||
|
password_cache[2] = temporary_password_length;
|
||||||
|
update = true;
|
||||||
|
}
|
||||||
|
if (update) passwordArea.update();
|
||||||
updatePasswordArea();
|
updatePasswordArea();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -192,7 +192,7 @@ pub fn make_menubar(host: Rc<Host>, is_index: bool) {
|
|||||||
app_menu.addItem_(new_item);
|
app_menu.addItem_(new_item);
|
||||||
} else {
|
} else {
|
||||||
// When app launched without argument, is the main panel.
|
// When app launched without argument, is the main panel.
|
||||||
let about_item = make_menu_item("About", "a", SHOW_ABOUT_TAG);
|
let about_item = make_menu_item("About", "", SHOW_ABOUT_TAG);
|
||||||
app_menu.addItem_(about_item);
|
app_menu.addItem_(about_item);
|
||||||
let separator = NSMenuItem::separatorItem(nil).autorelease();
|
let separator = NSMenuItem::separatorItem(nil).autorelease();
|
||||||
app_menu.addItem_(separator);
|
app_menu.addItem_(separator);
|
||||||
|
@ -91,7 +91,7 @@ class MsgboxComponent: Reactor.Component {
|
|||||||
var color = this.getColor();
|
var color = this.getColor();
|
||||||
var icon = this.getIcon(color);
|
var icon = this.getIcon(color);
|
||||||
var content = this.getContent();
|
var content = this.getContent();
|
||||||
var hasCancel = this.type.indexOf("error") < 0 && this.type.indexOf("nocancel") < 0;
|
var hasCancel = this.type.indexOf("error") < 0 && this.type.indexOf("nocancel") < 0 && this.type != "restarting";
|
||||||
var hasOk = this.type != "connecting" && this.type != "success" && this.type.indexOf("nook") < 0;
|
var hasOk = this.type != "connecting" && this.type != "success" && this.type.indexOf("nook") < 0;
|
||||||
var hasClose = this.type.indexOf("hasclose") >= 0;
|
var hasClose = this.type.indexOf("hasclose") >= 0;
|
||||||
var show_progress = this.type == "connecting";
|
var show_progress = this.type == "connecting";
|
||||||
|
115
src/ui/remote.rs
115
src/ui/remote.rs
@ -23,10 +23,6 @@ use clipboard::{
|
|||||||
get_rx_clip_client, server_clip_file,
|
get_rx_clip_client, server_clip_file,
|
||||||
};
|
};
|
||||||
use enigo::{self, Enigo, KeyboardControllable};
|
use enigo::{self, Enigo, KeyboardControllable};
|
||||||
use hbb_common::fs::{
|
|
||||||
can_enable_overwrite_detection, get_job, get_string, new_send_confirm, DigestCheckResult,
|
|
||||||
RemoveJobMeta,
|
|
||||||
};
|
|
||||||
use hbb_common::{
|
use hbb_common::{
|
||||||
allow_err,
|
allow_err,
|
||||||
config::{Config, LocalConfig, PeerConfig},
|
config::{Config, LocalConfig, PeerConfig},
|
||||||
@ -44,6 +40,13 @@ use hbb_common::{
|
|||||||
};
|
};
|
||||||
use hbb_common::{config::TransferSerde, fs::TransferJobMeta};
|
use hbb_common::{config::TransferSerde, fs::TransferJobMeta};
|
||||||
use rdev::{Event, EventType::*, Key as RdevKey};
|
use rdev::{Event, EventType::*, Key as RdevKey};
|
||||||
|
use hbb_common::{
|
||||||
|
fs::{
|
||||||
|
can_enable_overwrite_detection, get_job, get_string, new_send_confirm, DigestCheckResult,
|
||||||
|
RemoveJobMeta,
|
||||||
|
},
|
||||||
|
get_version_number,
|
||||||
|
};
|
||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
use crate::clipboard_file::*;
|
use crate::clipboard_file::*;
|
||||||
@ -89,6 +92,7 @@ pub struct Handler {
|
|||||||
inner: Arc<RwLock<HandlerInner>>,
|
inner: Arc<RwLock<HandlerInner>>,
|
||||||
cmd: String,
|
cmd: String,
|
||||||
id: String,
|
id: String,
|
||||||
|
password: String,
|
||||||
args: Vec<String>,
|
args: Vec<String>,
|
||||||
lc: Arc<RwLock<LoginConfigHandler>>,
|
lc: Arc<RwLock<LoginConfigHandler>>,
|
||||||
}
|
}
|
||||||
@ -238,23 +242,16 @@ impl sciter::EventHandler for Handler {
|
|||||||
fn has_hwcodec();
|
fn has_hwcodec();
|
||||||
fn supported_hwcodec();
|
fn supported_hwcodec();
|
||||||
fn change_prefer_codec();
|
fn change_prefer_codec();
|
||||||
|
fn restart_remote_device();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
|
||||||
struct QualityStatus {
|
|
||||||
speed: Option<String>,
|
|
||||||
fps: Option<i32>,
|
|
||||||
delay: Option<i32>,
|
|
||||||
target_bitrate: Option<i32>,
|
|
||||||
codec_format: Option<CodecFormat>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Handler {
|
impl Handler {
|
||||||
pub fn new(cmd: String, id: String, args: Vec<String>) -> Self {
|
pub fn new(cmd: String, id: String, password: String, args: Vec<String>) -> Self {
|
||||||
let me = Self {
|
let me = Self {
|
||||||
cmd,
|
cmd,
|
||||||
id: id.clone(),
|
id: id.clone(),
|
||||||
|
password: password.clone(),
|
||||||
args,
|
args,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
@ -480,6 +477,17 @@ impl Handler {
|
|||||||
self.send(Data::Message(msg));
|
self.send(Data::Message(msg));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn restart_remote_device(&mut self) {
|
||||||
|
let mut lc = self.lc.write().unwrap();
|
||||||
|
lc.restarting_remote_device = true;
|
||||||
|
let msg = lc.restart_remote_device();
|
||||||
|
self.send(Data::Message(msg));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_restarting_remote_device(&self) -> bool {
|
||||||
|
self.lc.read().unwrap().restarting_remote_device
|
||||||
|
}
|
||||||
|
|
||||||
fn t(&self, name: String) -> String {
|
fn t(&self, name: String) -> String {
|
||||||
crate::client::translate(name)
|
crate::client::translate(name)
|
||||||
}
|
}
|
||||||
@ -993,7 +1001,7 @@ impl Handler {
|
|||||||
|
|
||||||
fn transfer_file(&mut self) {
|
fn transfer_file(&mut self) {
|
||||||
let id = self.get_id();
|
let id = self.get_id();
|
||||||
let args = vec!["--file-transfer", &id];
|
let args = vec!["--file-transfer", &id, &self.password];
|
||||||
if let Err(err) = crate::run_me(args) {
|
if let Err(err) = crate::run_me(args) {
|
||||||
log::error!("Failed to spawn file transfer: {}", err);
|
log::error!("Failed to spawn file transfer: {}", err);
|
||||||
}
|
}
|
||||||
@ -1001,7 +1009,7 @@ impl Handler {
|
|||||||
|
|
||||||
fn tunnel(&mut self) {
|
fn tunnel(&mut self) {
|
||||||
let id = self.get_id();
|
let id = self.get_id();
|
||||||
let args = vec!["--port-forward", &id];
|
let args = vec!["--port-forward", &id, &self.password];
|
||||||
if let Err(err) = crate::run_me(args) {
|
if let Err(err) = crate::run_me(args) {
|
||||||
log::error!("Failed to spawn IP tunneling: {}", err);
|
log::error!("Failed to spawn IP tunneling: {}", err);
|
||||||
}
|
}
|
||||||
@ -1390,6 +1398,7 @@ async fn start_one_port_forward(
|
|||||||
handler.lc.write().unwrap().port_forward = (remote_host, remote_port);
|
handler.lc.write().unwrap().port_forward = (remote_host, remote_port);
|
||||||
if let Err(err) = crate::port_forward::listen(
|
if let Err(err) = crate::port_forward::listen(
|
||||||
handler.id.clone(),
|
handler.id.clone(),
|
||||||
|
handler.password.clone(),
|
||||||
port,
|
port,
|
||||||
handler.clone(),
|
handler.clone(),
|
||||||
receiver,
|
receiver,
|
||||||
@ -1626,8 +1635,13 @@ impl Remote {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
log::info!("Reset by the peer");
|
if self.handler.is_restarting_remote_device() {
|
||||||
self.handler.msgbox("error", "Connection Error", "Reset by the peer");
|
log::info!("Restart remote device");
|
||||||
|
self.handler.msgbox("restarting", "Restarting Remote Device", "remote_restarting_tip");
|
||||||
|
} else {
|
||||||
|
log::info!("Reset by the peer");
|
||||||
|
self.handler.msgbox("error", "Connection Error", "Reset by the peer");
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1808,7 +1822,6 @@ impl Remote {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_msg_from_ui(&mut self, data: Data, peer: &mut Stream) -> bool {
|
async fn handle_msg_from_ui(&mut self, data: Data, peer: &mut Stream) -> bool {
|
||||||
// log::info!("new msg from ui, {}",data);
|
|
||||||
match data {
|
match data {
|
||||||
Data::Close => {
|
Data::Close => {
|
||||||
let mut misc = Misc::new();
|
let mut misc = Misc::new();
|
||||||
@ -2197,6 +2210,22 @@ impl Remote {
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn send_opts_after_login(&self, peer: &mut Stream) {
|
||||||
|
if let Some(opts) = self
|
||||||
|
.handler
|
||||||
|
.lc
|
||||||
|
.read()
|
||||||
|
.unwrap()
|
||||||
|
.get_option_message_after_login()
|
||||||
|
{
|
||||||
|
let mut misc = Misc::new();
|
||||||
|
misc.set_option(opts);
|
||||||
|
let mut msg_out = Message::new();
|
||||||
|
msg_out.set_misc(misc);
|
||||||
|
allow_err!(peer.send(&msg_out).await);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async fn handle_msg_from_peer(&mut self, data: &[u8], peer: &mut Stream) -> bool {
|
async fn handle_msg_from_peer(&mut self, data: &[u8], peer: &mut Stream) -> bool {
|
||||||
if let Ok(msg_in) = Message::parse_from_bytes(&data) {
|
if let Ok(msg_in) = Message::parse_from_bytes(&data) {
|
||||||
match msg_in.union {
|
match msg_in.union {
|
||||||
@ -2205,6 +2234,7 @@ impl Remote {
|
|||||||
self.first_frame = true;
|
self.first_frame = true;
|
||||||
self.handler.call2("closeSuccess", &make_args!());
|
self.handler.call2("closeSuccess", &make_args!());
|
||||||
self.handler.call("adaptSize", &make_args!());
|
self.handler.call("adaptSize", &make_args!());
|
||||||
|
self.send_opts_after_login(peer).await;
|
||||||
}
|
}
|
||||||
let incomming_format = CodecFormat::from(&vf);
|
let incomming_format = CodecFormat::from(&vf);
|
||||||
if self.video_format != incomming_format {
|
if self.video_format != incomming_format {
|
||||||
@ -2217,7 +2247,9 @@ impl Remote {
|
|||||||
self.video_sender.send(MediaData::VideoFrame(vf)).ok();
|
self.video_sender.send(MediaData::VideoFrame(vf)).ok();
|
||||||
}
|
}
|
||||||
Some(message::Union::Hash(hash)) => {
|
Some(message::Union::Hash(hash)) => {
|
||||||
self.handler.handle_hash(hash, peer).await;
|
self.handler
|
||||||
|
.handle_hash(&self.handler.password.clone(), hash, peer)
|
||||||
|
.await;
|
||||||
}
|
}
|
||||||
Some(message::Union::LoginResponse(lr)) => match lr.union {
|
Some(message::Union::LoginResponse(lr)) => match lr.union {
|
||||||
Some(login_response::Union::Error(err)) => {
|
Some(login_response::Union::Error(err)) => {
|
||||||
@ -2458,6 +2490,10 @@ impl Remote {
|
|||||||
self.handler
|
self.handler
|
||||||
.call2("setPermission", &make_args!("file", p.enabled));
|
.call2("setPermission", &make_args!("file", p.enabled));
|
||||||
}
|
}
|
||||||
|
Permission::Restart => {
|
||||||
|
self.handler
|
||||||
|
.call2("setPermission", &make_args!("restart", p.enabled));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some(misc::Union::SwitchDisplay(s)) => {
|
Some(misc::Union::SwitchDisplay(s)) => {
|
||||||
@ -2513,14 +2549,14 @@ impl Remote {
|
|||||||
match notification.union {
|
match notification.union {
|
||||||
Some(back_notification::Union::BlockInputState(state)) => {
|
Some(back_notification::Union::BlockInputState(state)) => {
|
||||||
self.handle_back_msg_block_input(
|
self.handle_back_msg_block_input(
|
||||||
state.enum_value_or(back_notification::BlockInputState::StateUnknown),
|
state.enum_value_or(back_notification::BlockInputState::BlkStateUnknown),
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
Some(back_notification::Union::PrivacyModeState(state)) => {
|
Some(back_notification::Union::PrivacyModeState(state)) => {
|
||||||
if !self
|
if !self
|
||||||
.handle_back_msg_privacy_mode(
|
.handle_back_msg_privacy_mode(
|
||||||
state.enum_value_or(back_notification::PrivacyModeState::StateUnknown),
|
state.enum_value_or(back_notification::PrivacyModeState::PrvStateUnknown),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
@ -2539,18 +2575,18 @@ impl Remote {
|
|||||||
|
|
||||||
async fn handle_back_msg_block_input(&mut self, state: back_notification::BlockInputState) {
|
async fn handle_back_msg_block_input(&mut self, state: back_notification::BlockInputState) {
|
||||||
match state {
|
match state {
|
||||||
back_notification::BlockInputState::OnSucceeded => {
|
back_notification::BlockInputState::BlkOnSucceeded => {
|
||||||
self.update_block_input_state(true);
|
self.update_block_input_state(true);
|
||||||
}
|
}
|
||||||
back_notification::BlockInputState::OnFailed => {
|
back_notification::BlockInputState::BlkOnFailed => {
|
||||||
self.handler
|
self.handler
|
||||||
.msgbox("custom-error", "Block user input", "Failed");
|
.msgbox("custom-error", "Block user input", "Failed");
|
||||||
self.update_block_input_state(false);
|
self.update_block_input_state(false);
|
||||||
}
|
}
|
||||||
back_notification::BlockInputState::OffSucceeded => {
|
back_notification::BlockInputState::BlkOffSucceeded => {
|
||||||
self.update_block_input_state(false);
|
self.update_block_input_state(false);
|
||||||
}
|
}
|
||||||
back_notification::BlockInputState::OffFailed => {
|
back_notification::BlockInputState::BlkOffFailed => {
|
||||||
self.handler
|
self.handler
|
||||||
.msgbox("custom-error", "Unblock user input", "Failed");
|
.msgbox("custom-error", "Unblock user input", "Failed");
|
||||||
}
|
}
|
||||||
@ -2572,7 +2608,7 @@ impl Remote {
|
|||||||
state: back_notification::PrivacyModeState,
|
state: back_notification::PrivacyModeState,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
match state {
|
match state {
|
||||||
back_notification::PrivacyModeState::OnByOther => {
|
back_notification::PrivacyModeState::PrvOnByOther => {
|
||||||
self.handler.msgbox(
|
self.handler.msgbox(
|
||||||
"error",
|
"error",
|
||||||
"Connecting...",
|
"Connecting...",
|
||||||
@ -2580,46 +2616,46 @@ impl Remote {
|
|||||||
);
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
back_notification::PrivacyModeState::NotSupported => {
|
back_notification::PrivacyModeState::PrvNotSupported => {
|
||||||
self.handler
|
self.handler
|
||||||
.msgbox("custom-error", "Privacy mode", "Unsupported");
|
.msgbox("custom-error", "Privacy mode", "Unsupported");
|
||||||
self.update_privacy_mode(false);
|
self.update_privacy_mode(false);
|
||||||
}
|
}
|
||||||
back_notification::PrivacyModeState::OnSucceeded => {
|
back_notification::PrivacyModeState::PrvOnSucceeded => {
|
||||||
self.handler
|
self.handler
|
||||||
.msgbox("custom-nocancel", "Privacy mode", "In privacy mode");
|
.msgbox("custom-nocancel", "Privacy mode", "In privacy mode");
|
||||||
self.update_privacy_mode(true);
|
self.update_privacy_mode(true);
|
||||||
}
|
}
|
||||||
back_notification::PrivacyModeState::OnFailedDenied => {
|
back_notification::PrivacyModeState::PrvOnFailedDenied => {
|
||||||
self.handler
|
self.handler
|
||||||
.msgbox("custom-error", "Privacy mode", "Peer denied");
|
.msgbox("custom-error", "Privacy mode", "Peer denied");
|
||||||
self.update_privacy_mode(false);
|
self.update_privacy_mode(false);
|
||||||
}
|
}
|
||||||
back_notification::PrivacyModeState::OnFailedPlugin => {
|
back_notification::PrivacyModeState::PrvOnFailedPlugin => {
|
||||||
self.handler
|
self.handler
|
||||||
.msgbox("custom-error", "Privacy mode", "Please install plugins");
|
.msgbox("custom-error", "Privacy mode", "Please install plugins");
|
||||||
self.update_privacy_mode(false);
|
self.update_privacy_mode(false);
|
||||||
}
|
}
|
||||||
back_notification::PrivacyModeState::OnFailed => {
|
back_notification::PrivacyModeState::PrvOnFailed => {
|
||||||
self.handler
|
self.handler
|
||||||
.msgbox("custom-error", "Privacy mode", "Failed");
|
.msgbox("custom-error", "Privacy mode", "Failed");
|
||||||
self.update_privacy_mode(false);
|
self.update_privacy_mode(false);
|
||||||
}
|
}
|
||||||
back_notification::PrivacyModeState::OffSucceeded => {
|
back_notification::PrivacyModeState::PrvOffSucceeded => {
|
||||||
self.handler
|
self.handler
|
||||||
.msgbox("custom-nocancel", "Privacy mode", "Out privacy mode");
|
.msgbox("custom-nocancel", "Privacy mode", "Out privacy mode");
|
||||||
self.update_privacy_mode(false);
|
self.update_privacy_mode(false);
|
||||||
}
|
}
|
||||||
back_notification::PrivacyModeState::OffByPeer => {
|
back_notification::PrivacyModeState::PrvOffByPeer => {
|
||||||
self.handler
|
self.handler
|
||||||
.msgbox("custom-error", "Privacy mode", "Peer exit");
|
.msgbox("custom-error", "Privacy mode", "Peer exit");
|
||||||
self.update_privacy_mode(false);
|
self.update_privacy_mode(false);
|
||||||
}
|
}
|
||||||
back_notification::PrivacyModeState::OffFailed => {
|
back_notification::PrivacyModeState::PrvOffFailed => {
|
||||||
self.handler
|
self.handler
|
||||||
.msgbox("custom-error", "Privacy mode", "Failed to turn off");
|
.msgbox("custom-error", "Privacy mode", "Failed to turn off");
|
||||||
}
|
}
|
||||||
back_notification::PrivacyModeState::OffUnknown => {
|
back_notification::PrivacyModeState::PrvOffUnknown => {
|
||||||
self.handler
|
self.handler
|
||||||
.msgbox("custom-error", "Privacy mode", "Turned off");
|
.msgbox("custom-error", "Privacy mode", "Turned off");
|
||||||
// log::error!("Privacy mode is turned off with unknown reason");
|
// log::error!("Privacy mode is turned off with unknown reason");
|
||||||
@ -2710,6 +2746,9 @@ impl Interface for Handler {
|
|||||||
pi_sciter.set_item("hostname", pi.hostname.clone());
|
pi_sciter.set_item("hostname", pi.hostname.clone());
|
||||||
pi_sciter.set_item("platform", pi.platform.clone());
|
pi_sciter.set_item("platform", pi.platform.clone());
|
||||||
pi_sciter.set_item("sas_enabled", pi.sas_enabled);
|
pi_sciter.set_item("sas_enabled", pi.sas_enabled);
|
||||||
|
if get_version_number(&pi.version) < get_version_number("1.1.10") {
|
||||||
|
self.call2("setPermission", &make_args!("restart", false));
|
||||||
|
}
|
||||||
if self.is_file_transfer() {
|
if self.is_file_transfer() {
|
||||||
if pi.username.is_empty() {
|
if pi.username.is_empty() {
|
||||||
self.on_error("No active console user logged on, please connect and logon first.");
|
self.on_error("No active console user logged on, please connect and logon first.");
|
||||||
@ -2777,8 +2816,8 @@ impl Interface for Handler {
|
|||||||
self.start_keyboard_hook();
|
self.start_keyboard_hook();
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_hash(&mut self, hash: Hash, peer: &mut Stream) {
|
async fn handle_hash(&mut self, pass: &str, hash: Hash, peer: &mut Stream) {
|
||||||
handle_hash(self.lc.clone(), hash, self, peer).await;
|
handle_hash(self.lc.clone(), pass, hash, self, peer).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_login_from_ui(&mut self, password: String, remember: bool, peer: &mut Stream) {
|
async fn handle_login_from_ui(&mut self, password: String, remember: bool, peer: &mut Stream) {
|
||||||
|
@ -11,6 +11,7 @@ var keyboard_enabled = true; // server side
|
|||||||
var clipboard_enabled = true; // server side
|
var clipboard_enabled = true; // server side
|
||||||
var audio_enabled = true; // server side
|
var audio_enabled = true; // server side
|
||||||
var file_enabled = true; // server side
|
var file_enabled = true; // server side
|
||||||
|
var restart_enabled = true; // server side
|
||||||
var scroll_body = $(body);
|
var scroll_body = $(body);
|
||||||
|
|
||||||
handler.setDisplay = function(x, y, w, h) {
|
handler.setDisplay = function(x, y, w, h) {
|
||||||
@ -505,6 +506,7 @@ handler.setPermission = function(name, enabled) {
|
|||||||
if (name == "audio") audio_enabled = enabled;
|
if (name == "audio") audio_enabled = enabled;
|
||||||
if (name == "file") file_enabled = enabled;
|
if (name == "file") file_enabled = enabled;
|
||||||
if (name == "clipboard") clipboard_enabled = enabled;
|
if (name == "clipboard") clipboard_enabled = enabled;
|
||||||
|
if (name == "restart") restart_enabled = enabled;
|
||||||
input_blocked = false;
|
input_blocked = false;
|
||||||
header.update();
|
header.update();
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user