mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2024-12-25 15:29:44 -05:00
Release 2.5.2
Added - Browser: Show UI warning when entering invalid URLs [#3912] - Browser: Option to use an entry only for HTTP auth [#3927] Changed - Disable the user interface when merging or saving the database [#3991] - Ability to hide protected attribute after reveal [#3877] - Remove mention of "snaps" in Windows and macOS [#3879] - CLI: Merge parameter for source database key file (--key-file-from) [#3961] - Improve GUI tests reliability on Hi-DPI displays [#4075] - Disable deprecation warnings to allow building with Qt 5.14+ [#4075] - OPVault: Use 'otp' attribute for TOTP field imports [#4075] Fixed - Fix crashes when saving a database to cloud storage [#3991] - Fix crash when pressing enter twice while opening database [#3885] - Fix handling of HTML when displayed in the entry preview panel [#3910] - Fix start minimized to tray on Linux [#3899] - Fix Auto Open with key file only databases [#4075] - Fix escape key closing the standalone password generator [#3892] - macOS: Fix monospace font usage in password field and notes [#4075] - macOS: Fix building on macOS 10.9 to 10.11 [#3946] - Fix TOTP setup dialog not closing on database lock [#4075] - Browser: Fix condition where additional URLs are ignored [#4033] - Browser: Fix subdomain matching to return only relevant site entries [#3854] - Secret Service: Fix multiple crashes and incompatibilities [#3871, #4009, #4074] - Secret Service: Fix searching of entries [#4008, #4036] - Secret Service: Fix behavior when exposed group is recycled [#3914] - CLI: Release the database instance before exiting interactive mode [#3889] - Fix (most) memory leaks in tests [#3922]
This commit is contained in:
commit
62cda9dd40
36
CHANGELOG.md
36
CHANGELOG.md
@ -1,5 +1,41 @@
|
||||
# Changelog
|
||||
|
||||
## 2.5.2 (2020-01-04)
|
||||
|
||||
### Added
|
||||
|
||||
- Browser: Show UI warning when entering invalid URLs [#3912]
|
||||
- Browser: Option to use an entry only for HTTP auth [#3927]
|
||||
|
||||
### Changed
|
||||
|
||||
- Disable the user interface when merging or saving the database [#3991]
|
||||
- Ability to hide protected attribute after reveal [#3877]
|
||||
- Remove mention of "snaps" in Windows and macOS [#3879]
|
||||
- CLI: Merge parameter for source database key file (--key-file-from) [#3961]
|
||||
- Improve GUI tests reliability on Hi-DPI displays [#4075]
|
||||
- Disable deprecation warnings to allow building with Qt 5.14+ [#4075]
|
||||
- OPVault: Use 'otp' attribute for TOTP field imports [#4075]
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix crashes when saving a database to cloud storage [#3991]
|
||||
- Fix crash when pressing enter twice while opening database [#3885]
|
||||
- Fix handling of HTML when displayed in the entry preview panel [#3910]
|
||||
- Fix start minimized to tray on Linux [#3899]
|
||||
- Fix Auto Open with key file only databases [#4075]
|
||||
- Fix escape key closing the standalone password generator [#3892]
|
||||
- macOS: Fix monospace font usage in password field and notes [#4075]
|
||||
- macOS: Fix building on macOS 10.9 to 10.11 [#3946]
|
||||
- Fix TOTP setup dialog not closing on database lock [#4075]
|
||||
- Browser: Fix condition where additional URLs are ignored [#4033]
|
||||
- Browser: Fix subdomain matching to return only relevant site entries [#3854]
|
||||
- Secret Service: Fix multiple crashes and incompatibilities [#3871, #4009, #4074]
|
||||
- Secret Service: Fix searching of entries [#4008, #4036]
|
||||
- Secret Service: Fix behavior when exposed group is recycled [#3914]
|
||||
- CLI: Release the database instance before exiting interactive mode [#3889]
|
||||
- Fix (most) memory leaks in tests [#3922]
|
||||
|
||||
## 2.5.1 (2019-11-11)
|
||||
|
||||
### Added
|
||||
|
@ -36,6 +36,7 @@ endif (CCACHE_FOUND)
|
||||
|
||||
# Support Visual Studio Code
|
||||
include(CMakeToolsHelpers OPTIONAL)
|
||||
include(FeatureSummary)
|
||||
|
||||
include(CheckCCompilerFlag)
|
||||
include(CheckCXXCompilerFlag)
|
||||
@ -94,7 +95,7 @@ endif()
|
||||
|
||||
set(KEEPASSXC_VERSION_MAJOR "2")
|
||||
set(KEEPASSXC_VERSION_MINOR "5")
|
||||
set(KEEPASSXC_VERSION_PATCH "1")
|
||||
set(KEEPASSXC_VERSION_PATCH "2")
|
||||
set(KEEPASSXC_VERSION "${KEEPASSXC_VERSION_MAJOR}.${KEEPASSXC_VERSION_MINOR}.${KEEPASSXC_VERSION_PATCH}")
|
||||
set(OVERRIDE_VERSION "" CACHE STRING "Override the KeePassXC Version for Snapshot builds")
|
||||
|
||||
@ -299,6 +300,9 @@ endif()
|
||||
|
||||
if(WITH_DEV_BUILD)
|
||||
add_definitions(-DQT_DEPRECATED_WARNINGS -DGCRYPT_NO_DEPRECATED)
|
||||
else()
|
||||
add_definitions(-DQT_NO_DEPRECATED_WARNINGS)
|
||||
add_gcc_compiler_cxxflags("-Wno-deprecated-declarations")
|
||||
endif()
|
||||
|
||||
if(MINGW)
|
||||
@ -465,8 +469,6 @@ endif()
|
||||
|
||||
include_directories(SYSTEM ${GCRYPT_INCLUDE_DIR} ${ZLIB_INCLUDE_DIR})
|
||||
|
||||
include(FeatureSummary)
|
||||
|
||||
add_subdirectory(src)
|
||||
add_subdirectory(share)
|
||||
if(WITH_TESTS)
|
||||
|
5
COPYING
5
COPYING
@ -248,3 +248,8 @@ Comment: from Freedesktop.org website
|
||||
|
||||
Files: share/icons/application/32x32/actions/statistics.png
|
||||
Copyright: Icon made by Freepik from https://www.flaticon.com/free-icon/bars-chart_265733
|
||||
|
||||
Files: share/icons/application/scalable/actions/object-locked.svg
|
||||
share/icons/application/scalable/actions/object-unlocked.svg
|
||||
License: LGPL-3
|
||||
Comment: from Breeze icon theme (https://github.com/KDE/breeze-icons)
|
||||
|
10
INSTALL.md
10
INSTALL.md
@ -150,9 +150,15 @@ You can create a package to redistribute KeePassXC (zip, deb, rpm, dmg, etc..).
|
||||
Testing
|
||||
=======
|
||||
|
||||
You can perform test on the executable
|
||||
You can perform tests on the built executables with:
|
||||
```
|
||||
make test
|
||||
make test ARGS+="--output-on-failure"
|
||||
```
|
||||
|
||||
If you are not currently running on an X Server or Wayland, run the tests as follows:
|
||||
```
|
||||
make test ARGS+="-E test\(cli\|gui\) --output-on-failure"
|
||||
xvfb-run -e errors -a --server-args="-screen 0 1024x768x24" make test ARGS+="-R test\(cli\|gui\) --output-on-failure"
|
||||
```
|
||||
|
||||
Common parameters:
|
||||
|
@ -117,7 +117,7 @@ Displays the program version.
|
||||
.IP "-d, --dry-run <path>"
|
||||
Prints the changes detected by the merge operation without making any changes to the database.
|
||||
|
||||
.IP "-f, --key-file-from <path>"
|
||||
.IP "--key-file-from <path>"
|
||||
Sets the path of the key file for the second database.
|
||||
|
||||
.IP "--no-password-from"
|
||||
|
14
share/icons/application/scalable/actions/object-locked.svg
Normal file
14
share/icons/application/scalable/actions/object-locked.svg
Normal file
@ -0,0 +1,14 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<defs id="defs3051">
|
||||
<style type="text/css" id="current-color-scheme">
|
||||
.ColorScheme-Text {
|
||||
color:#232629;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<path style="fill:currentColor;fill-opacity:1;stroke:none"
|
||||
d="M 11,3 C 8.784,3 7,4.784 7,7 l 0,4 -2,0 c 0,2.666667 0,5.333333 0,8 4,0 8,0 12,0 l 0,-8 c -0.666667,0 -1.333333,0 -2,0 L 15,7 C 15,4.784 13.216,3 11,3 m 0,1 c 1.662,0 3,1.561 3,3.5 L 14,11 8,11 8,7.5 C 8,5.561 9.338,4 11,4"
|
||||
class="ColorScheme-Text"
|
||||
transform="translate(1,1)"
|
||||
/>
|
||||
</svg>
|
After Width: | Height: | Size: 592 B |
15
share/icons/application/scalable/actions/object-unlocked.svg
Normal file
15
share/icons/application/scalable/actions/object-unlocked.svg
Normal file
@ -0,0 +1,15 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<defs id="defs3051">
|
||||
<style type="text/css" id="current-color-scheme">
|
||||
.ColorScheme-Text {
|
||||
color:#232629;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<path
|
||||
style="fill:currentColor;fill-opacity:1;stroke:none"
|
||||
d="m11 3c-2.216 0-4 1.784-4 4v1h1v-.5c0-1.939 1.338-3.5 3-3.5 1.662 0 3 1.561 3 3.5v3.5h-5-1-1-1-1v1 7h1 10 1v-8h-1-1v-4c0-2.216-1.784-4-4-4m-5 9h10v6h-10v-6"
|
||||
class="ColorScheme-Text"
|
||||
transform="translate(1,1)"
|
||||
/>
|
||||
</svg>
|
After Width: | Height: | Size: 523 B |
@ -50,6 +50,37 @@
|
||||
</screenshots>
|
||||
|
||||
<releases>
|
||||
<release version="2.5.2" date="2020-01-04">
|
||||
<description>
|
||||
<ul>
|
||||
<li>Browser: Show UI warning when entering invalid URLs [#3912]</li>
|
||||
<li>Browser: Option to use an entry only for HTTP auth [#3927]</li>
|
||||
<li>Disable the user interface when merging or saving the database [#3991]</li>
|
||||
<li>Ability to hide protected attribute after reveal [#3877]</li>
|
||||
<li>Remove mention of "snaps" in Windows and macOS [#3879]</li>
|
||||
<li>CLI: Merge parameter for source database key file (--key-file-from) [#3961]</li>
|
||||
<li>Improve GUI tests reliability on Hi-DPI displays [#4075]</li>
|
||||
<li>Disable deprecation warnings to allow building with Qt 5.14+ [#4075]</li>
|
||||
<li>OPVault: Use 'otp' attribute for TOTP field imports [#4075]</li>
|
||||
<li>Fix crashes when saving a database to cloud storage [#3991]</li>
|
||||
<li>Fix crash when pressing enter twice while opening database [#3885]</li>
|
||||
<li>Fix handling of HTML when displayed in the entry preview panel [#3910]</li>
|
||||
<li>Fix start minimized to tray on Linux [#3899]</li>
|
||||
<li>Fix Auto Open with key file only databases [#4075]</li>
|
||||
<li>Fix escape key closing the standalone password generator [#3892]</li>
|
||||
<li>macOS: Fix monospace font usage in password field and notes [#4075]</li>
|
||||
<li>macOS: Fix building on macOS 10.9 to 10.11 [#3946]</li>
|
||||
<li>Fix TOTP setup dialog not closing on database lock [#4075]</li>
|
||||
<li>Browser: Fix condition where additional URLs are ignored [#4033]</li>
|
||||
<li>Browser: Fix subdomain matching to return only relevant site entries [#3854]</li>
|
||||
<li>Secret Service: Fix multiple crashes and incompatibilities [#3871, #4009, #4074]</li>
|
||||
<li>Secret Service: Fix searching of entries [#4008, #4036]</li>
|
||||
<li>Secret Service: Fix behavior when exposed group is recycled [#3914]</li>
|
||||
<li>CLI: Release the database instance before exiting interactive mode [#3889]</li>
|
||||
<li>Fix (most) memory leaks in tests [#3922]</li>
|
||||
</ul>
|
||||
</description>
|
||||
</release>
|
||||
<release version="2.5.1" date="2019-11-11">
|
||||
<description>
|
||||
<ul>
|
||||
|
@ -23,7 +23,7 @@
|
||||
</message>
|
||||
<message>
|
||||
<source><a href="https://github.com/keepassxreboot/keepassxc/graphs/contributors">See Contributions on GitHub</a></source>
|
||||
<translation><a href="https://github.com/keepassxreboot/keepassxc/graphs/contributors">إستكشاف المساهمين عبر GitHub</a></translation>
|
||||
<translation><a href="https://github.com/keepassxreboot/keepassxc/graphs/contributors">إستكشاف المساهمات عبر GitHub</a></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Debug Info</source>
|
||||
@ -43,7 +43,7 @@
|
||||
</message>
|
||||
<message>
|
||||
<source>Special thanks from the KeePassXC team go to debfx for creating the original KeePassX.</source>
|
||||
<translation>شكر خاص من فريق KeePassXC يذهب إلى debfx لإنشاء KeePassX الأصلي.</translation>
|
||||
<translation>شكر خاص من فريق KeePassXC إلى debfx لإنشاء KeePassX الأصلي.</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@ -77,23 +77,23 @@
|
||||
</message>
|
||||
<message>
|
||||
<source>Icon only</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>الأيقونة فقط</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Text only</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>الوصف بالكلمات فقط </translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Text beside icon</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>وصف حرفي بجانب الأيقونة</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Text under icon</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>الوصف أسفل الأيقونة</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Follow style</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>تابع النمط</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Reset Settings?</source>
|
||||
@ -101,7 +101,7 @@
|
||||
</message>
|
||||
<message>
|
||||
<source>Are you sure you want to reset all general and security settings to default?</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>هل أنت متأكد من رغبتك في إعادة جميع الإعدادات العامة وإعدادات الأمن الى وضعها النمطي الأولي ؟</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@ -116,7 +116,7 @@
|
||||
</message>
|
||||
<message>
|
||||
<source>Start only a single instance of KeePassXC</source>
|
||||
<translation>شغل تطبيق واحد فقط من KeePassXC</translation>
|
||||
<translation>شغل نسخة واحدة فقط من تطبيق KeePassXC</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Minimize window at application startup</source>
|
||||
@ -132,7 +132,7 @@
|
||||
</message>
|
||||
<message>
|
||||
<source>Backup database file before saving</source>
|
||||
<translation>إحتفظ بنسخة من ملف قاعدة البيانات قبل الحفظ</translation>
|
||||
<translation>عمل نسخة إحتياطية من ملف قاعدة البيانات قبل الحفظ</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Automatically save after every change</source>
|
||||
@ -152,15 +152,15 @@
|
||||
</message>
|
||||
<message>
|
||||
<source>Entry Management</source>
|
||||
<translation>إدارة الإدخالات</translation>
|
||||
<translation>إدارة ادخال البيانات</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Use group icon on entry creation</source>
|
||||
<translation>استخدم رمز المجموعة عند إنشاء الإدخال</translation>
|
||||
<translation>استخدم شعار المجموعة عند إنشاء المدخلات</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Hide the entry preview panel</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>إخفاء إستعراض لوحة المدخلات </translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>General</source>
|
||||
@ -168,15 +168,15 @@
|
||||
</message>
|
||||
<message>
|
||||
<source>Hide toolbar (icons)</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>إخفاء شريط العُدد (الأيقونات)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Minimize instead of app exit</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation> تصغيرالتطبيق بدل الخروج منه </translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Show a system tray icon</source>
|
||||
<translation>اظهر أيقونة البرنامج في صينية النظام</translation>
|
||||
<translation>اظهار أيقونة البرنامج في شريط واجهة النظام</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Dark system tray icon</source>
|
||||
@ -188,7 +188,7 @@
|
||||
</message>
|
||||
<message>
|
||||
<source>Auto-Type</source>
|
||||
<translation>الطباعة التلقائية</translation>
|
||||
<translation> نمط تلقائي</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Use entry title to match windows for global Auto-Type</source>
|
||||
@ -200,7 +200,7 @@
|
||||
</message>
|
||||
<message>
|
||||
<source>Always ask before performing Auto-Type</source>
|
||||
<translation>اسأل دائما قبل تنفيذ الطباعة التلقائية</translation>
|
||||
<translation>السؤأل دائما قبل التغيير للنمط التلقائي</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Global Auto-Type shortcut</source>
|
||||
@ -213,7 +213,7 @@
|
||||
<message>
|
||||
<source> ms</source>
|
||||
<comment>Milliseconds</comment>
|
||||
<translation>مل.ثانية</translation>
|
||||
<translation>جزء الثانية</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Auto-Type start delay</source>
|
||||
@ -221,15 +221,15 @@
|
||||
</message>
|
||||
<message>
|
||||
<source>Movable toolbar</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation> شريط عُدد قابل للتحريك</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Remember previously used databases</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation> تذكُر قاعدة البيانات المستخدمة أخيراً</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Load previously open databases on startup</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation> عند بدء التشغيل إعادة فتح قواعد البيانات التي مفتوحة سابقاً</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Remember database key files and security dongles</source>
|
||||
@ -237,7 +237,7 @@
|
||||
</message>
|
||||
<message>
|
||||
<source>Check for updates at application startup once per week</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation> فحص وجود تحديثات للبرامج عند بدء تشغيل التطبيق مرة واحدة في الأسبوع </translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Include beta releases when checking for updates</source>
|
||||
@ -249,39 +249,39 @@
|
||||
</message>
|
||||
<message>
|
||||
<source>Language:</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation> اللغة</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>(restart program to activate)</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation> ( للتفعيل يرجى إعادة التشغيل ) </translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Minimize window after unlocking database</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation> تصغيرنافذة التطبيق بعد فتح قاعدة البيانات</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Minimize when opening a URL</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation> تصغير البرنامج عند فتح رابط URL من النت </translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Hide window when copying to clipboard</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation> إخفاء نافذة البرنامج عند النسخ الى الحافظة </translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Minimize</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation> تصغير</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Drop to background</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation> الإرجاع الى الخلفية </translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Favicon download timeout:</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation> فوت الوقت لتنزيل أيقونة الشعارالمفضل</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Website icon download timeout in seconds</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation> الزمن بالثانية لفوت الإتصال لتنزيل أيقونة الموقع</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source> sec</source>
|
||||
@ -290,38 +290,39 @@
|
||||
</message>
|
||||
<message>
|
||||
<source>Toolbar button style</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation> نمط مفاتيح شريط العُدد </translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Use monospaced font for Notes</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation> إستخدام بنط ذا مسافات أحادية في الملاحظات</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Language selection</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation> إختيار اللغة</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Reset Settings to Default</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation> إعادة الإعدادات الى وضعها الأولي</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Global auto-type shortcut</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation> المختصر الى النمط-التلقائي العمومي</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Auto-type character typing delay milliseconds</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation> تأخيرالطباعة لأحرف النمط-التلقائي
|
||||
بأجزاء الثانية </translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Auto-type start delay milliseconds</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation> تأخير بدء النمط-التلقائي بأجزاء الثانية</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ApplicationSettingsWidgetSecurity</name>
|
||||
<message>
|
||||
<source>Timeouts</source>
|
||||
<translation>مهلة نفاد الوقت</translation>
|
||||
<translation> نفاذ الوقت</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Clear clipboard after</source>
|
||||
@ -334,11 +335,11 @@
|
||||
</message>
|
||||
<message>
|
||||
<source>Lock databases after inactivity of</source>
|
||||
<translation>أغلق قواعد البيانات بعد حالة عدم النشاط ل</translation>
|
||||
<translation>أغلق قواعد البيانات بعد حالة عدم النشاط لـ</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source> min</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation> دقيقة</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Forget TouchID after inactivity of</source>
|
||||
@ -354,7 +355,7 @@
|
||||
</message>
|
||||
<message>
|
||||
<source>Forget TouchID when session is locked or lid is closed</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation> إنسى هوية البصمة عند قفل حصة التفاعل أو عند إغلاق الشاشة </translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Lock databases after minimizing the window</source>
|
||||
@ -362,7 +363,7 @@
|
||||
</message>
|
||||
<message>
|
||||
<source>Re-lock previously locked database after performing Auto-Type</source>
|
||||
<translation>أعد قفل قاعدة البيانات التي تم تأمينها سابقًا بعد تنفيذ الطباعة التلقائية</translation>
|
||||
<translation>أعد قفل قاعدة البيانات التي تم تأمينها سابقًا بعدالتغيير للنمط التلقائي</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Don't require password repeat when it is visible</source>
|
||||
@ -370,15 +371,15 @@
|
||||
</message>
|
||||
<message>
|
||||
<source>Don't hide passwords when editing them</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation> لا تخفي كلمات السر عند تعديلها </translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Don't use placeholder for empty password fields</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation> لا تستعمل محددات أقواس أو مزدوجات لحقول كلمات السر الفارغة </translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Hide passwords in the entry preview panel</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation> اخفي كلمات السر في لوحة إستعراض المدخلات</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Hide entry notes by default</source>
|
||||
@ -390,28 +391,28 @@
|
||||
</message>
|
||||
<message>
|
||||
<source>Use DuckDuckGo service to download website icons</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation> استعمل خدمة DuckDuckGo لتنزيل أيقونات مواقع الإنترنت</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Clipboard clear seconds</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation> مسح لوحة الحفظ بالثواني</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Touch ID inactivity reset</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation> المهلة الزمنية لعدم التفاعل لنسيان هوية البصمة</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Database lock timeout seconds</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation> المهلة الزنسة لقفل قواعد البيانات </translation>
|
||||
</message>
|
||||
<message>
|
||||
<source> min</source>
|
||||
<comment>Minutes</comment>
|
||||
<translation type="unfinished"/>
|
||||
<translation> دقيقة</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Clear search query after</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation> مسح محددات البحث بعد مضي </translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@ -446,11 +447,12 @@
|
||||
</message>
|
||||
<message>
|
||||
<source>Permission Required</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>إذن السماح مطلوب</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>KeePassXC requires the Accessibility permission in order to perform entry level Auto-Type. If you already granted permission, you may have to restart KeePassXC.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation> KeePassXC يطلب إذن السماح بالدخول لإنجاز التغييرات المبدئية وفق النمط-التلقائي . إذا كنت قد أعطيت الإذن بالسماح
|
||||
فقد يستوجب منك ذلك إعادة تشغيل KeePassXC </translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@ -491,18 +493,18 @@
|
||||
<name>AutoTypeMatchView</name>
|
||||
<message>
|
||||
<source>Copy &username</source>
|
||||
<translation>نسخ &اسم المستخدم</translation>
|
||||
<translation> نسخ و اسم المستخدم</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Copy &password</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation> نسخ و كلمة السر</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>AutoTypePlatformMac</name>
|
||||
<message>
|
||||
<source>Permission Required</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>إذن السماح مطلوب</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>KeePassXC requires the Accessibility and Screen Recorder permission in order to perform global Auto-Type. Screen Recording is necessary to use the window title to find entries. If you already granted permission, you may have to restart KeePassXC.</source>
|
||||
@ -513,11 +515,11 @@
|
||||
<name>AutoTypeSelectDialog</name>
|
||||
<message>
|
||||
<source>Auto-Type - KeePassXC</source>
|
||||
<translation>الطباعة التلقائية - KeePassXC</translation>
|
||||
<translation>النمط التلقائي - KeePassXC</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Select entry to Auto-Type:</source>
|
||||
<translation>حدد مدخل للطباعة التلقائية:</translation>
|
||||
<translation>إختيار مدخلات الى النمط التلقائي:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Search...</source>
|
||||
@ -550,31 +552,32 @@ Please select whether you want to allow access.</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>Allow access</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation> أسمح بالدخول</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Deny access</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation> أمنع الدخول</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>BrowserEntrySaveDialog</name>
|
||||
<message>
|
||||
<source>KeePassXC-Browser Save Entry</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation> متصفح KeePassXC إحفظ المدخلات</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Ok</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation> تمام</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Cancel</source>
|
||||
<translation>ألغ</translation>
|
||||
<translation>ألغاء</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>You have multiple databases open.
|
||||
Please select the correct database for saving credentials.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation> لديك العديد من قواعد البيانات مفتوحة
|
||||
الرجاء إختيار قاعدة البيانات الصحيحة لحفظ المسوغات </translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@ -597,19 +600,19 @@ Please select the correct database for saving credentials.</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>&Google Chrome</source>
|
||||
<translation>&Google Chrome</translation>
|
||||
<translation>&قوقل كروم</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>&Firefox</source>
|
||||
<translation>&Firefox</translation>
|
||||
<translation>&فايرفوكس</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>&Chromium</source>
|
||||
<translation>&Chromium</translation>
|
||||
<translation>&كروميوم</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>&Vivaldi</source>
|
||||
<translation>&Vivaldi</translation>
|
||||
<translation>&فافيلادي</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Show a &notification when credentials are requested</source>
|
||||
@ -713,20 +716,20 @@ Please select the correct database for saving credentials.</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>&Tor Browser</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>متصفح تور</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Executable Files</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation> ملفات برامج </translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>All Files</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation> جميع الملفات</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Do not ask permission for HTTP &Basic Auth</source>
|
||||
<extracomment>An extra HTTP Basic Auth setting</extracomment>
|
||||
<translation type="unfinished"/>
|
||||
<translation> لا تطلب الإذن لصفحات النترنت العادية HTTP& و عند الإذن الأساسي</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Due to Snap sandboxing, you must run a script to enable browser integration.<br />You can obtain this script from %1</source>
|
||||
@ -742,7 +745,7 @@ Please select the correct database for saving credentials.</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>&Brave</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation> متصفح &Brave</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Returns expired credentials. String [expired] is added to the title.</source>
|
||||
@ -3477,7 +3480,7 @@ You can enable the DuckDuckGo website icon service in the security section of th
|
||||
</message>
|
||||
<message>
|
||||
<source>Ok</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation> تمام</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Already Exists</source>
|
||||
@ -4477,7 +4480,7 @@ We recommend you use the AppImage available on our downloads page.</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>Copy &password</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation> نسخ و كلمة السر</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Perform &Auto-Type</source>
|
||||
|
@ -116,7 +116,7 @@
|
||||
</message>
|
||||
<message>
|
||||
<source>Start only a single instance of KeePassXC</source>
|
||||
<translation>Obriu només una sola instància del KeePassXC</translation>
|
||||
<translation>Obre només una sola instància del KeePassXC</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Minimize window at application startup</source>
|
||||
@ -136,7 +136,7 @@
|
||||
</message>
|
||||
<message>
|
||||
<source>Automatically save after every change</source>
|
||||
<translation>Deseu després de cada canvi de forma automàtica</translation>
|
||||
<translation>Desa després de cada canvi de forma automàtica</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Automatically save on exit</source>
|
||||
@ -144,11 +144,11 @@
|
||||
</message>
|
||||
<message>
|
||||
<source>Don't mark database as modified for non-data changes (e.g., expanding groups)</source>
|
||||
<translation>No marqueu la base de dades com a modificada si no han hagut canvis en les dades (per exemple, a l'expandir grups)</translation>
|
||||
<translation>No marquis la base de dades com a modificada si no hi han hagut canvis en les dades (per exemple, a l'expandir grups)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Automatically reload the database when modified externally</source>
|
||||
<translation>Torneu a carregar automàticament la base de dades quan siga modificada de forma externa</translation>
|
||||
<translation>Torna a carregar automàticament la base de dades quan sigui modificada de forma externa</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Entry Management</source>
|
||||
@ -334,7 +334,7 @@
|
||||
</message>
|
||||
<message>
|
||||
<source>Lock databases after inactivity of</source>
|
||||
<translation>Bloquegeu les bases de dades després d'estar inactives per</translation>
|
||||
<translation>Bloqueja les bases de dades després d'estar inactives</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source> min</source>
|
||||
@ -350,7 +350,7 @@
|
||||
</message>
|
||||
<message>
|
||||
<source>Lock databases when session is locked or lid is closed</source>
|
||||
<translation>Bloquegeu les bases de dades quan sessió siga bloquejada o es tanque la tapa</translation>
|
||||
<translation>Bloqueja les bases de dades quan la sessió sigui bloquejada o es tanqui la tapa</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Forget TouchID when session is locked or lid is closed</source>
|
||||
@ -358,7 +358,7 @@
|
||||
</message>
|
||||
<message>
|
||||
<source>Lock databases after minimizing the window</source>
|
||||
<translation>Bloquegeu les bases de dades després minimitzar la finestra</translation>
|
||||
<translation>Bloqueja les bases de dades després minimitzar la finestra</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Re-lock previously locked database after performing Auto-Type</source>
|
||||
@ -411,7 +411,7 @@
|
||||
</message>
|
||||
<message>
|
||||
<source>Clear search query after</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Elimina la teva cerca després de</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@ -434,15 +434,15 @@
|
||||
</message>
|
||||
<message>
|
||||
<source>This Auto-Type command contains a very long delay. Do you really want to proceed?</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Aquesta ordre de compleció automàtica conté un retard molt llarg. Esteu segur que voleu continuar?</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>This Auto-Type command contains very slow key presses. Do you really want to proceed?</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Aquesta comanda de compleció automàtica conté pulsacions de tecles molt lentes. Esteu segur que voleu continuar?</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>This Auto-Type command contains arguments which are repeated very often. Do you really want to proceed?</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Aquesta comanda de compleció automàtica conté arguments que es repeteixen molt sovint. Esteu segur que voleu continuar?</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Permission Required</source>
|
||||
@ -574,7 +574,8 @@ Seleccioneu si voleu permetre l'accés.</translation>
|
||||
<message>
|
||||
<source>You have multiple databases open.
|
||||
Please select the correct database for saving credentials.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Teniu diverses bases de dades obertes.
|
||||
Seleccioneu la base de dades correcta per desar les credencials.</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@ -675,11 +676,11 @@ Please select the correct database for saving credentials.</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>Updates KeePassXC or keepassxc-proxy binary path automatically to native messaging scripts on startup.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Actualitza la ruta binària de KeePassXC o keepassxc-proxy automàticament als scripts de missatgeria nativa a l'inici.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Update &native messaging manifest files at startup</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Actualitza el fitxers de manifest de la missatgeria &nativa a l'inici</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Support a proxy application between KeePassXC and browser extension.</source>
|
||||
@ -738,7 +739,7 @@ Please select the correct database for saving credentials.</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>KeePassXC-Browser is needed for the browser integration to work. <br />Download it for %1 and %2. %3</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Perquè funcioni la integració del navegador cal el KeePassXC-Browse.<br>Baixeu-lo de %1 i de %2. %3</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>&Brave</source>
|
||||
@ -750,7 +751,7 @@ Please select the correct database for saving credentials.</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>&Allow returning expired credentials.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>&Permet el retorn de credencials caducades.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Enable browser integration</source>
|
||||
@ -758,7 +759,7 @@ Please select the correct database for saving credentials.</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>Browsers installed as snaps are currently not supported.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Actualment, no s'admeten els navegadors instal·lats com a Snaps.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>All databases connected to the extension will return matching credentials.</source>
|
||||
@ -1167,7 +1168,7 @@ Please consider generating a new key file.</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>TouchID for Quick Unlock</source>
|
||||
<translation>TouchID per desbloquejar ràpidament</translation>
|
||||
<translation>Usa el TouchID per desbloquejar ràpidament</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Clear</source>
|
||||
@ -1179,7 +1180,7 @@ Please consider generating a new key file.</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>Unlock failed and no password given</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>El desbloqueig ha fallat i no s'ha donat cap contrasenya</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Unlocking the database failed and you did not enter a password.
|
||||
@ -1306,7 +1307,7 @@ This may prevent connection to the browser plugin.</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>Enable Browser Integration to access these settings.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Permet que la integració del navegador accedeixi a aquesta configuració.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Disconnect all browsers</source>
|
||||
@ -1594,7 +1595,7 @@ If you keep this number, your database may be too easy to crack!</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>History Settings</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Configuració de l'historial</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Max. history items:</source>
|
||||
@ -1937,7 +1938,7 @@ This is definitely a bug, please report it to the developers.</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>Export database to HTML file</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Exporta la base de dades a un fitxer HTML</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>HTML file</source>
|
||||
@ -2034,11 +2035,11 @@ Voleu fusionar els canvis?</translation>
|
||||
</message>
|
||||
<message numerus="yes">
|
||||
<source>Do you really want to delete %n entry(s) for good?</source>
|
||||
<translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
|
||||
<translation><numerusform>Esteu segur que voleu suprimir% n entrades per al Good?</numerusform><numerusform>Esteu segur que voleu suprimir %n entrada/es permanentment?</numerusform></translation>
|
||||
</message>
|
||||
<message numerus="yes">
|
||||
<source>Delete entry(s)?</source>
|
||||
<translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
|
||||
<translation><numerusform>Voleu suprimir les entrades?</numerusform><numerusform>Voleu suprimir la/es entrada/es?</numerusform></translation>
|
||||
</message>
|
||||
<message numerus="yes">
|
||||
<source>Move entry(s) to recycle bin?</source>
|
||||
@ -2102,7 +2103,7 @@ Voleu deshabilitar el desat segur i provar-ho un altre cop?</translation>
|
||||
</message>
|
||||
<message numerus="yes">
|
||||
<source>Entry "%1" has %2 reference(s). Do you want to overwrite references with values, skip this entry, or delete anyway?</source>
|
||||
<translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
|
||||
<translation><numerusform>L'entrada "%1" té %2 referències. Voleu sobreescriure les referències amb valors, ometre aquesta entrada o suprimir igualment?</numerusform><numerusform>L'entrada "%1" té %2 referència/es. Voleu sobreescriure les referències amb valors, ometre aquesta entrada, o suprimir igualment?</numerusform></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Delete group</source>
|
||||
@ -2126,7 +2127,7 @@ Voleu deshabilitar el desat segur i provar-ho un altre cop?</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Shared group...</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Grup compartit...</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Writing the database failed: %1</source>
|
||||
@ -2181,7 +2182,7 @@ Voleu deshabilitar el desat segur i provar-ho un altre cop?</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>File too large to be a private key</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Fitxer massa gran per ser una clau privada</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Failed to open private key</source>
|
||||
@ -2603,7 +2604,7 @@ Voleu deshabilitar el desat segur i provar-ho un altre cop?</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Remove key from agent when database is closed/locked</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Elimina la clau de l'agent quan la base de dades està tancada/bloquejada</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Public key</source>
|
||||
@ -2611,7 +2612,7 @@ Voleu deshabilitar el desat segur i provar-ho un altre cop?</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Add key to agent when database is opened/unlocked</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Afegeix la clau a l'agent quan la base de dades s'obri o es desbloquegi</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Comment</source>
|
||||
@ -2942,7 +2943,7 @@ Supported extensions are: %1.</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>No icons were loaded</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>No s'ha carrega cap icona</translation>
|
||||
</message>
|
||||
<message numerus="yes">
|
||||
<source>%n icon(s) already exist in the database</source>
|
||||
@ -2954,7 +2955,7 @@ Supported extensions are: %1.</source>
|
||||
</message>
|
||||
<message numerus="yes">
|
||||
<source>This icon is used by %n entry(s), and will be replaced by the default icon. Are you sure you want to delete it?</source>
|
||||
<translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
|
||||
<translation><numerusform>Aquesta icona s'utilitza en% n entrada (s), i serà substituïda per la icona per defecte. Esteu segur que voleu suprimir-la?</numerusform><numerusform>Aquesta icona s'usa en %n entrada/es, i serà substituïda per la icona per defecte. Esteu segur que voleu suprimir-la?</numerusform></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>You can enable the DuckDuckGo website icon service under Tools -> Settings -> Security</source>
|
||||
@ -2970,7 +2971,7 @@ Supported extensions are: %1.</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>Apply icon &to ...</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Usa la &icona a...</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Apply to this only</source>
|
||||
@ -3013,7 +3014,7 @@ Supported extensions are: %1.</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>Plugin Data</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Dades del connector</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Remove</source>
|
||||
@ -3065,7 +3066,7 @@ This may cause the affected plugins to malfunction.</source>
|
||||
<name>Entry</name>
|
||||
<message>
|
||||
<source>%1 - Clone</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>%1 - Clon</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@ -3076,7 +3077,7 @@ This may cause the affected plugins to malfunction.</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>Size</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Mida</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@ -3111,12 +3112,13 @@ This may cause the affected plugins to malfunction.</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>Save attachments</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Desa els fitxers adjunts</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Unable to create directory:
|
||||
%1</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>No s'ha pogut crear el directori:
|
||||
%1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Are you sure you want to overwrite the existing file "%1" with the attachment?</source>
|
||||
@ -3124,7 +3126,7 @@ This may cause the affected plugins to malfunction.</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>Confirm overwrite</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Confirma la sobreescriptura</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Unable to save attachments:
|
||||
@ -3143,7 +3145,7 @@ This may cause the affected plugins to malfunction.</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>Confirm remove</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Confirma l'eliminació</translation>
|
||||
</message>
|
||||
<message numerus="yes">
|
||||
<source>Unable to open file(s):
|
||||
@ -3254,7 +3256,7 @@ This may cause the affected plugins to malfunction.</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>Yes</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Sí</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>TOTP</source>
|
||||
@ -3329,12 +3331,12 @@ This may cause the affected plugins to malfunction.</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>[PROTECTED]</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>[PROTEGIT]</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><b>%1</b>: %2</source>
|
||||
<comment>attributes line</comment>
|
||||
<translation type="unfinished"/>
|
||||
<translation><b>%1</b>: %2</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Enabled</source>
|
||||
@ -3346,7 +3348,7 @@ This may cause the affected plugins to malfunction.</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>Share</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Comparteix</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Display current TOTP value</source>
|
||||
@ -3361,7 +3363,7 @@ This may cause the affected plugins to malfunction.</source>
|
||||
<name>EntryView</name>
|
||||
<message>
|
||||
<source>Customize View</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Personalitza la visualització</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Hide Usernames</source>
|
||||
@ -3373,19 +3375,19 @@ This may cause the affected plugins to malfunction.</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>Fit to window</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Ajusta a la finestra</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Fit to contents</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Ajusta al contingut</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Reset to defaults</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Reinicialitza-la als valors predeterminats</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Attachments (icon)</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Fitxers adjunts (icona)</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@ -3419,14 +3421,14 @@ This may cause the affected plugins to malfunction.</source>
|
||||
<message>
|
||||
<source>[empty]</source>
|
||||
<comment>group has no children</comment>
|
||||
<translation type="unfinished"/>
|
||||
<translation>[buit]</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>HostInstaller</name>
|
||||
<message>
|
||||
<source>KeePassXC: Cannot save file!</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>KeePassXC: no s'ha pogut desar el fitxer!</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Cannot save the native messaging script file.</source>
|
||||
@ -3512,19 +3514,19 @@ You can enable the DuckDuckGo website icon service in the security section of th
|
||||
</message>
|
||||
<message>
|
||||
<source>Header doesn't match hash</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>La capçalera no coincideix amb el hash</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Invalid header id size</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Mida de l'id de capçalera no vàlid</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Invalid header field length</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Longitud del camp de capçalera no vàlid</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Invalid header data length</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Longitud de dades de la capçalera no vàlida</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Invalid credentials were provided, please try again.
|
||||
@ -3567,15 +3569,15 @@ If this reoccurs, then your database file may be corrupt.</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>Invalid header id size</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Mida de l'id de capçalera no vàlid</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Invalid header field length</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Longitud del camp de capçalera no vàlid</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Invalid header data length</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Longitud de dades de la capçalera no vàlida</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Failed to open buffer for KDF parameters in header</source>
|
||||
@ -3591,7 +3593,7 @@ If this reoccurs, then your database file may be corrupt.</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>Invalid inner header id size</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Mida de l'identificador intern d'encapçalament no vàlida</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Invalid inner header field length</source>
|
||||
@ -3793,7 +3795,7 @@ Es tracta d'una migració unidireccional. No obrir la base de dades importa
|
||||
</message>
|
||||
<message>
|
||||
<source>Invalid EnableAutoType value</source>
|
||||
<translation>Valor d'EnableAutoType invàlid</translation>
|
||||
<translation>Valor no vàlid de compleció automàtica</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Invalid EnableSearching value</source>
|
||||
@ -4071,7 +4073,7 @@ If this reoccurs, then your database file may be corrupt.</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>Exported to %1</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Exportat a %1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Synchronized with %1</source>
|
||||
@ -4079,11 +4081,11 @@ If this reoccurs, then your database file may be corrupt.</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>Import is disabled in settings</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>La importació està inhabilitada a la configuració</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Export is disabled in settings</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>L'exportació està deshabilitada a la configuració</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Inactive share</source>
|
||||
@ -4095,7 +4097,7 @@ If this reoccurs, then your database file may be corrupt.</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>Exported to</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Exportat a</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Synchronized with</source>
|
||||
@ -4410,7 +4412,7 @@ We recommend you use the AppImage available on our downloads page.</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>&Import</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>& Importa</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Copy att&ribute...</source>
|
||||
@ -4446,11 +4448,11 @@ We recommend you use the AppImage available on our downloads page.</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>&Edit entry</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>&Edita l'entrada</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>View or edit entry</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Mostra o edita l'entrada</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>&New group</source>
|
||||
@ -4466,7 +4468,7 @@ We recommend you use the AppImage available on our downloads page.</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>&Database settings...</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Configuració de la base de &dades...</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Copy &password</source>
|
||||
@ -4474,7 +4476,7 @@ We recommend you use the AppImage available on our downloads page.</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>Perform &Auto-Type</source>
|
||||
<translation>Realitza una compleció &automàtic</translation>
|
||||
<translation>Fes una compleció &automàtica</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Open &URL</source>
|
||||
@ -4490,11 +4492,11 @@ We recommend you use the AppImage available on our downloads page.</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>CSV file...</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Fitxer CSV...</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Import a CSV file</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Importa un fitxer CSV</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Show TOTP...</source>
|
||||
@ -4524,7 +4526,7 @@ Podeu esperar alguns errors i incidències menors. Aquesta versió no està pens
|
||||
</message>
|
||||
<message>
|
||||
<source>&Export</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>&Exporta</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>&Check for Updates...</source>
|
||||
@ -4552,7 +4554,7 @@ Podeu esperar alguns errors i incidències menors. Aquesta versió no està pens
|
||||
</message>
|
||||
<message>
|
||||
<source>&Export to HTML file...</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>&Exporta a un fitxer HTML...</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>1Password Vault...</source>
|
||||
@ -4560,7 +4562,7 @@ Podeu esperar alguns errors i incidències menors. Aquesta versió no està pens
|
||||
</message>
|
||||
<message>
|
||||
<source>Import a 1Password Vault</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Importa un 1Password Vault</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>&Getting Started</source>
|
||||
@ -4836,7 +4838,7 @@ Podeu esperar alguns errors i incidències menors. Aquesta versió no està pens
|
||||
</message>
|
||||
<message>
|
||||
<source>Passphrase is required to decrypt this key</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Cal la frase de contrasenya per desxifrar aquesta clau</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Key derivation failed, key file corrupted?</source>
|
||||
@ -4844,7 +4846,7 @@ Podeu esperar alguns errors i incidències menors. Aquesta versió no està pens
|
||||
</message>
|
||||
<message>
|
||||
<source>Decryption failed, wrong passphrase?</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>El desxifrat ha fallat, la frase de contrasenya és errònia?</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Unexpected EOF while reading public key</source>
|
||||
@ -4982,7 +4984,7 @@ Podeu esperar alguns errors i incidències menors. Aquesta versió no està pens
|
||||
</message>
|
||||
<message>
|
||||
<source>Exclude look-alike characters</source>
|
||||
<translation>Excloure caràcters d'aspecte semblant</translation>
|
||||
<translation>Exclou caràcters d'aspecte similar</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Pick characters from every group</source>
|
||||
@ -4994,7 +4996,7 @@ Podeu esperar alguns errors i incidències menors. Aquesta versió no està pens
|
||||
</message>
|
||||
<message>
|
||||
<source>Passphrase</source>
|
||||
<translation>Contrasenya</translation>
|
||||
<translation>Frase de contrasenya</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Wordlist:</source>
|
||||
@ -5150,11 +5152,11 @@ Podeu esperar alguns errors i incidències menors. Aquesta versió no està pens
|
||||
</message>
|
||||
<message>
|
||||
<source>Upper-case letters</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Lletra majúscula</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Lower-case letters</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Lletra minúscula</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Special characters</source>
|
||||
@ -5170,7 +5172,7 @@ Podeu esperar alguns errors i incidències menors. Aquesta versió no està pens
|
||||
</message>
|
||||
<message>
|
||||
<source>Excluded characters</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Caràcters exclosos</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Hex Passwords</source>
|
||||
@ -5198,11 +5200,11 @@ Podeu esperar alguns errors i incidències menors. Aquesta versió no està pens
|
||||
</message>
|
||||
<message>
|
||||
<source>lower case</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>minúscules</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>UPPER CASE</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>MAJÚSCULES</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Title Case</source>
|
||||
@ -5376,7 +5378,7 @@ Podeu esperar alguns errors i incidències menors. Aquesta versió no està pens
|
||||
</message>
|
||||
<message>
|
||||
<source>Edit an entry.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Edita una entrada.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Title for the entry.</source>
|
||||
@ -5384,11 +5386,11 @@ Podeu esperar alguns errors i incidències menors. Aquesta versió no està pens
|
||||
</message>
|
||||
<message>
|
||||
<source>title</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>títol</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Path of the entry to edit.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Ruta de l'entrada a editar.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Estimate the entropy of a password.</source>
|
||||
@ -5400,7 +5402,7 @@ Podeu esperar alguns errors i incidències menors. Aquesta versió no està pens
|
||||
</message>
|
||||
<message>
|
||||
<source>Perform advanced analysis on the password.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Fes una anàlisi avançada de la contrasenya.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>WARNING: You are using a legacy key file format which may become
|
||||
@ -5530,16 +5532,17 @@ Available commands:
|
||||
</message>
|
||||
<message>
|
||||
<source>Generate a new random diceware passphrase.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Genera una frase de contrasenya nova amb llançament de daus.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Word count for the diceware passphrase.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Recompte de paraules per a la frase de contrasenya amb llançament de daus.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Wordlist for the diceware generator.
|
||||
[Default: EFF English]</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Llista de paraules per al generador de llançament de daus.
|
||||
[Per defecte: EFF English]</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Generate a new random password.</source>
|
||||
@ -5620,7 +5623,7 @@ Available commands:
|
||||
</message>
|
||||
<message>
|
||||
<source>Successfully edited entry %1.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>L'entrada %1 s'ha editat correctament.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Length %1</source>
|
||||
@ -5728,35 +5731,35 @@ Available commands:
|
||||
</message>
|
||||
<message>
|
||||
<source>Length of the generated password</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Longitud de la contrasenya generada</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Use lowercase characters</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Usa caràcters en minúscula</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Use uppercase characters</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Usa caràcters en majúscules</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Use special characters</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Usa caràcters especials</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Use extended ASCII</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Usa l'ASCII ampliat</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Exclude character set</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Exclou el conjunt de caràcters</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>chars</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>caràcters</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Exclude similar looking characters</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Exclou caràcters d'aspecte similar</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Include characters from every selected group</source>
|
||||
@ -5790,7 +5793,7 @@ Available commands:
|
||||
</message>
|
||||
<message>
|
||||
<source>Successfully deleted entry %1.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>L'entrada %1 s'ha suprimit correctament.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Show the entry's current TOTP.</source>
|
||||
@ -5810,7 +5813,7 @@ Available commands:
|
||||
</message>
|
||||
<message>
|
||||
<source>file empty</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>fitxer buit</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>%1: (row, col) %2,%3</source>
|
||||
@ -5818,15 +5821,15 @@ Available commands:
|
||||
</message>
|
||||
<message>
|
||||
<source>AES: 256-bit</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>AES: 256-bit</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Twofish: 256-bit</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Twofish: 256-bit</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>ChaCha20: 256-bit</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>ChaCha20:256-bits</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Argon2 (KDBX 4 – recommended)</source>
|
||||
@ -5834,21 +5837,21 @@ Available commands:
|
||||
</message>
|
||||
<message>
|
||||
<source>AES-KDF (KDBX 4)</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>AES-KDF (KDBX 4)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>AES-KDF (KDBX 3.1)</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>AES-KDF (KDBX 3,1)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Invalid Settings</source>
|
||||
<comment>TOTP</comment>
|
||||
<translation type="unfinished"/>
|
||||
<translation>La configuració no és vàlida</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Invalid Key</source>
|
||||
<comment>TOTP</comment>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Clau invàlida</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Message encryption failed.</source>
|
||||
@ -5860,7 +5863,7 @@ Available commands:
|
||||
</message>
|
||||
<message>
|
||||
<source>Create a new database.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Crea una nova base de dades.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>File %1 already exists.</source>
|
||||
@ -5892,7 +5895,7 @@ Available commands:
|
||||
</message>
|
||||
<message>
|
||||
<source>Path of the entry to remove.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Ruta de l'entrada a eliminar.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Existing single-instance lock file is invalid. Launching new instance.</source>
|
||||
@ -6100,7 +6103,7 @@ Nucli: %3 %4</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Format to use when exporting. Available choices are xml or csv. Defaults to xml.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Format a utilitzar en l'exportació. Les opcions disponibles són XML o CSV. Per defect s'usa XML.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Exports the content of a database to standard output in the specified format.</source>
|
||||
@ -6348,31 +6351,31 @@ Nucli: %3 %4</translation>
|
||||
<name>SearchHelpWidget</name>
|
||||
<message>
|
||||
<source>Search Help</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Cerca l'ajuda</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Search terms are as follows: [modifiers][field:]["]term["]</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Els termes de cerca són així: [Modificadors] [camp:] ["] terme ["]</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Every search term must match (ie, logical AND)</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Cada terme de cerca ha de coincidir (p.e. I lògica)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Modifiers</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Modificadors</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>exclude term from results</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>exclou el terme dels resultats</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>match term exactly</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>el terme ha de coincidir exactament</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>use regex in term</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>usa regex al terme</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Fields</source>
|
||||
@ -6380,19 +6383,19 @@ Nucli: %3 %4</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Term Wildcards</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Comodins</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>match anything</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>concorda amb qualsevol cosa</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>match one</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>coincideix amb un</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>logical OR</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>operador lògic O</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Examples</source>
|
||||
@ -6415,7 +6418,7 @@ Nucli: %3 %4</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Search Help</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Cerca l'ajuda</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Search (%1)...</source>
|
||||
@ -6530,7 +6533,7 @@ Nucli: %3 %4</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Own certificate</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Certificat propi</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Fingerprint:</source>
|
||||
@ -6538,11 +6541,11 @@ Nucli: %3 %4</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Certificate:</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Certificat:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Signer</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Signant</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Key:</source>
|
||||
@ -6594,7 +6597,7 @@ Nucli: %3 %4</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Certificate</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Certificat</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Trusted</source>
|
||||
@ -6627,23 +6630,23 @@ Nucli: %3 %4</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Exporting changed certificate</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Exportant el certificat modificat</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>The exported certificate is not the same as the one in use. Do you want to export the current certificate?</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>El certificat exportat no és el mateix que el que està en ús. Voleu exportar el certificat actual?</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Signer:</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Signant:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Allow KeeShare imports</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Permet les importacions de KeeShare</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Allow KeeShare exports</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Permet les exportacions de KeeShare</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Only show warnings and errors</source>
|
||||
@ -6741,7 +6744,7 @@ Nucli: %3 %4</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Import from container with certificate</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Importa des del contenidor amb certificat</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Do you want to trust %1 with the fingerprint of %2 from %3?</source>
|
||||
@ -6820,11 +6823,11 @@ Nucli: %3 %4</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Export to %1 failed (%2)</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>L'exportació a %1 ha fallat (%2)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Export to %1 successful (%2)</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>L'exportació a %1 s'ha realitzat amb èxit (%2)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Export to %1</source>
|
||||
@ -6952,7 +6955,7 @@ Example: JBSWY3DPEHPK3PXP</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>Are you sure you want to delete TOTP settings for this entry?</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Esteu segur que voleu suprimir la configuració TOTP de l'entrada?</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@ -7038,7 +7041,7 @@ Example: JBSWY3DPEHPK3PXP</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>Import from 1Password</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Importa des de 1Password</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Open a recent database</source>
|
||||
|
@ -3462,7 +3462,7 @@ Det kan få de påvirkede plugins til at svigte.</translation>
|
||||
<name>IconDownloaderDialog</name>
|
||||
<message>
|
||||
<source>Download Favicons</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Download faviconer</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Cancel</source>
|
||||
@ -4556,27 +4556,27 @@ Forvent nogle fejl og mindre problemer. Denne version er ikke beregnet til produ
|
||||
</message>
|
||||
<message>
|
||||
<source>&Export</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>&Eksportér</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>&Check for Updates...</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>&Søg efter opdateringer ...</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Downlo&ad all favicons</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>&Download alle faviconer</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Sort &A-Z</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Sortér &A-Å</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Sort &Z-A</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Sortér &Å-A</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>&Password Generator</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>&Adgangskodegenerator</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Download favicon</source>
|
||||
@ -4584,19 +4584,19 @@ Forvent nogle fejl og mindre problemer. Denne version er ikke beregnet til produ
|
||||
</message>
|
||||
<message>
|
||||
<source>&Export to HTML file...</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>&Eksportér til HTML-fil ...</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>1Password Vault...</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>1Password-boks ...</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Import a 1Password Vault</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Importér en 1Password-boks</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>&Getting Started</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>&Kom godt i gang</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Open Getting Started Guide PDF</source>
|
||||
@ -4604,7 +4604,7 @@ Forvent nogle fejl og mindre problemer. Denne version er ikke beregnet til produ
|
||||
</message>
|
||||
<message>
|
||||
<source>&Online Help...</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>&Onlinehjælp ...</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Go to online documentation (opens browser)</source>
|
||||
@ -4612,7 +4612,7 @@ Forvent nogle fejl og mindre problemer. Denne version er ikke beregnet til produ
|
||||
</message>
|
||||
<message>
|
||||
<source>&User Guide</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>&Brugerguide</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Open User Guide PDF</source>
|
||||
@ -4620,7 +4620,7 @@ Forvent nogle fejl og mindre problemer. Denne version er ikke beregnet til produ
|
||||
</message>
|
||||
<message>
|
||||
<source>&Keyboard Shortcuts</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>&Tastaturgenveje</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@ -7077,7 +7077,7 @@ Example: JBSWY3DPEHPK3PXP</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>Import from 1Password</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Importér fra 1Password</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Open a recent database</source>
|
||||
|
@ -574,8 +574,8 @@ Bitte wählen Sie, ob Sie den Zugriff erlauben möchten.</translation>
|
||||
<message>
|
||||
<source>You have multiple databases open.
|
||||
Please select the correct database for saving credentials.</source>
|
||||
<translation>Du hast mehrere Datenbanken geöffnet.
|
||||
Bitte wähle die richtige Datenbank zum speichern der Anmeldedaten.</translation>
|
||||
<translation>Sie haben mehrere Datenbanken geöffnet.
|
||||
Bitte wählen Sie die richtige Datenbank zum Speichern der Anmeldedaten.</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@ -1070,7 +1070,7 @@ Sicherungsdatenbank bei %2</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Database file has unmerged changes.</source>
|
||||
<translation>Die Datenbankdatei hat Änderungen die noch nicht gemergt wurden.</translation>
|
||||
<translation>Die Datenbankdatei hat Änderungen die noch nicht zusammengeführt wurden.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Recycle Bin</source>
|
||||
@ -1217,7 +1217,7 @@ Um zu verhindern, dass dieser Fehler auftritt, müssen Sie zu "Datenbankein
|
||||
</message>
|
||||
<message>
|
||||
<source><p>In addition to your master password, you can use a secret file to enhance the security of your database. Such a file can be generated in your database's security settings.</p><p>This is <strong>not</strong> your *.kdbx database file!<br>If you do not have a key file, leave the field empty.</p><p>Click for more information...</p></source>
|
||||
<translation><p>Zusätzlich zu ihrem Masterpasswort können sie eine geheime Datei verwenden um die Sicherheit ihrer Datenbank zu erhöhen. Eine solche Datei kann in den Sicherheitseinstellung ihrer Datenbank generiert werden.</p> <p>Diese Datei ist jedoch <strong>nicht</strong> Ihre *.kdbx Datei!<br> Wenn sie keine Schlüsseldatei haben lassen sie das Feld leer.</p> <p>Klicken sie hier für weitere Informationen...</p></translation>
|
||||
<translation><p>Zusätzlich zu Ihrem Masterpasswort können Sie eine geheime Datei verwenden, um die Sicherheit Ihrer Datenbank zu erhöhen. Eine solche Datei kann in den Sicherheitseinstellungen Ihrer Datenbank generiert werden.</p> <p>Diese Datei ist jedoch <strong>nicht</strong> Ihre *.kdbx-Datei!<br> Wenn Sie keine Schlüsseldatei haben, lassen Sie das Feld leer.</p> <p>Klicken Sie hier für weitere Informationen...</p></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Key file help</source>
|
||||
@ -1331,7 +1331,7 @@ Das wird die Verbindung zum Browser-Plugin verhindern.</translation>
|
||||
<message>
|
||||
<source>Do you really want to disconnect all browsers?
|
||||
This may prevent connection to the browser plugin.</source>
|
||||
<translation>Möchtest du wirklich alle Browserverbindungen entfernen?
|
||||
<translation>Möchten Sie wirklich alle Browserverbindungen entfernen?
|
||||
Das wird die Verbindung zu dem Browser-Plugin verhindern.</translation>
|
||||
</message>
|
||||
<message>
|
||||
@ -3094,7 +3094,7 @@ Dies kann dazu führen, dass die jeweiligen Plugins nicht mehr richtig funktioni
|
||||
<name>Entry</name>
|
||||
<message>
|
||||
<source>%1 - Clone</source>
|
||||
<translation>%1 - Kopie</translation>
|
||||
<translation>%1 - Klon</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@ -3608,7 +3608,7 @@ Falls dies wiederholt passiert, dann könnte Ihre Datenbank beschädigt sein.</t
|
||||
</message>
|
||||
<message>
|
||||
<source>Invalid header field length</source>
|
||||
<translation>Ungültiger Header-Feldlänge</translation>
|
||||
<translation>Ungültige Header-Feldlänge</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Invalid header data length</source>
|
||||
@ -4451,8 +4451,8 @@ Diese Version ist nicht für den Produktiveinsatz gedacht.</translation>
|
||||
<message>
|
||||
<source>WARNING: Your Qt version may cause KeePassXC to crash with an On-Screen Keyboard!
|
||||
We recommend you use the AppImage available on our downloads page.</source>
|
||||
<translation>WARNUNG: Deine Qt Version könnte KeePassXC mit einer Bildschirmtastatur zu abstürzen bringen!
|
||||
Wir empfehlen dir die Verwendung des auf unserer Downloadseite verfügbaren AppImage.</translation>
|
||||
<translation>WARNUNG: Ihre Qt Version könnte KeePassXC mit einer Bildschirmtastatur zu abstürzen bringen!
|
||||
Wir empfehlen Ihnen die Verwendung des auf unserer Downloadseite verfügbaren AppImage.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>&Import</source>
|
||||
@ -4665,7 +4665,7 @@ Da sie Fehler beinhalten könnte, ist diese Version nicht für den Produktiveins
|
||||
</message>
|
||||
<message>
|
||||
<source>Reapplying older target entry on top of newer source %1 [%2]</source>
|
||||
<translation>Älterer Ziel-Eintrag wird auf neuere Quellen-Eintrag angewendet %1 [%2]</translation>
|
||||
<translation>Älterer Ziel-Eintrag wird auf neueren Quellen-Eintrag angewendet %1 [%2]</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Reapplying older source entry on top of newer target %1 [%2]</source>
|
||||
@ -4758,7 +4758,7 @@ Da sie Fehler beinhalten könnte, ist diese Version nicht für den Produktiveins
|
||||
</message>
|
||||
<message>
|
||||
<source>A master key known only to you protects your database.</source>
|
||||
<translation>Ein nur dir bekannter Schlüssel schützt die Datenbank.</translation>
|
||||
<translation>Ein nur Ihnen bekannter Schlüssel schützt die Datenbank.</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@ -5287,7 +5287,7 @@ Da sie Fehler beinhalten könnte, ist diese Version nicht für den Produktiveins
|
||||
</message>
|
||||
<message>
|
||||
<source>Empty</source>
|
||||
<translation>Leer</translation>
|
||||
<translation>Leeren</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Remove</source>
|
||||
|
@ -2340,6 +2340,14 @@ Disable safe saves and try again?</translation>
|
||||
<source>Are you sure you want to remove this URL?</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Reveal</source>
|
||||
<translation type="unfinished">Reveal</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Hide</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>EditEntryWidgetAdvanced</name>
|
||||
@ -2521,6 +2529,14 @@ Disable safe saves and try again?</translation>
|
||||
<source>Edit</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Only send this setting to the browser for HTTP Auth dialogs. If enabled, normal login forms will not show this entry for selection.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Use this entry only with HTTP Basic Auth</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>EditEntryWidgetHistory</name>
|
||||
@ -3455,6 +3471,13 @@ This may cause the affected plugins to malfunction.</translation>
|
||||
<translation type="unfinished">Advanced</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>EntryURLModel</name>
|
||||
<message>
|
||||
<source>Invalid URL</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>EntryView</name>
|
||||
<message>
|
||||
@ -3508,6 +3531,40 @@ This may cause the affected plugins to malfunction.</translation>
|
||||
</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>FdoSecrets::SettingsDatabaseModel</name>
|
||||
<message>
|
||||
<source>File Name</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Group</source>
|
||||
<translation type="unfinished">Group</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Manage</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Unlock to show</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>None</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>FdoSecrets::SettingsSessionModel</name>
|
||||
<message>
|
||||
<source>Application</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Manage</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>FdoSecretsPlugin</name>
|
||||
<message>
|
||||
@ -4699,6 +4756,40 @@ Expect some bugs and minor issues, this version is not meant for production use.
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ManageDatabase</name>
|
||||
<message>
|
||||
<source>Database settings</source>
|
||||
<translation type="unfinished">Database settings</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Edit database settings</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Unlock database</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Unlock database to show more information</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Lock database</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ManageSession</name>
|
||||
<message>
|
||||
<source>Disconnect</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Disconnect this application</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>Merger</name>
|
||||
<message>
|
||||
@ -6576,18 +6667,6 @@ Kernel: %3 %4</source>
|
||||
<source>Exposed database groups:</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>File Name</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Group</source>
|
||||
<translation type="unfinished">Group</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Manage</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Authorization</source>
|
||||
<translation type="unfinished"></translation>
|
||||
@ -6596,42 +6675,6 @@ Kernel: %3 %4</source>
|
||||
<source>These applications are currently connected:</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Application</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Disconnect</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Database settings</source>
|
||||
<translation type="unfinished">Database settings</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Edit database settings</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Unlock database</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Unlock database to show more information</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Lock database</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Unlock to show</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>None</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>SettingsWidgetKeeShare</name>
|
||||
@ -7077,6 +7120,13 @@ Example: JBSWY3DPEHPK3PXP</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>URLEdit</name>
|
||||
<message>
|
||||
<source>Invalid URL</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>UpdateCheckDialog</name>
|
||||
<message>
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -6199,7 +6199,7 @@ CPU アーキテクチャー: %2
|
||||
</message>
|
||||
<message>
|
||||
<source>Unable to import XML database export %1</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>XMLデータベース %1 をインポートできません</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Successfully imported database.</source>
|
||||
|
@ -450,7 +450,7 @@
|
||||
</message>
|
||||
<message>
|
||||
<source>KeePassXC requires the Accessibility permission in order to perform entry level Auto-Type. If you already granted permission, you may have to restart KeePassXC.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>항목별 자동 입력을 사용하려면 KeePassXC에 접근성 권한을 허용해야 합니다. 권한을 이미 허용했다면 KeePassXC를 다시 시작해 보십시오.</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@ -506,7 +506,7 @@
|
||||
</message>
|
||||
<message>
|
||||
<source>KeePassXC requires the Accessibility and Screen Recorder permission in order to perform global Auto-Type. Screen Recording is necessary to use the window title to find entries. If you already granted permission, you may have to restart KeePassXC.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>항목별 자동 입력을 사용하려면 KeePassXC에 접근성 및 화면 녹화 권한을 허용해야 합니다. 화면 녹화 권한은 창 제목과 항목을 찾기 위해서 필요합니다. 권한을 이미 허용했다면 KeePassXC를 다시 시작해 보십시오.</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@ -878,7 +878,11 @@ Would you like to migrate your existing settings now?</source>
|
||||
|
||||
Give the connection a unique name or ID, for example:
|
||||
chrome-laptop.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>다음 데이터베이스 연결 요청을 받았습니다:
|
||||
%1
|
||||
|
||||
데이터베이스 연결을 식별할 수 있는 이름이나 ID를 입력하십시오. 예:
|
||||
chrome-laptop.</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@ -1211,11 +1215,12 @@ To prevent this error from appearing, you must go to "Database Settings / S
|
||||
<message>
|
||||
<source><p>You can use a hardware security key such as a <strong>YubiKey</strong> or <strong>OnlyKey</strong> with slots configured for HMAC-SHA1.</p>
|
||||
<p>Click for more information...</p></source>
|
||||
<translation type="unfinished"/>
|
||||
<translation><p><strong>YubiKey</strong>나 <strong>OnlyKey</strong>와 같은 하드웨어 키의 슬롯을 HMAC-SHA1로 설정 시 사용할 수 있습니다.</p>
|
||||
<p>더 많은 정보를 보려면 누르십시오...</p></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><p>In addition to your master password, you can use a secret file to enhance the security of your database. Such a file can be generated in your database's security settings.</p><p>This is <strong>not</strong> your *.kdbx database file!<br>If you do not have a key file, leave the field empty.</p><p>Click for more information...</p></source>
|
||||
<translation type="unfinished"/>
|
||||
<translation><p>데이터베이스 보안 향상을 위하여 마스터 암호 외에도 비밀 파일을 사용할 수 있습니다. 데이터베이스 보안 설정에서 비밀 파일을 생성할 수 있습니다.</p><p>이 파일은 *.kdbx 데이터베이스 파일이 <strong>아닙니다!</strong><br>만약 키 파일이 없다면 이 필드를 비워 두십시오.</p><p>더 많은 정보를 보려면 누르십시오...</p> </translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Key file help</source>
|
||||
@ -1881,7 +1886,7 @@ Are you sure you want to continue without a password?</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>Please wait, database statistics are being calculated...</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>데이터베이스 통계를 계산하는 동안 잠시 기다려 주십시오...</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@ -6322,7 +6327,7 @@ CPU 아키텍처: %2
|
||||
</message>
|
||||
<message>
|
||||
<source>Show the protected attributes in clear text.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>보호된 속성을 평문으로 표시합니다.</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
|
@ -35,7 +35,7 @@
|
||||
</message>
|
||||
<message>
|
||||
<source>Copy to clipboard</source>
|
||||
<translation>Kopier til utklippstavla</translation>
|
||||
<translation>Kopier til utklippstavle</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Project Maintainers:</source>
|
||||
@ -97,11 +97,11 @@
|
||||
</message>
|
||||
<message>
|
||||
<source>Reset Settings?</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Tilbakestill innstillinger?</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Are you sure you want to reset all general and security settings to default?</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Er du sikker på at du vil tilbakestille alle generelle og sikkerhetsinnstillinger til standard?</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@ -249,7 +249,7 @@
|
||||
</message>
|
||||
<message>
|
||||
<source>Language:</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Språk:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>(restart program to activate)</source>
|
||||
@ -269,7 +269,7 @@
|
||||
</message>
|
||||
<message>
|
||||
<source>Minimize</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Minimer</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Drop to background</source>
|
||||
@ -298,7 +298,7 @@
|
||||
</message>
|
||||
<message>
|
||||
<source>Language selection</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Valg av språk</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Reset Settings to Default</source>
|
||||
@ -411,7 +411,7 @@
|
||||
</message>
|
||||
<message>
|
||||
<source>Clear search query after</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Tøm søket etter</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@ -446,7 +446,7 @@
|
||||
</message>
|
||||
<message>
|
||||
<source>Permission Required</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Tillatelse kreves</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>KeePassXC requires the Accessibility permission in order to perform entry level Auto-Type. If you already granted permission, you may have to restart KeePassXC.</source>
|
||||
@ -502,7 +502,7 @@
|
||||
<name>AutoTypePlatformMac</name>
|
||||
<message>
|
||||
<source>Permission Required</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Tillatelse kreves</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>KeePassXC requires the Accessibility and Screen Recorder permission in order to perform global Auto-Type. Screen Recording is necessary to use the window title to find entries. If you already granted permission, you may have to restart KeePassXC.</source>
|
||||
@ -550,7 +550,7 @@ Velg om du vil gi tilgang eller ikke.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Allow access</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Gi tilgang</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Deny access</source>
|
||||
@ -743,7 +743,7 @@ Vennligst velg riktig database for å lagre legitimasjon.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>&Brave</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>&Brave</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Returns expired credentials. String [expired] is added to the title.</source>
|
||||
@ -1124,15 +1124,15 @@ Vurder å opprette en ny nøkkelfil.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Unlock KeePassXC Database</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Låse opp KeePassXC Database</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Enter Password:</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Oppgi passord:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Password field</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Passord felt</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Toggle password visibility</source>
|
||||
@ -1191,7 +1191,7 @@ To prevent this error from appearing, you must go to "Database Settings / S
|
||||
</message>
|
||||
<message>
|
||||
<source>Retry with empty password</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Prøv igjen med tomt passord</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Enter Additional Credentials (if any):</source>
|
||||
@ -1726,7 +1726,7 @@ Er du sikker på at du vil fortsette uten passord?</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Continue without password</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Fortsett uten passord</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@ -2550,7 +2550,7 @@ Deaktivere sikker lagring og prøve igjen?</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Password field</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Passord felt</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Toggle password visibility</source>
|
||||
@ -2826,7 +2826,7 @@ Supported extensions are: %1.</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>Password field</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Passord felt</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Toggle password visibility</source>
|
||||
@ -4555,7 +4555,7 @@ Expect some bugs and minor issues, this version is not meant for production use.
|
||||
</message>
|
||||
<message>
|
||||
<source>&Password Generator</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>&Passord generator</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Download favicon</source>
|
||||
@ -4906,7 +4906,7 @@ Expect some bugs and minor issues, this version is not meant for production use.
|
||||
<name>PasswordEdit</name>
|
||||
<message>
|
||||
<source>Passwords do not match</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Passordene er ikke like</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Passwords match so far</source>
|
||||
@ -4941,7 +4941,7 @@ Expect some bugs and minor issues, this version is not meant for production use.
|
||||
</message>
|
||||
<message>
|
||||
<source>Password field</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Passord felt</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Toggle password visibility</source>
|
||||
@ -6248,7 +6248,7 @@ Kjerne: %3 %4</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Enter password to unlock %1: </source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Skriv inn passord for å låse opp %1:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Invalid YubiKey slot %1</source>
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -747,11 +747,11 @@ Selecione a base de dados correta para guardar as credenciais.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Returns expired credentials. String [expired] is added to the title.</source>
|
||||
<translation>Devolve as credenciais expiradas. Adiciona [expirada] ao título.</translation>
|
||||
<translation>Devolve as credenciais caducadas. Adiciona [caducada] ao título.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>&Allow returning expired credentials.</source>
|
||||
<translation>Permitir devolução de credencias expir&adas</translation>
|
||||
<translation>Permitir devolução de credencias caduc&adas</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Enable browser integration</source>
|
||||
@ -1830,11 +1830,11 @@ Tem a certeza de que deseja continuar?</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Number of expired entries</source>
|
||||
<translation>Número de entradas expiradas</translation>
|
||||
<translation>Número de entradas caducadas</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>The database contains entries that have expired.</source>
|
||||
<translation>A base de dados contém entradas expiradas.</translation>
|
||||
<translation>A base de dados contém entradas caducadas.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Unique passwords</source>
|
||||
@ -2553,7 +2553,7 @@ Desativar salvaguardas e tentar novamente?</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Expires</source>
|
||||
<translation>Expira</translation>
|
||||
<translation>Caduca</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Url field</source>
|
||||
@ -2585,15 +2585,15 @@ Desativar salvaguardas e tentar novamente?</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Expiration field</source>
|
||||
<translation>Campo Expira</translation>
|
||||
<translation>Campo Caduca</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Expiration Presets</source>
|
||||
<translation>Predefinições de expiração</translation>
|
||||
<translation>Predefinições de caducidade</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Expiration presets</source>
|
||||
<translation>Predefinições de expiração</translation>
|
||||
<translation>Predefinições de caducidade</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Notes field</source>
|
||||
@ -2609,7 +2609,7 @@ Desativar salvaguardas e tentar novamente?</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Toggle expiration</source>
|
||||
<translation>Alternar expiração</translation>
|
||||
<translation>Alternar caducidade</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@ -2813,11 +2813,11 @@ As extensões suportadas são: %1.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>%1 is already being exported by this database.</source>
|
||||
<translation>%1 já está a ser exportada para esta base de dados.</translation>
|
||||
<translation>%1 já está a ser exportado para esta base de dados.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>%1 is already being imported by this database.</source>
|
||||
<translation>%1 já está a ser importada para esta base de dados.</translation>
|
||||
<translation>%1 já está a ser importado para esta base de dados.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>%1 is being imported and exported by different groups in this database.</source>
|
||||
@ -2877,7 +2877,7 @@ As extensões suportadas são: %1.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Expires</source>
|
||||
<translation>Expira</translation>
|
||||
<translation>Caduca</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Search</source>
|
||||
@ -2905,7 +2905,7 @@ As extensões suportadas são: %1.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Toggle expiration</source>
|
||||
<translation>Alternar expiração</translation>
|
||||
<translation>Alternar caducidade</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Auto-Type toggle for this and sub groups</source>
|
||||
@ -2913,7 +2913,7 @@ As extensões suportadas são: %1.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Expiration field</source>
|
||||
<translation>Campo Expira</translation>
|
||||
<translation>Campo Caduca</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Search toggle for this and sub groups</source>
|
||||
@ -3271,7 +3271,7 @@ Esta ação pode implicar um funcionamento errático.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Expires</source>
|
||||
<translation>Expira</translation>
|
||||
<translation>Caduca</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Created</source>
|
||||
@ -3318,7 +3318,7 @@ Esta ação pode implicar um funcionamento errático.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Expiration</source>
|
||||
<translation>Expira</translation>
|
||||
<translation>Caducidade</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>URL</source>
|
||||
@ -4029,7 +4029,7 @@ Linha %2, coluna %3</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Incorrect group expiry time field size</source>
|
||||
<translation>Tamanho de campo de tempo de expiração de grupo incorreto</translation>
|
||||
<translation>Tamanho inválido para o campo de caducidade do grupo</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Incorrect group icon field size</source>
|
||||
@ -4081,7 +4081,7 @@ Linha %2, coluna %3</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Invalid entry expiry time field size</source>
|
||||
<translation>Tamanho da entrada para o campo tempo de expiração inválido</translation>
|
||||
<translation>Tamanho inválido para o campo de caducidade da entrada</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Invalid entry field type</source>
|
||||
@ -4181,7 +4181,7 @@ Caso isto volte a acontecer, pode ser que a base de dados esteja danificada.</tr
|
||||
<message>
|
||||
<source>%1 set, click to change or remove</source>
|
||||
<comment>Change or remove a key component</comment>
|
||||
<translation>%1 definido, clique para alterar ou remover</translation>
|
||||
<translation>%1 definida, clique para alterar ou remover</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@ -6912,7 +6912,7 @@ Kernel: %3 %4</translation>
|
||||
</message>
|
||||
<message numerus="yes">
|
||||
<source>Expires in <b>%n</b> second(s)</source>
|
||||
<translation><numerusform>Expira em <b>%n</b> segundo</numerusform><numerusform>Expira dentro de <b>%n</b> segundos</numerusform></translation>
|
||||
<translation><numerusform>Caduca dentro de <b>%n</b> segundo</numerusform><numerusform>Caduca dentro de <b>%n</b> segundos</numerusform></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -101,7 +101,7 @@
|
||||
</message>
|
||||
<message>
|
||||
<source>Are you sure you want to reset all general and security settings to default?</source>
|
||||
<translation>Вы уверены, что вы хотите сбросить по умолчанию все общие настройки и настройки безопасности? </translation>
|
||||
<translation>Действительно сбросить все общие параметры и параметры безопасности к значениям, заданным по умолчанию? </translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@ -325,7 +325,7 @@
|
||||
</message>
|
||||
<message>
|
||||
<source>Clear clipboard after</source>
|
||||
<translation>Задержка очистки поискового запроса:</translation>
|
||||
<translation>Задержка очистки буфера обмена:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source> sec</source>
|
||||
@ -382,7 +382,7 @@
|
||||
</message>
|
||||
<message>
|
||||
<source>Hide entry notes by default</source>
|
||||
<translation>Скрывать заметки по умолчанию</translation>
|
||||
<translation>Не показывать текст заметкок </translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Privacy</source>
|
||||
@ -521,7 +521,7 @@
|
||||
</message>
|
||||
<message>
|
||||
<source>Search...</source>
|
||||
<translation>Поиск...</translation>
|
||||
<translation>Поиск…</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@ -640,12 +640,12 @@ Please select the correct database for saving credentials.</source>
|
||||
<message>
|
||||
<source>Sort &matching credentials by title</source>
|
||||
<extracomment>Credentials mean login data requested via browser extension</extracomment>
|
||||
<translation>Сортировать &подходящие учётные данные по названию</translation>
|
||||
<translation>Сортировать возвращаемые данные по &названию</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Sort matching credentials by &username</source>
|
||||
<extracomment>Credentials mean login data requested via browser extension</extracomment>
|
||||
<translation>Сортировать по &имени пользователя</translation>
|
||||
<translation>Сортировать возвращаемые данные по &имени пользователя</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Advanced</source>
|
||||
@ -654,17 +654,17 @@ Please select the correct database for saving credentials.</source>
|
||||
<message>
|
||||
<source>Never &ask before accessing credentials</source>
|
||||
<extracomment>Credentials mean login data requested via browser extension</extracomment>
|
||||
<translation>Никогда не &спрашивать перед доступом к учётным данным</translation>
|
||||
<translation>Не подтверждать &доступ к записям</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Never ask before &updating credentials</source>
|
||||
<extracomment>Credentials mean login data requested via browser extension</extracomment>
|
||||
<translation>Никогда не спрашивать перед &обновлением учётных данных</translation>
|
||||
<translation>Не подтверждать &обновление записей</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Searc&h in all opened databases for matching credentials</source>
|
||||
<extracomment>Credentials mean login data requested via browser extension</extracomment>
|
||||
<translation>&Поиск во всех открытых базах для сопоставления учётных данных</translation>
|
||||
<translation>Искать во &всех открытых базах</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Automatically creating or updating string fields is not supported.</source>
|
||||
@ -688,7 +688,7 @@ Please select the correct database for saving credentials.</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>Use a &proxy application between KeePassXC and browser extension</source>
|
||||
<translation>Использование &прокси-приложения между KeePassXC и расширением браузера</translation>
|
||||
<translation>Использовать &прокси-приложение между KeePassXC и расширением браузера</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Use a custom proxy location if you installed a proxy manually.</source>
|
||||
@ -697,12 +697,12 @@ Please select the correct database for saving credentials.</source>
|
||||
<message>
|
||||
<source>Use a &custom proxy location</source>
|
||||
<comment>Meant is the proxy for KeePassXC-Browser</comment>
|
||||
<translation>Использовать &пользовательское местоположение прокси</translation>
|
||||
<translation>Задать &своё расположение прокси</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Browse...</source>
|
||||
<extracomment>Button for opening file dialog</extracomment>
|
||||
<translation>Обзор...</translation>
|
||||
<translation>Обзор…</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><b>Warning:</b> The following options can be dangerous!</source>
|
||||
@ -739,7 +739,7 @@ Please select the correct database for saving credentials.</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>KeePassXC-Browser is needed for the browser integration to work. <br />Download it for %1 and %2. %3</source>
|
||||
<translation>Для интеграции с браузерами требуется KeePassXC-Browser. <br />Скачайте его для %1 и %2. %3</translation>
|
||||
<translation>Для интеграции требуется установить расширение для браузера «KeePassXC-Browser». <br />Установите его для %1 и %2. %3</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>&Brave</source>
|
||||
@ -751,7 +751,7 @@ Please select the correct database for saving credentials.</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>&Allow returning expired credentials.</source>
|
||||
<translation>Разрешить возвращать &истёкшие записи</translation>
|
||||
<translation>Возвращать &истёкшие записи</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Enable browser integration</source>
|
||||
@ -763,7 +763,7 @@ Please select the correct database for saving credentials.</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>All databases connected to the extension will return matching credentials.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Получать результаты поиска из всех подключённых баз данных</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Don't display the popup suggesting migration of legacy KeePassHTTP settings.</source>
|
||||
@ -1129,11 +1129,11 @@ Please consider generating a new key file.</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>Select slot...</source>
|
||||
<translation>Выбрать слот...</translation>
|
||||
<translation>Выберите слот…</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Unlock KeePassXC Database</source>
|
||||
<translation>Разблокировать базу данных KeePassXC</translation>
|
||||
<translation>Открытие базы данных KeePassXC</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Enter Password:</source>
|
||||
@ -1157,7 +1157,7 @@ Please consider generating a new key file.</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>Browse for key file</source>
|
||||
<translation>Открытие диалога выбора файла-ключа</translation>
|
||||
<translation>Открыть диалога выбора файла-ключа</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Browse...</source>
|
||||
@ -1308,7 +1308,7 @@ If you do not have a key file, please leave the field empty.</source>
|
||||
<message>
|
||||
<source>Do you really want to delete the selected key?
|
||||
This may prevent connection to the browser plugin.</source>
|
||||
<translation>Вы действительно хотите удалить выбранный ключ?
|
||||
<translation>Действительно удалить выбранный ключ?
|
||||
Это может воспрепятствовать соединению с подключаемым модулем браузера.</translation>
|
||||
</message>
|
||||
<message>
|
||||
@ -1580,19 +1580,19 @@ If you keep this number, your database may be too easy to crack!</source>
|
||||
<name>DatabaseSettingsWidgetFdoSecrets</name>
|
||||
<message>
|
||||
<source>Exposed Entries</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Доступ к записям</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Don't e&xpose this database</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Не &публиковать эту базу данных</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Expose entries &under this group:</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>&Предоставить доступ к записям выбранной группы:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Enable fd.o Secret Service to access these settings.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Для доступа к этим параметрам включите службу Secret Service.</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@ -1774,7 +1774,7 @@ Are you sure you want to continue without a password?</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>Hover over lines with error icons for further information.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Для получения дополнительной информации наведите курсор мыши на строки, содержащие значок ошибки.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Name</source>
|
||||
@ -1958,7 +1958,7 @@ This is definitely a bug, please report it to the developers.</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>Failed to open %1. It either does not exist or is not accessible.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Не удалось открыть «%1», файл не существует или недоступен.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Export database to HTML file</source>
|
||||
@ -2222,7 +2222,7 @@ Disable safe saves and try again?</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>Edit entry</source>
|
||||
<translation>Редактировать запись</translation>
|
||||
<translation>Редактирование записи</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Different passwords supplied.</source>
|
||||
@ -2262,7 +2262,7 @@ Disable safe saves and try again?</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>Entry has unsaved changes</source>
|
||||
<translation>В записи есть несохранённые изменения</translation>
|
||||
<translation>Запись содержит несохранённые изменения.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>New attribute %1</source>
|
||||
@ -2278,7 +2278,7 @@ Disable safe saves and try again?</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>Confirm Removal</source>
|
||||
<translation>Подтвердите удаление</translation>
|
||||
<translation>Подтверждение удаления</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Browser Integration</source>
|
||||
@ -2408,7 +2408,7 @@ Disable safe saves and try again?</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>Open Auto-Type help webpage</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Открыть страницу справки по авто-вводу</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Existing window associations</source>
|
||||
@ -2685,7 +2685,7 @@ Disable safe saves and try again?</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>Remove key from agent after specified seconds</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Убрать ключ из агента по истечению заданного интервала времени</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Browser for key file</source>
|
||||
@ -2763,7 +2763,7 @@ Disable safe saves and try again?</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>Inactive</source>
|
||||
<translation>Неактивные</translation>
|
||||
<translation>Не участвует в обмене</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>KeeShare unsigned container</source>
|
||||
@ -2791,32 +2791,32 @@ Disable safe saves and try again?</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>Import</source>
|
||||
<translation>Импортировать</translation>
|
||||
<translation>Импортируется</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Export</source>
|
||||
<translation>Экспортировать</translation>
|
||||
<translation>Экспортируется</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Synchronize</source>
|
||||
<translation>Синхронизировать</translation>
|
||||
<translation>Синхронизируется</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Your KeePassXC version does not support sharing this container type.
|
||||
Supported extensions are: %1.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Установленная версия KeePassXC не поддерживает совместное использование контейнера такого типа. Список поддерживаемых расширений: %1.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>%1 is already being exported by this database.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>%1 уже экспортируется этой базой данных.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>%1 is already being imported by this database.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>%1 уже импортируется этой базой данных.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>%1 is being imported and exported by different groups in this database.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>%1 уже экспортируется и импортируется различными группами этой базы данных.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>KeeShare is currently disabled. You can enable import/export in the application settings.</source>
|
||||
@ -2833,15 +2833,15 @@ Supported extensions are: %1.</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>Sharing mode field</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Поле состояния общего доступа</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Path to share file field</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Поле пути к общему файлу</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Browser for share file</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Запуск диалога открытия общего файла</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Password field</source>
|
||||
@ -3038,7 +3038,7 @@ Supported extensions are: %1.</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>Plugin Data</source>
|
||||
<translation>Данные подключаемых модулей</translation>
|
||||
<translation>Данные подключаемого модуля браузера</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Remove</source>
|
||||
@ -3046,13 +3046,13 @@ Supported extensions are: %1.</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>Delete plugin data?</source>
|
||||
<translation>Удалить данные подключаемых модулей?</translation>
|
||||
<translation>Удалить данные подключаемого модуля браузера?</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Do you really want to delete the selected plugin data?
|
||||
This may cause the affected plugins to malfunction.</source>
|
||||
<translation>Действительно удалить выбранные данные подключаемых модулей?
|
||||
Это действие может привести к неработоспособности подключаемых модулей.</translation>
|
||||
<translation>Действительно удалить выбранные данные подключаемого модуля?
|
||||
Это действие может привести к неработоспособности подключаемых модулей в браузерах.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Key</source>
|
||||
@ -3064,15 +3064,15 @@ This may cause the affected plugins to malfunction.</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>Datetime created</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Дата и время создания</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Datetime modified</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Дата и время изменения</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Datetime accessed</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Дата и время последнего использования</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Unique ID</source>
|
||||
@ -3080,11 +3080,11 @@ This may cause the affected plugins to malfunction.</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>Plugin data</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Данные подключаемого модуля </translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Remove selected plugin data</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Удаление выбранных данных подключаемого модуля браузера</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@ -3133,7 +3133,7 @@ This may cause the affected plugins to malfunction.</source>
|
||||
</message>
|
||||
<message numerus="yes">
|
||||
<source>Are you sure you want to remove %n attachment(s)?</source>
|
||||
<translation><numerusform>Вы уверены, что вы хотите удалить %n вложение?</numerusform><numerusform>Вы уверены, что вы хотите удалить %n вложения?</numerusform><numerusform>Вы уверены, что вы хотите удалить %n вложений?</numerusform><numerusform>Вы действительно хотите удалить вложения (%n шт.)?</numerusform></translation>
|
||||
<translation><numerusform>Действительно удалить это вложение?</numerusform><numerusform>Действительно удалить %n вложения?</numerusform><numerusform>Действительно удалить %n вложений?</numerusform><numerusform>Действительно удалить %n вложения?</numerusform></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Save attachments</source>
|
||||
@ -3172,7 +3172,7 @@ This may cause the affected plugins to malfunction.</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>Confirm remove</source>
|
||||
<translation>Подтвердите удаление</translation>
|
||||
<translation>Подтверждение удаления</translation>
|
||||
</message>
|
||||
<message numerus="yes">
|
||||
<source>Unable to open file(s):
|
||||
@ -3189,19 +3189,19 @@ This may cause the affected plugins to malfunction.</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>Add new attachment</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Добавить вложение</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Remove selected attachment</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Удалить выбранное вложение</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Open selected attachment</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Открыть выбранное вложение</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Save selected attachment to disk</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Сохранить выбранное вложение на диск</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@ -3379,7 +3379,7 @@ This may cause the affected plugins to malfunction.</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>Share</source>
|
||||
<translation>Предоставить общий доступ</translation>
|
||||
<translation>Общий файл</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Display current TOTP value</source>
|
||||
@ -3444,7 +3444,7 @@ This may cause the affected plugins to malfunction.</source>
|
||||
<name>FdoSecretsPlugin</name>
|
||||
<message>
|
||||
<source>Fdo Secret Service: %1</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>FDO Secret Service: %1</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@ -3470,7 +3470,7 @@ This may cause the affected plugins to malfunction.</source>
|
||||
<name>IconDownloaderDialog</name>
|
||||
<message>
|
||||
<source>Download Favicons</source>
|
||||
<translation>Загрузить значки</translation>
|
||||
<translation>Получение значков сайтов</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Cancel</source>
|
||||
@ -3563,7 +3563,8 @@ You can enable the DuckDuckGo website icon service in the security section of th
|
||||
<message>
|
||||
<source>Invalid credentials were provided, please try again.
|
||||
If this reoccurs, then your database file may be corrupt.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Вероятно, для входа были указанные неверные данные.
|
||||
Попробуйте ввести данные ещё раз, если ошибка повториться, возможно, файл базы данных повреждён.</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@ -3698,11 +3699,12 @@ If this reoccurs, then your database file may be corrupt.</source>
|
||||
<message>
|
||||
<source>Invalid credentials were provided, please try again.
|
||||
If this reoccurs, then your database file may be corrupt.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Вероятно, для входа были указанные неверные данные.
|
||||
Попробуйте ввести данные ещё раз, если ошибка повториться, возможно, файл базы данных повреждён.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>(HMAC mismatch)</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>(несоответствие HMAC)</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@ -3931,7 +3933,7 @@ Line %2, column %3</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>Import KeePass1 Database</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Импортировать базу данных в формате KeePass1</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@ -4088,18 +4090,19 @@ Line %2, column %3</source>
|
||||
<message>
|
||||
<source>Invalid credentials were provided, please try again.
|
||||
If this reoccurs, then your database file may be corrupt.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Вероятно, для входа были указанные неверные данные.
|
||||
Попробуйте ввести данные ещё раз, если ошибка повториться, возможно, файл базы данных повреждён.</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>KeeShare</name>
|
||||
<message>
|
||||
<source>Invalid sharing reference</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Неверная ссылка общего доступа</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Inactive share %1</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Неактивная общая база %1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Imported from %1</source>
|
||||
@ -4107,35 +4110,35 @@ If this reoccurs, then your database file may be corrupt.</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>Exported to %1</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Выполнен экспорт в «%1»</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Synchronized with %1</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Синхронизировано с «%1»</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Import is disabled in settings</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Возможность импортировать отключена в параметрах программы</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Export is disabled in settings</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Возможность экспорировать отключена в параметрах программы</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Inactive share</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Неактивная общая база</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Imported from</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Импортировано из</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Exported to</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Экспортировано в</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Synchronized with</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Синхронизировано с</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@ -4240,7 +4243,7 @@ Message: %2</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>Browse for key file</source>
|
||||
<translation>Открытие диалога выбора ключевого файла </translation>
|
||||
<translation>Открыть диалога выбора файла-ключа</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Browse...</source>
|
||||
@ -4248,11 +4251,11 @@ Message: %2</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>Generate a new key file</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Создать новый файл ключа</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Note: Do not use a file that may change as that will prevent you from unlocking your database!</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Внимание: изменение файла приведёт к невозможности разблокировать базу данных!</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Invalid Key File</source>
|
||||
@ -4260,7 +4263,7 @@ Message: %2</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>You cannot use the current database as its own keyfile. Please choose a different file or generate a new key file.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Файл базы данных не может быть использован в качестве файла-ключа. Выберите другой файл или создайте файл-ключ.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Suspicious Key File</source>
|
||||
@ -4269,7 +4272,8 @@ Message: %2</source>
|
||||
<message>
|
||||
<source>The chosen key file looks like a password database file. A key file must be a static file that never changes or you will lose access to your database forever.
|
||||
Are you sure you want to continue with this file?</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Выбранный файл-ключ, вероятно, является файлом базы данных паролей. Файл-ключ должен являться неизменяемым файлом, в противном случае доступ к базе данных будет безвозвратно утерян.
|
||||
Продолжить использовать выбранный файл?</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@ -4564,7 +4568,7 @@ Expect some bugs and minor issues, this version is not meant for production use.
|
||||
</message>
|
||||
<message>
|
||||
<source>&Export</source>
|
||||
<translation> &Экспорт</translation>
|
||||
<translation>&Экспорт</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>&Check for Updates...</source>
|
||||
@ -4576,15 +4580,15 @@ Expect some bugs and minor issues, this version is not meant for production use.
|
||||
</message>
|
||||
<message>
|
||||
<source>Sort &A-Z</source>
|
||||
<translation>Сортировать &A-Z</translation>
|
||||
<translation>Сортировать &А-Я</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Sort &Z-A</source>
|
||||
<translation>Сортировать &Z-A</translation>
|
||||
<translation>Сортировать &Я-А</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>&Password Generator</source>
|
||||
<translation>&Генератор Паролей</translation>
|
||||
<translation>&Генератор паролей</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Download favicon</source>
|
||||
@ -4620,11 +4624,11 @@ Expect some bugs and minor issues, this version is not meant for production use.
|
||||
</message>
|
||||
<message>
|
||||
<source>&User Guide</source>
|
||||
<translation>&Руководство Пользователя</translation>
|
||||
<translation>&Руководство пользователя</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Open User Guide PDF</source>
|
||||
<translation>Открыть инструкцию пользователя в формате PDF</translation>
|
||||
<translation>Открыть руководство пользователя в формате PDF</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>&Keyboard Shortcuts</source>
|
||||
@ -4691,11 +4695,11 @@ Expect some bugs and minor issues, this version is not meant for production use.
|
||||
</message>
|
||||
<message>
|
||||
<source>Removed custom data %1 [%2]</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Пользовательские данные %1 [%2] удалены </translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Adding custom data %1 [%2]</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Добавление пользовательских данные %1 [%2]</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@ -4770,31 +4774,31 @@ Expect some bugs and minor issues, this version is not meant for production use.
|
||||
<name>OpData01</name>
|
||||
<message>
|
||||
<source>Invalid OpData01, does not contain header</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Данные OpData01 не содержат заголовка</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Unable to read all IV bytes, wanted 16 but got %1</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Невозможно считать все данные вектора инициализации: ожидалось 16 байт, но получено %1 байт</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Unable to init cipher for opdata01: %1</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Невозможно инициализировать шифрование для данных opdata01: %1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Unable to read all HMAC signature bytes</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Не удалось полностью считать данные подписи HMAC</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Malformed OpData01 due to a failed HMAC</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Повреждённые данные OpData01 по причине неверного HMAC</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Unable to process clearText in place</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Невозможно обработать простой текст</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Expected %1 bytes of clear-text, found %2</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Ожидалось %1 байт простого текста, найдено %2</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@ -4802,26 +4806,27 @@ Expect some bugs and minor issues, this version is not meant for production use.
|
||||
<message>
|
||||
<source>Read Database did not produce an instance
|
||||
%1</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Невозможно создать базу данных из прочитанного файла
|
||||
%1</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>OpVaultReader</name>
|
||||
<message>
|
||||
<source>Directory .opvault must exist</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Не найден каталог «.opvault»</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Directory .opvault must be readable</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Каталог «.opvault» не доступен для чтения</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Directory .opvault/default must exist</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Не найден каталог «.opvault/default»</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Directory .opvault/default must be readable</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Каталог «.opvault/default» не доступен для чтения</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Unable to decode masterKey: %1</source>
|
||||
@ -4829,7 +4834,7 @@ Expect some bugs and minor issues, this version is not meant for production use.
|
||||
</message>
|
||||
<message>
|
||||
<source>Unable to derive master key: %1</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Не удалось извлечь мастер-ключ: %1</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@ -4939,7 +4944,7 @@ Expect some bugs and minor issues, this version is not meant for production use.
|
||||
</message>
|
||||
<message>
|
||||
<source>Passwords match so far</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Количество совпавших паролей</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@ -5026,7 +5031,7 @@ Expect some bugs and minor issues, this version is not meant for production use.
|
||||
</message>
|
||||
<message>
|
||||
<source>Pick characters from every group</source>
|
||||
<translation>Подобрать символы из каждой группы</translation>
|
||||
<translation>Использовать символы из каждой группы</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>&Length:</source>
|
||||
@ -5363,7 +5368,7 @@ Expect some bugs and minor issues, this version is not meant for production use.
|
||||
</message>
|
||||
<message>
|
||||
<source>Key file of the database.</source>
|
||||
<translation>Ключевой файл базы данных.</translation>
|
||||
<translation>Файл-ключ базы данных.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>path</source>
|
||||
@ -5403,7 +5408,7 @@ Expect some bugs and minor issues, this version is not meant for production use.
|
||||
</message>
|
||||
<message>
|
||||
<source>Copy an entry's password to the clipboard.</source>
|
||||
<translation>Скопировать пароль в буфер обмена.</translation>
|
||||
<translation>Скопировать пароль записи в буфер обмена.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Path of the entry to clip.</source>
|
||||
@ -5971,7 +5976,7 @@ Available commands:
|
||||
</message>
|
||||
<message>
|
||||
<source>Parent window handle</source>
|
||||
<translation>Дескриптор родительского окна</translation>
|
||||
<translation>Дескриптор родительского окна.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Another instance of KeePassXC is already running.</source>
|
||||
@ -6099,27 +6104,27 @@ Kernel: %3 %4</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>Check if any passwords have been publicly leaked. FILENAME must be the path of a file listing SHA-1 hashes of leaked passwords in HIBP format, as available from https://haveibeenpwned.com/Passwords.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Проверка паролей на компрометацию. Параметр ИМЯ_ФАЙЛА должен быть путём к файлу данных списка SHA-1 хэшей паролей в формате HIBP, полученным, например, с сайта https://haveibeenpwned.com/Passwords.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>FILENAME</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>ИМЯ_ФАЙЛА</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Analyze passwords for weaknesses and problems.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Проверка надёжности и других характеристик паролей.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Failed to open HIBP file %1: %2</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Не удалось открыть файл в формате HIBP %1: %2</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Evaluating database entries against HIBP file, this will take a while...</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Производится проверка записей базы данных относительно файла в формате HIBP…</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Close the currently opened database.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Закрыть текущую базу данных.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Display this help.</source>
|
||||
@ -6127,7 +6132,7 @@ Kernel: %3 %4</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>Yubikey slot used to encrypt the database.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Слот устройства Yubikey, использованный для шифрования базы данных.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>slot</source>
|
||||
@ -6135,7 +6140,7 @@ Kernel: %3 %4</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>Invalid word count %1</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Количество неверных слов: %1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>The word list is too small (< 1000 items)</source>
|
||||
@ -6147,15 +6152,15 @@ Kernel: %3 %4</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>Format to use when exporting. Available choices are xml or csv. Defaults to xml.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Выбор формата файла для экспорта. Возможные варианты: XML (по умолчанию) или CSV.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Exports the content of a database to standard output in the specified format.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Экспорт базы данных в заданном формате на устройство стандартного вывода.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Unable to export database to XML: %1</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Ошибка экспортирования базы данных в формат XML: %1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Unsupported format %1</source>
|
||||
@ -6179,11 +6184,11 @@ Kernel: %3 %4</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>Import the contents of an XML database.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Импортировать базу данных в формате XML.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Path of the XML database export.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Путь для экспорта базы данных в формат XML</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Path of the new database.</source>
|
||||
@ -6191,7 +6196,7 @@ Kernel: %3 %4</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>Unable to import XML database export %1</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Ошибка импорта базы данных %1 из формата XML</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Successfully imported database.</source>
|
||||
@ -6207,7 +6212,7 @@ Kernel: %3 %4</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>Only print the changes detected by the merge operation.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Не выполнять объединение, а только сообщать о найденных изменениях</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Yubikey slot for the second database.</source>
|
||||
@ -6215,7 +6220,7 @@ Kernel: %3 %4</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>Successfully merged %1 into %2.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>%1 успешно объединён с %2.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Database was not modified by merge operation.</source>
|
||||
@ -6287,15 +6292,15 @@ Kernel: %3 %4</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>Please touch the button on your YubiKey to unlock %1</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Для разблокирования %1 нажмите кнопку на устройстве YubiKey</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Enter password to encrypt database (optional): </source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Введите пароль для шифрования базы данных (необязательно):</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>HIBP file, line %1: parse error</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Ошибка разбора строки %1 файла в формате HIBP</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Secret Service Integration</source>
|
||||
@ -6307,15 +6312,15 @@ Kernel: %3 %4</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>%1[%2] Challenge Response - Slot %3 - %4</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>%1 [%2] Вызов-ответ — слот %3 - %4</translation>
|
||||
</message>
|
||||
<message numerus="yes">
|
||||
<source>Password for '%1' has been leaked %2 time(s)!</source>
|
||||
<translation type="unfinished"><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform></translation>
|
||||
<translation><numerusform>Пароль к «%1» был замечен в утечках %2 раз.</numerusform><numerusform>Пароль к «%1» был замечен в утечках %2 раза.</numerusform><numerusform>Пароль к «%1» был замечен в утечках %2 раз.</numerusform><numerusform>Пароль к «%1» был замечен в утечках %2 раза.</numerusform></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Invalid password generator after applying all options</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Невозможно создать пароль с заданными параметрами</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Show the protected attributes in clear text.</source>
|
||||
@ -6482,7 +6487,7 @@ Kernel: %3 %4</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>Enable KeepassXC Freedesktop.org Secret Service integration</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Включить интеграцию KeepassXC со службой Freedesktop.org Secret Service</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>General</source>
|
||||
@ -6490,23 +6495,23 @@ Kernel: %3 %4</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>Show notification when credentials are requested</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Выводить уведомления при запросе записей</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><html><head/><body><p>If recycle bin is enabled for the database, entries will be moved to recycle bin directly. Otherwise, they will be deleted without confirmation.</p><p>You will still be prompted if any entries are referenced by others.</p></body></html></source>
|
||||
<translation type="unfinished"/>
|
||||
<translation><html><head/><body><p>Если в параметрах базы данных разрешено использование корзины, то при удалении записи будут перемещены в неё. В противном случае, будет выполнено необратимое удаление без подтверждения.</p><p>В случае, если на удаляемые записи имеются ссылки, потребуется подтверждение удаления.</p></body></html></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Don't confirm when entries are deleted by clients.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Не подтверждать удаление записей приложениями-клиентами</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Exposed database groups:</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Доступные группы из базы данных:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>File Name</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Имя файла</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Group</source>
|
||||
@ -6514,7 +6519,7 @@ Kernel: %3 %4</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>Manage</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Управление</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Authorization</source>
|
||||
@ -6522,15 +6527,15 @@ Kernel: %3 %4</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>These applications are currently connected:</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Подключены следующие приложения:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Application</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Приложение</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Disconnect</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Отключить</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Database settings</source>
|
||||
@ -6538,7 +6543,7 @@ Kernel: %3 %4</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>Edit database settings</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Редактирование параметров базы данных</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Unlock database</source>
|
||||
@ -6662,7 +6667,7 @@ Kernel: %3 %4</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>KeeShare key file</source>
|
||||
<translation>Ключевой файл KeeShare</translation>
|
||||
<translation>Файл ключа KeeShare</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>All files</source>
|
||||
@ -6962,7 +6967,7 @@ Kernel: %3 %4</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>Secret Key:</source>
|
||||
<translation>Секретный ключ</translation>
|
||||
<translation>Секретный ключ:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Secret key must be in Base32 format</source>
|
||||
|
@ -743,7 +743,7 @@ Prosím, vyberte správnu databázu na uloženie prihlasovacích údajov.</trans
|
||||
</message>
|
||||
<message>
|
||||
<source>&Brave</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>&Odvážny</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Returns expired credentials. String [expired] is added to the title.</source>
|
||||
@ -767,11 +767,11 @@ Prosím, vyberte správnu databázu na uloženie prihlasovacích údajov.</trans
|
||||
</message>
|
||||
<message>
|
||||
<source>Don't display the popup suggesting migration of legacy KeePassHTTP settings.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Nezobrazovať okno s návrhom na migráciu starých nastavení KeePassHTTP.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>&Do not prompt for KeePassHTTP settings migration.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>&Nepýtať sa na migráciu nastavení KeePassHTTP.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Custom proxy location field</source>
|
||||
@ -878,7 +878,11 @@ Chcete teraz migrovať svoje nastavenia?</translation>
|
||||
|
||||
Give the connection a unique name or ID, for example:
|
||||
chrome-laptop.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Obdržali ste požiadavku na priradenie nasledujúcej databázy.
|
||||
%1
|
||||
|
||||
Zadajte mu jedinečný názov alebo identifikátor, napríklad:
|
||||
chrome-laptop.</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@ -2286,7 +2290,7 @@ Vypnúť bezpečné ukladanie a skúsiť znova?</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><empty URL></source>
|
||||
<translation type="unfinished"/>
|
||||
<translation><prázdna URL></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Are you sure you want to remove this URL?</source>
|
||||
@ -2451,15 +2455,15 @@ Vypnúť bezpečné ukladanie a skúsiť znova?</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Skip Auto-Submit for this entry</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Zapnúť Automatické vypĺňanie pre túto položku</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Hide this entry from the browser extension</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Skryť túto položku v rozšírení prehliadača</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Additional URL's</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Ďalšie URL</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Add</source>
|
||||
@ -2494,19 +2498,19 @@ Vypnúť bezpečné ukladanie a skúsiť znova?</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Entry history selection</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Výber histórie položky</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Show entry at selected history state</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Zobraziť stav položky v okamžiku zvolenej histórie</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Restore entry to selected history state</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Obnoviť stav položky do okamžiku zvolenej histórie</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Delete selected history state</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Vymazať zvolený stav histórie</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Delete all history</source>
|
||||
@ -2557,7 +2561,7 @@ Vypnúť bezpečné ukladanie a skúsiť znova?</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Download favicon for URL</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Stiahnuť ikonu URL</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Repeat password field</source>
|
||||
@ -2597,11 +2601,11 @@ Vypnúť bezpečné ukladanie a skúsiť znova?</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Title field</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Pole nadpisu</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Username field</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Pole použ. mena</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Toggle expiration</source>
|
||||
@ -2685,19 +2689,19 @@ Vypnúť bezpečné ukladanie a skúsiť znova?</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Remove key from agent after specified seconds</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Odstrániť kľúč z agenta po zadanom počte sekúnd</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Browser for key file</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Vybrať súbor kľúča</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>External key file</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Súbor externého kľúča</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Select attachment file</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Zvoľte súbor prílohy</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@ -2799,20 +2803,21 @@ Vypnúť bezpečné ukladanie a skúsiť znova?</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Synchronize</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Synchronizovať</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Your KeePassXC version does not support sharing this container type.
|
||||
Supported extensions are: %1.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Táto verzia KeePassXC nepodporuje zdieľanie tohoto typu kontajnera.
|
||||
Podporované rozšírenia sú: %1.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>%1 is already being exported by this database.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>%1 už bolo exportované touto databázou.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>%1 is already being imported by this database.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>%1 už bolo importované touto databázou.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>%1 is being imported and exported by different groups in this database.</source>
|
||||
@ -2821,27 +2826,27 @@ Supported extensions are: %1.</source>
|
||||
<message>
|
||||
<source>KeeShare is currently disabled. You can enable import/export in the application settings.</source>
|
||||
<comment>KeeShare is a proper noun</comment>
|
||||
<translation type="unfinished"/>
|
||||
<translation>KeeShare je momentálne vypnuté. Import/export môžete zapnúť v nastaveniach aplikácie.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Database export is currently disabled by application settings.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Export databázy je momentálne vypnutý v nastaveniach aplikácie.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Database import is currently disabled by application settings.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Import databázy je momentálne vypnutý v nastaveniach aplikácie.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Sharing mode field</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Pole režimu zdieľania</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Path to share file field</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Pole cesty zdieľania súboru</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Browser for share file</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Vybrať zdieľaný súbor</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Password field</source>
|
||||
@ -2912,7 +2917,7 @@ Supported extensions are: %1.</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>Search toggle for this and sub groups</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Prepnúť hľadanie tejto a podriadených skupín</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Default auto-type sequence field</source>
|
||||
@ -2979,43 +2984,43 @@ Supported extensions are: %1.</source>
|
||||
</message>
|
||||
<message numerus="yes">
|
||||
<source>This icon is used by %n entry(s), and will be replaced by the default icon. Are you sure you want to delete it?</source>
|
||||
<translation type="unfinished"><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform></translation>
|
||||
<translation><numerusform>Táto ikona je použitá v %n položke a bude nahradená predvolenou ikonou. Naozaj ju chcete odstrániť?</numerusform><numerusform>Táto ikona je použitá v %n položkách a bude nahradená predvolenou ikonou. Naozaj ju chcete odstrániť?</numerusform><numerusform>Táto ikona je použitá v %n položkách a bude nahradená predvolenou ikonou. Naozaj ju chcete odstrániť?</numerusform><numerusform>Táto ikona je použitá v %n položkách a bude nahradená predvolenou ikonou. Naozaj ju chcete odstrániť?</numerusform></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>You can enable the DuckDuckGo website icon service under Tools -> Settings -> Security</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Môžete zapnúť webovú službu ikon DuckDuckGo v Nástroje -> Nastavenie -> Bezpečnosť</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Download favicon for URL</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Stiahnuť ikonu URL</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Apply selected icon to subgroups and entries</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Použiť zvolenú ikonu na podskupiny a položky</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Apply icon &to ...</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Použiť ikonu &na …</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Apply to this only</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Použiť len na túto</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Also apply to child groups</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Použiť aj na podriadené skupiny</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Also apply to child entries</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Použiť aj na podriadené položky</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Also apply to all children</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Použiť na všetkých potomkov</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Existing icon selected.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Zvolená existujúca ikona.</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@ -3178,7 +3183,11 @@ Môže to spôsobiť nefunkčnosť dotknutých zásuvných modulov.</translation
|
||||
<message numerus="yes">
|
||||
<source>Unable to open file(s):
|
||||
%1</source>
|
||||
<translation type="unfinished"><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform></translation>
|
||||
<translation><numerusform>Nemožno otvoriť súbor:
|
||||
%1</numerusform><numerusform>Nemožno otvoriť súbory:
|
||||
%1</numerusform><numerusform>Nemožno otvoriť súbory:
|
||||
%1</numerusform><numerusform>Nemožno otvoriť súbory:
|
||||
%1</numerusform></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Attachments</source>
|
||||
@ -3186,19 +3195,19 @@ Môže to spôsobiť nefunkčnosť dotknutých zásuvných modulov.</translation
|
||||
</message>
|
||||
<message>
|
||||
<source>Add new attachment</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Pridať novú prílohu</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Remove selected attachment</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Odstrániť zvolenú prílohu</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Open selected attachment</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Otvoriť zvolenú prílohu</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Save selected attachment to disk</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Uložiť zvolenú prílohu na disk</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@ -3380,7 +3389,7 @@ Môže to spôsobiť nefunkčnosť dotknutých zásuvných modulov.</translation
|
||||
</message>
|
||||
<message>
|
||||
<source>Display current TOTP value</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Zobraziť aktuálnu hodnotu TOTP</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Advanced</source>
|
||||
@ -3429,7 +3438,7 @@ Môže to spôsobiť nefunkčnosť dotknutých zásuvných modulov.</translation
|
||||
<name>FdoSecrets::Service</name>
|
||||
<message>
|
||||
<source>Failed to register DBus service at %1: another secret service is running.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Zlyhala registrácia služby DBus na %1: je spustená iná služba.</translation>
|
||||
</message>
|
||||
<message numerus="yes">
|
||||
<source>%n Entry(s) was used by %1</source>
|
||||
@ -3467,7 +3476,7 @@ Môže to spôsobiť nefunkčnosť dotknutých zásuvných modulov.</translation
|
||||
<name>IconDownloaderDialog</name>
|
||||
<message>
|
||||
<source>Download Favicons</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Stiahnuť ikony</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Cancel</source>
|
||||
@ -3476,7 +3485,8 @@ Môže to spôsobiť nefunkčnosť dotknutých zásuvných modulov.</translation
|
||||
<message>
|
||||
<source>Having trouble downloading icons?
|
||||
You can enable the DuckDuckGo website icon service in the security section of the application settings.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Máte problémy so sťahovaním ikon?
|
||||
V bezpečnostnej sekcii nastavení aplikácie môžete zapnúť webovú službu ikon.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Close</source>
|
||||
@ -3492,11 +3502,11 @@ You can enable the DuckDuckGo website icon service in the security section of th
|
||||
</message>
|
||||
<message>
|
||||
<source>Please wait, processing entry list...</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Prosím, počkajte, spracovanie zoznamu položiek…</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Downloading...</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Sťahovanie…</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Ok</source>
|
||||
@ -3512,7 +3522,7 @@ You can enable the DuckDuckGo website icon service in the security section of th
|
||||
</message>
|
||||
<message>
|
||||
<source>Downloading favicons (%1/%2)...</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Sťahovanie ikon (%1/%2)…</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@ -3559,7 +3569,8 @@ You can enable the DuckDuckGo website icon service in the security section of th
|
||||
<message>
|
||||
<source>Invalid credentials were provided, please try again.
|
||||
If this reoccurs, then your database file may be corrupt.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Boli zadané neplatné prihlasovacie údaje, prosím skúste znova.
|
||||
Ak sa to opakuje, potom môže byť súbor databázy poškodený.</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@ -3694,11 +3705,12 @@ If this reoccurs, then your database file may be corrupt.</source>
|
||||
<message>
|
||||
<source>Invalid credentials were provided, please try again.
|
||||
If this reoccurs, then your database file may be corrupt.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Boli zadané neplatné prihlasovacie údaje, prosím skúste znova.
|
||||
Ak sa to opakuje, potom môže byť súbor databázy poškodený.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>(HMAC mismatch)</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>(nezhoda HMAC)</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@ -4084,7 +4096,8 @@ Riadok %2, stĺpec %3</translation>
|
||||
<message>
|
||||
<source>Invalid credentials were provided, please try again.
|
||||
If this reoccurs, then your database file may be corrupt.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Boli zadané neplatné prihlasovacie údaje, prosím skúste znova.
|
||||
Ak sa to opakuje, potom môže byť súbor databázy poškodený.</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@ -4613,7 +4626,7 @@ Očakávajte chyby a menšie problémy, táto verzia nie je určená na produkč
|
||||
</message>
|
||||
<message>
|
||||
<source>Go to online documentation (opens browser)</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Prejsť na dokumentáciu on-line (otvorí prehliadač)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>&User Guide</source>
|
||||
@ -4771,7 +4784,7 @@ Očakávajte chyby a menšie problémy, táto verzia nie je určená na produkč
|
||||
</message>
|
||||
<message>
|
||||
<source>Unable to read all IV bytes, wanted 16 but got %1</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Nemožno čítať všetky bajty IV, potrebných 16, ale získaných %1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Unable to init cipher for opdata01: %1</source>
|
||||
@ -4779,7 +4792,7 @@ Očakávajte chyby a menšie problémy, táto verzia nie je určená na produkč
|
||||
</message>
|
||||
<message>
|
||||
<source>Unable to read all HMAC signature bytes</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Nemožno čítať všetky bajty podpisu HMAC</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Malformed OpData01 due to a failed HMAC</source>
|
||||
@ -4806,27 +4819,27 @@ Očakávajte chyby a menšie problémy, táto verzia nie je určená na produkč
|
||||
<name>OpVaultReader</name>
|
||||
<message>
|
||||
<source>Directory .opvault must exist</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Adresár .opvault musí existovať</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Directory .opvault must be readable</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Adresár .opvault musí byť čitateľný</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Directory .opvault/default must exist</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Adresár .opvault/default musí existovať</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Directory .opvault/default must be readable</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Adresár .opvault/default musí byť čitateľný</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Unable to decode masterKey: %1</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Nemožno dekódovať hlavný kľúč: %1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Unable to derive master key: %1</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Nemožno odvodiť hlavný kľúč: %1</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@ -4932,11 +4945,11 @@ Očakávajte chyby a menšie problémy, táto verzia nie je určená na produkč
|
||||
<name>PasswordEdit</name>
|
||||
<message>
|
||||
<source>Passwords do not match</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Heslá sa nezhodujú.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Passwords match so far</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Heslá sa zhodujú až potiaľ</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@ -6072,7 +6085,7 @@ Jadro: %3 %4</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Cannot generate a password and prompt at the same time!</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Nemožno naraz generovať aj zadať heslo!</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Adds a new group to a database.</source>
|
||||
@ -6096,11 +6109,11 @@ Jadro: %3 %4</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Check if any passwords have been publicly leaked. FILENAME must be the path of a file listing SHA-1 hashes of leaked passwords in HIBP format, as available from https://haveibeenpwned.com/Passwords.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Skontrolovať, či nejaké heslo neuniklo na verejnosť. MENOSÚBORU musí byť cesta k súboru so zoznamom odtlačkov SHA-1 uniknutých hesiel vo formáte HIBP, ako je dostupný z https://haveibeenpwned.com/Passwords.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>FILENAME</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>MENOSÚBORU</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Analyze passwords for weaknesses and problems.</source>
|
||||
@ -6108,7 +6121,7 @@ Jadro: %3 %4</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Failed to open HIBP file %1: %2</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Zlyhalo otvorenie súboru HIBP %1: %2</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Evaluating database entries against HIBP file, this will take a while...</source>
|
||||
@ -6120,7 +6133,7 @@ Jadro: %3 %4</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Display this help.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Zobrazí tohoto pomocníka.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Yubikey slot used to encrypt the database.</source>
|
||||
@ -6128,11 +6141,11 @@ Jadro: %3 %4</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>slot</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>slot</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Invalid word count %1</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Neplatný počet slov %1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>The word list is too small (< 1000 items)</source>
|
||||
@ -6140,7 +6153,7 @@ Jadro: %3 %4</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Exit interactive mode.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Ukončiť interaktívny režim.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Format to use when exporting. Available choices are xml or csv. Defaults to xml.</source>
|
||||
@ -6152,27 +6165,27 @@ Jadro: %3 %4</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Unable to export database to XML: %1</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Nemožno exportovať databázu do XML: %1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Unsupported format %1</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Nepodporovaný formát: %1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Use numbers</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Použiť čísla</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Invalid password length %1</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Neplatná dĺžka hesla %1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Display command help.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Zobrazí pomocníka príkazu.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Available commands:</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Dostupné príkazy:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Import the contents of an XML database.</source>
|
||||
@ -6188,15 +6201,15 @@ Jadro: %3 %4</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Unable to import XML database export %1</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Nemožno importovať databázu z exportu XML %1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Successfully imported database.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Úspešne importovaná databáza.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Unknown command %1</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Neznáma príkaz %1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Flattens the output to single lines.</source>
|
||||
@ -6212,7 +6225,7 @@ Jadro: %3 %4</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Successfully merged %1 into %2.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Úspešne zlúčené %1 do %2.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Database was not modified by merge operation.</source>
|
||||
@ -6220,7 +6233,7 @@ Jadro: %3 %4</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Moves an entry to a new group.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Presunie položku do novej skupiny.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Path of the entry to move.</source>
|
||||
@ -6228,7 +6241,7 @@ Jadro: %3 %4</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Path of the destination group.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Cesta cieľovej skupiny.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Could not find group with path %1.</source>
|
||||
@ -6527,7 +6540,7 @@ Jadro: %3 %4</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Disconnect</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Odpojiť</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Database settings</source>
|
||||
@ -6535,7 +6548,7 @@ Jadro: %3 %4</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Edit database settings</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Upraviť nastavenia databázy</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Unlock database</source>
|
||||
@ -6543,7 +6556,7 @@ Jadro: %3 %4</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Unlock database to show more information</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Odomknúť databázu na zobrazenie ďalších informácií</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Lock database</source>
|
||||
@ -6551,7 +6564,7 @@ Jadro: %3 %4</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Unlock to show</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Odomknúť na zobrazenie</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>None</source>
|
||||
@ -6683,15 +6696,15 @@ Jadro: %3 %4</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Allow KeeShare imports</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Povoliť importy KeeShare</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Allow KeeShare exports</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Povoliť exporty KeeShare</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Only show warnings and errors</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Zobraziť len upozornenia a chyby</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Key</source>
|
||||
@ -6703,35 +6716,35 @@ Jadro: %3 %4</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Generate new certificate</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Generovať nový certifikát</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Import existing certificate</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Importovať existujúci certifikát</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Export own certificate</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Exportuje vlastný certifikát</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Known shares</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Známe zdieľania</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Trust selected certificate</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Dôverovať zvolenému certifikátu</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Ask whether to trust the selected certificate every time</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Vždy sa spýtať, či dôverovať zvolenému certifikátu</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Untrust selected certificate</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Zrušiť dôveru zvoleného certifikátu</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Remove selected certificate</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Odstrániť zvolený certifikát</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@ -6900,7 +6913,7 @@ Prepis podpísaných zdieľaných kontajnerov nie je podporovaný - export sa ne
|
||||
</message>
|
||||
<message numerus="yes">
|
||||
<source>Expires in <b>%n</b> second(s)</source>
|
||||
<translation type="unfinished"><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform></translation>
|
||||
<translation><numerusform>Platnosť vyprší za % n sekundu</numerusform><numerusform>Platnosť vyprší za % n sekundy</numerusform><numerusform>Platnosť vyprší za % n sekúnd</numerusform><numerusform>Platnosť vyprší za <b>% n</b> sekúnd</numerusform></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@ -6960,19 +6973,19 @@ Prepis podpísaných zdieľaných kontajnerov nie je podporovaný - export sa ne
|
||||
</message>
|
||||
<message>
|
||||
<source>Secret Key:</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Tajný kľúč:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Secret key must be in Base32 format</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Tajný kľúč musí byť vo formáte Base32</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Secret key field</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Pole tajného kľúča</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Algorithm:</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Algoritmus:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Time step field</source>
|
||||
@ -6980,24 +6993,25 @@ Prepis podpísaných zdieľaných kontajnerov nie je podporovaný - export sa ne
|
||||
</message>
|
||||
<message>
|
||||
<source> digits</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>číslice</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Invalid TOTP Secret</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Neplatné tajomstvo TOTP</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>You have entered an invalid secret key. The key must be in Base32 format.
|
||||
Example: JBSWY3DPEHPK3PXP</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Zadali ste neplatný tajný kľúč. Kľúč musí byť vo formáte Base32.
|
||||
Napríklad: JBSWY3DPEHPK3PXP</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Confirm Remove TOTP Settings</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Potvrďte odstránenie nastavení TOTP</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Are you sure you want to delete TOTP settings for this entry?</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Naozaj chcete odstrániť nastavenia TOTP tejto položky?</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@ -7087,7 +7101,7 @@ Example: JBSWY3DPEHPK3PXP</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>Open a recent database</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Otvoriť nedávnu databázu</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,5 +1,5 @@
|
||||
name: keepassxc
|
||||
version: 2.5.1
|
||||
version: 2.5.2
|
||||
grade: stable
|
||||
summary: Community-driven port of the Windows application “KeePass Password Safe”
|
||||
description: |
|
||||
|
@ -121,6 +121,7 @@ set(keepassx_SOURCES
|
||||
gui/TotpDialog.cpp
|
||||
gui/TotpExportSettingsDialog.cpp
|
||||
gui/DatabaseOpenDialog.cpp
|
||||
gui/URLEdit.cpp
|
||||
gui/WelcomeWidget.cpp
|
||||
gui/csvImport/CsvImportWidget.cpp
|
||||
gui/csvImport/CsvImportWizard.cpp
|
||||
|
@ -42,7 +42,7 @@ QJsonObject BrowserAction::readResponse(const QJsonObject& json)
|
||||
|
||||
bool triggerUnlock = false;
|
||||
const QString trigger = json.value("triggerUnlock").toString();
|
||||
if (!trigger.isEmpty() && trigger.compare("true", Qt::CaseSensitive) == 0) {
|
||||
if (!trigger.isEmpty() && trigger.compare(TRUE_STR, Qt::CaseSensitive) == 0) {
|
||||
triggerUnlock = true;
|
||||
}
|
||||
|
||||
@ -268,7 +268,7 @@ QJsonObject BrowserAction::handleGetLogins(const QJsonObject& json, const QStrin
|
||||
const QString id = decrypted.value("id").toString();
|
||||
const QString submit = decrypted.value("submitUrl").toString();
|
||||
const QString auth = decrypted.value("httpAuth").toString();
|
||||
const bool httpAuth = auth.compare("true", Qt::CaseSensitive) == 0 ? true : false;
|
||||
const bool httpAuth = auth.compare(TRUE_STR, Qt::CaseSensitive) == 0 ? true : false;
|
||||
const QJsonArray users = m_browserService.findMatchingEntries(id, url, submit, "", keyList, httpAuth);
|
||||
|
||||
if (users.isEmpty()) {
|
||||
@ -469,7 +469,7 @@ QJsonObject BrowserAction::buildMessage(const QString& nonce) const
|
||||
{
|
||||
QJsonObject message;
|
||||
message["version"] = KEEPASSXC_VERSION;
|
||||
message["success"] = "true";
|
||||
message["success"] = TRUE_STR;
|
||||
message["nonce"] = nonce;
|
||||
return message;
|
||||
}
|
||||
|
@ -68,6 +68,10 @@ BrowserOptionDialog::BrowserOptionDialog(QWidget* parent)
|
||||
connect(m_ui->useCustomProxy, SIGNAL(toggled(bool)), m_ui->customProxyLocationBrowseButton, SLOT(setEnabled(bool)));
|
||||
connect(m_ui->customProxyLocationBrowseButton, SIGNAL(clicked()), this, SLOT(showProxyLocationFileDialog()));
|
||||
|
||||
#ifndef Q_OS_LINUX
|
||||
m_ui->snapWarningLabel->setVisible(false);
|
||||
#endif
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
// Brave uses Chrome's registry settings
|
||||
m_ui->braveSupport->setHidden(true);
|
||||
@ -124,6 +128,9 @@ void BrowserOptionDialog::loadSettings()
|
||||
m_ui->vivaldiSupport->setChecked(settings->vivaldiSupport());
|
||||
m_ui->torBrowserSupport->setChecked(settings->torBrowserSupport());
|
||||
#endif
|
||||
#ifndef Q_OS_LINUX
|
||||
m_ui->snapWarningLabel->setVisible(false);
|
||||
#endif
|
||||
|
||||
#if defined(KEEPASSXC_DIST_APPIMAGE)
|
||||
m_ui->supportBrowserProxy->setChecked(true);
|
||||
|
@ -60,7 +60,7 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<widget class="QLabel" name="snapWarningLabel">
|
||||
<property name="text">
|
||||
<string>Browsers installed as snaps are currently not supported.</string>
|
||||
</property>
|
||||
|
@ -54,6 +54,7 @@ static const QString KEEPASSHTTP_GROUP_NAME = QStringLiteral("KeePassHttp Passwo
|
||||
// Extra entry related options saved in custom data
|
||||
const QString BrowserService::OPTION_SKIP_AUTO_SUBMIT = QStringLiteral("BrowserSkipAutoSubmit");
|
||||
const QString BrowserService::OPTION_HIDE_ENTRY = QStringLiteral("BrowserHideEntry");
|
||||
const QString BrowserService::OPTION_ONLY_HTTP_AUTH = QStringLiteral("BrowserOnlyHttpAuth");
|
||||
// Multiple URL's
|
||||
const QString BrowserService::ADDITIONAL_URL = QStringLiteral("KP2A_URL");
|
||||
|
||||
@ -380,9 +381,14 @@ QJsonArray BrowserService::findMatchingEntries(const QString& id,
|
||||
// Check entries for authorization
|
||||
QList<Entry*> pwEntriesToConfirm;
|
||||
QList<Entry*> pwEntries;
|
||||
for (auto* entry : searchEntries(url, keyList)) {
|
||||
for (auto* entry : searchEntries(url, submitUrl, keyList)) {
|
||||
if (entry->customData()->contains(BrowserService::OPTION_HIDE_ENTRY)
|
||||
&& entry->customData()->value(BrowserService::OPTION_HIDE_ENTRY) == "true") {
|
||||
&& entry->customData()->value(BrowserService::OPTION_HIDE_ENTRY) == TRUE_STR) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!httpAuth && entry->customData()->contains(BrowserService::OPTION_ONLY_HTTP_AUTH)
|
||||
&& entry->customData()->value(BrowserService::OPTION_ONLY_HTTP_AUTH) == TRUE_STR) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -583,7 +589,7 @@ BrowserService::ReturnValue BrowserService::updateEntry(const QString& id,
|
||||
}
|
||||
|
||||
QList<Entry*>
|
||||
BrowserService::searchEntries(const QSharedPointer<Database>& db, const QString& hostname, const QString& url)
|
||||
BrowserService::searchEntries(const QSharedPointer<Database>& db, const QString& url, const QString& submitUrl)
|
||||
{
|
||||
QList<Entry*> entries;
|
||||
auto* rootGroup = db->rootGroup();
|
||||
@ -601,20 +607,15 @@ BrowserService::searchEntries(const QSharedPointer<Database>& db, const QString&
|
||||
continue;
|
||||
}
|
||||
|
||||
auto domain = baseDomain(hostname);
|
||||
|
||||
// Search for additional URL's starting with KP2A_URL
|
||||
if (entry->attributes()->keys().contains(ADDITIONAL_URL)) {
|
||||
for (const auto& key : entry->attributes()->keys()) {
|
||||
if (key.startsWith(ADDITIONAL_URL)
|
||||
&& handleURL(entry->attributes()->value(key), domain, url)) {
|
||||
entries.append(entry);
|
||||
continue;
|
||||
}
|
||||
for (const auto& key : entry->attributes()->keys()) {
|
||||
if (key.startsWith(ADDITIONAL_URL) && handleURL(entry->attributes()->value(key), url, submitUrl)) {
|
||||
entries.append(entry);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (!handleURL(entry->url(), domain, url)) {
|
||||
if (!handleURL(entry->url(), url, submitUrl)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -625,7 +626,7 @@ BrowserService::searchEntries(const QSharedPointer<Database>& db, const QString&
|
||||
return entries;
|
||||
}
|
||||
|
||||
QList<Entry*> BrowserService::searchEntries(const QString& url, const StringPairList& keyList)
|
||||
QList<Entry*> BrowserService::searchEntries(const QString& url, const QString& submitUrl, const StringPairList& keyList)
|
||||
{
|
||||
// Check if database is connected with KeePassXC-Browser
|
||||
auto databaseConnected = [&](const QSharedPointer<Database>& db) {
|
||||
@ -662,7 +663,7 @@ QList<Entry*> BrowserService::searchEntries(const QString& url, const StringPair
|
||||
QList<Entry*> entries;
|
||||
do {
|
||||
for (const auto& db : databases) {
|
||||
entries << searchEntries(db, hostname, url);
|
||||
entries << searchEntries(db, url, submitUrl);
|
||||
}
|
||||
} while (entries.isEmpty() && removeFirstDomain(hostname));
|
||||
|
||||
@ -855,7 +856,7 @@ QJsonObject BrowserService::prepareEntry(const Entry* entry)
|
||||
}
|
||||
|
||||
if (entry->isExpired()) {
|
||||
res["expired"] = "true";
|
||||
res["expired"] = TRUE_STR;
|
||||
}
|
||||
|
||||
if (entry->customData()->contains(BrowserService::OPTION_SKIP_AUTO_SUBMIT)) {
|
||||
@ -1000,7 +1001,7 @@ bool BrowserService::removeFirstDomain(QString& hostname)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool BrowserService::handleURL(const QString& entryUrl, const QString& hostname, const QString& url)
|
||||
bool BrowserService::handleURL(const QString& entryUrl, const QString& url, const QString& submitUrl)
|
||||
{
|
||||
if (entryUrl.isEmpty()) {
|
||||
return false;
|
||||
@ -1017,31 +1018,36 @@ bool BrowserService::handleURL(const QString& entryUrl, const QString& hostname,
|
||||
}
|
||||
}
|
||||
|
||||
// Make a direct compare if a local file is used
|
||||
if (url.contains("file://")) {
|
||||
return entryUrl == submitUrl;
|
||||
}
|
||||
|
||||
// URL host validation fails
|
||||
if (browserSettings()->matchUrlScheme() && entryQUrl.host().isEmpty()) {
|
||||
if (entryQUrl.host().isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Match port, if used
|
||||
QUrl qUrl(url);
|
||||
if (entryQUrl.port() > 0 && entryQUrl.port() != qUrl.port()) {
|
||||
QUrl siteQUrl(url);
|
||||
if (entryQUrl.port() > 0 && entryQUrl.port() != siteQUrl.port()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Match scheme
|
||||
if (browserSettings()->matchUrlScheme() && !entryQUrl.scheme().isEmpty() && entryQUrl.scheme().compare(qUrl.scheme()) != 0) {
|
||||
if (browserSettings()->matchUrlScheme() && !entryQUrl.scheme().isEmpty()
|
||||
&& entryQUrl.scheme().compare(siteQUrl.scheme()) != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check for illegal characters
|
||||
QRegularExpression re("[<>\\^`{|}]");
|
||||
auto match = re.match(entryUrl);
|
||||
if (match.hasMatch()) {
|
||||
if (re.match(entryUrl).hasMatch()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Filter to match hostname in URL field
|
||||
if (entryQUrl.host().endsWith(hostname)) {
|
||||
if (siteQUrl.host().endsWith(entryQUrl.host())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -63,8 +63,8 @@ public:
|
||||
const QString& group,
|
||||
const QString& groupUuid,
|
||||
const QSharedPointer<Database>& selectedDb = {});
|
||||
QList<Entry*> searchEntries(const QSharedPointer<Database>& db, const QString& hostname, const QString& url);
|
||||
QList<Entry*> searchEntries(const QString& url, const StringPairList& keyList);
|
||||
QList<Entry*> searchEntries(const QSharedPointer<Database>& db, const QString& url, const QString& submitUrl);
|
||||
QList<Entry*> searchEntries(const QString& url, const QString& submitUrl, const StringPairList& keyList);
|
||||
void convertAttributesToCustomData(const QSharedPointer<Database>& currentDb = {});
|
||||
|
||||
public:
|
||||
@ -74,6 +74,7 @@ public:
|
||||
static const QString LEGACY_ASSOCIATE_KEY_PREFIX;
|
||||
static const QString OPTION_SKIP_AUTO_SUBMIT;
|
||||
static const QString OPTION_HIDE_ENTRY;
|
||||
static const QString OPTION_ONLY_HTTP_AUTH;
|
||||
static const QString ADDITIONAL_URL;
|
||||
|
||||
public slots:
|
||||
@ -130,7 +131,7 @@ private:
|
||||
sortPriority(const Entry* entry, const QString& host, const QString& submitUrl, const QString& baseSubmitUrl) const;
|
||||
bool schemeFound(const QString& url);
|
||||
bool removeFirstDomain(QString& hostname);
|
||||
bool handleURL(const QString& entryUrl, const QString& hostname, const QString& url);
|
||||
bool handleURL(const QString& entryUrl, const QString& url, const QString& submitUrl);
|
||||
QString baseDomain(const QString& hostname) const;
|
||||
QSharedPointer<Database> getDatabase();
|
||||
QSharedPointer<Database> selectedDatabase();
|
||||
|
@ -55,7 +55,7 @@ int Analyze::executeWithDatabase(QSharedPointer<Database> database, QSharedPoint
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
outputTextStream << QObject::tr("Evaluating database entries against HIBP file, this will take a while...");
|
||||
outputTextStream << QObject::tr("Evaluating database entries against HIBP file, this will take a while...") << endl;
|
||||
|
||||
QList<QPair<const Entry*, int>> findings;
|
||||
QString error;
|
||||
|
@ -30,8 +30,7 @@ const QCommandLineOption Merge::SameCredentialsOption =
|
||||
QObject::tr("Use the same credentials for both database files."));
|
||||
|
||||
const QCommandLineOption Merge::KeyFileFromOption =
|
||||
QCommandLineOption(QStringList() << "k"
|
||||
<< "key-file-from",
|
||||
QCommandLineOption(QStringList() << "key-file-from",
|
||||
QObject::tr("Key file of the database to merge from."),
|
||||
QObject::tr("path"));
|
||||
|
||||
|
@ -149,7 +149,7 @@ void enterInteractiveMode(const QStringList& arguments)
|
||||
prompt += "> ";
|
||||
command = reader->readLine(prompt);
|
||||
if (reader->isFinished()) {
|
||||
return;
|
||||
break;
|
||||
}
|
||||
|
||||
QStringList args = Utils::splitCommandString(command);
|
||||
@ -162,13 +162,17 @@ void enterInteractiveMode(const QStringList& arguments)
|
||||
errorTextStream << QObject::tr("Unknown command %1").arg(args[0]) << "\n";
|
||||
continue;
|
||||
} else if (cmd->name == "quit" || cmd->name == "exit") {
|
||||
return;
|
||||
break;
|
||||
}
|
||||
|
||||
cmd->currentDatabase = currentDatabase;
|
||||
cmd->execute(args);
|
||||
currentDatabase = cmd->currentDatabase;
|
||||
}
|
||||
|
||||
if (currentDatabase) {
|
||||
currentDatabase->releaseData();
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
|
@ -106,7 +106,11 @@ namespace Bootstrap
|
||||
{
|
||||
// start minimized if configured
|
||||
if (config()->get("GUI/MinimizeOnStartup").toBool()) {
|
||||
#ifdef Q_OS_WIN
|
||||
mainWindow.showMinimized();
|
||||
#else
|
||||
mainWindow.hideWindow();
|
||||
#endif
|
||||
} else {
|
||||
mainWindow.bringToFront();
|
||||
}
|
||||
|
@ -42,7 +42,6 @@ Database::Database()
|
||||
: m_metadata(new Metadata(this))
|
||||
, m_data()
|
||||
, m_rootGroup(nullptr)
|
||||
, m_timer(new QTimer(this))
|
||||
, m_fileWatcher(new FileWatcher(this))
|
||||
, m_emitModified(false)
|
||||
, m_uuid(QUuid::createUuid())
|
||||
@ -50,12 +49,12 @@ Database::Database()
|
||||
setRootGroup(new Group());
|
||||
rootGroup()->setUuid(QUuid::createUuid());
|
||||
rootGroup()->setName(tr("Root", "Root group name"));
|
||||
m_timer->setSingleShot(true);
|
||||
m_modifiedTimer.setSingleShot(true);
|
||||
|
||||
s_uuidMap.insert(m_uuid, this);
|
||||
|
||||
connect(m_metadata, SIGNAL(metadataModified()), SLOT(markAsModified()));
|
||||
connect(m_timer, SIGNAL(timeout()), SIGNAL(databaseModified()));
|
||||
connect(&m_modifiedTimer, SIGNAL(timeout()), SIGNAL(databaseModified()));
|
||||
connect(this, SIGNAL(databaseOpened()), SLOT(updateCommonUsernames()));
|
||||
connect(this, SIGNAL(databaseSaved()), SLOT(updateCommonUsernames()));
|
||||
connect(m_fileWatcher, SIGNAL(fileChanged()), SIGNAL(databaseFileChanged()));
|
||||
@ -229,6 +228,7 @@ bool Database::saveAs(const QString& filePath, QString* error, bool atomic, bool
|
||||
auto& canonicalFilePath = QFileInfo::exists(filePath) ? QFileInfo(filePath).canonicalFilePath() : filePath;
|
||||
bool ok = performSave(canonicalFilePath, error, atomic, backup);
|
||||
if (ok) {
|
||||
markAsClean();
|
||||
setFilePath(filePath);
|
||||
m_fileWatcher->start(canonicalFilePath, 30, 1);
|
||||
} else {
|
||||
@ -343,7 +343,6 @@ bool Database::writeDatabase(QIODevice* device, QString* error)
|
||||
return false;
|
||||
}
|
||||
|
||||
markAsClean();
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -832,7 +831,7 @@ void Database::emptyRecycleBin()
|
||||
void Database::setEmitModified(bool value)
|
||||
{
|
||||
if (m_emitModified && !value) {
|
||||
m_timer->stop();
|
||||
m_modifiedTimer.stop();
|
||||
}
|
||||
|
||||
m_emitModified = value;
|
||||
@ -846,8 +845,9 @@ bool Database::isModified() const
|
||||
void Database::markAsModified()
|
||||
{
|
||||
m_modified = true;
|
||||
if (m_emitModified) {
|
||||
startModifiedTimer();
|
||||
if (m_emitModified && !m_modifiedTimer.isActive()) {
|
||||
// Small time delay prevents numerous consecutive saves due to repeated signals
|
||||
m_modifiedTimer.start(150);
|
||||
}
|
||||
}
|
||||
|
||||
@ -855,6 +855,7 @@ void Database::markAsClean()
|
||||
{
|
||||
bool emitSignal = m_modified;
|
||||
m_modified = false;
|
||||
m_modifiedTimer.stop();
|
||||
if (emitSignal) {
|
||||
emit databaseSaved();
|
||||
}
|
||||
@ -869,18 +870,6 @@ Database* Database::databaseByUuid(const QUuid& uuid)
|
||||
return s_uuidMap.value(uuid, nullptr);
|
||||
}
|
||||
|
||||
void Database::startModifiedTimer()
|
||||
{
|
||||
if (!m_emitModified) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_timer->isActive()) {
|
||||
m_timer->stop();
|
||||
}
|
||||
m_timer->start(150);
|
||||
}
|
||||
|
||||
QSharedPointer<const CompositeKey> Database::key() const
|
||||
{
|
||||
return m_data.key;
|
||||
|
@ -21,23 +21,22 @@
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QHash>
|
||||
#include <QObject>
|
||||
#include <QPointer>
|
||||
#include <QScopedPointer>
|
||||
#include <QTimer>
|
||||
|
||||
#include "config-keepassx.h"
|
||||
#include "crypto/kdf/AesKdf.h"
|
||||
#include "crypto/kdf/Kdf.h"
|
||||
#include "format/KeePass2.h"
|
||||
#include "keys/PasswordKey.h"
|
||||
#include "keys/CompositeKey.h"
|
||||
#include "keys/PasswordKey.h"
|
||||
|
||||
class Entry;
|
||||
enum class EntryReferenceType;
|
||||
class FileWatcher;
|
||||
class Group;
|
||||
class Metadata;
|
||||
class QTimer;
|
||||
class QIODevice;
|
||||
|
||||
struct DeletedObject
|
||||
@ -155,9 +154,6 @@ signals:
|
||||
void databaseDiscarded();
|
||||
void databaseFileChanged();
|
||||
|
||||
private slots:
|
||||
void startModifiedTimer();
|
||||
|
||||
private:
|
||||
struct DatabaseData
|
||||
{
|
||||
@ -211,7 +207,7 @@ private:
|
||||
DatabaseData m_data;
|
||||
QPointer<Group> m_rootGroup;
|
||||
QList<DeletedObject> m_deletedObjects;
|
||||
QPointer<QTimer> m_timer;
|
||||
QTimer m_modifiedTimer;
|
||||
QPointer<FileWatcher> m_fileWatcher;
|
||||
bool m_initialized = false;
|
||||
bool m_modified = false;
|
||||
|
@ -35,11 +35,10 @@ namespace
|
||||
|
||||
FileWatcher::FileWatcher(QObject* parent)
|
||||
: QObject(parent)
|
||||
, m_ignoreFileChange(false)
|
||||
{
|
||||
connect(&m_fileWatcher, SIGNAL(fileChanged(QString)), SLOT(onWatchedFileChanged()));
|
||||
connect(&m_fileWatcher, SIGNAL(fileChanged(QString)), SLOT(checkFileChanged()));
|
||||
connect(&m_fileChecksumTimer, SIGNAL(timeout()), SLOT(checkFileChanged()));
|
||||
connect(&m_fileChangeDelayTimer, SIGNAL(timeout()), SIGNAL(fileChanged()));
|
||||
connect(&m_fileChecksumTimer, SIGNAL(timeout()), SLOT(checkFileChecksum()));
|
||||
m_fileChangeDelayTimer.setSingleShot(true);
|
||||
m_fileIgnoreDelayTimer.setSingleShot(true);
|
||||
}
|
||||
@ -101,17 +100,6 @@ void FileWatcher::resume()
|
||||
}
|
||||
}
|
||||
|
||||
void FileWatcher::onWatchedFileChanged()
|
||||
{
|
||||
// Don't notify if we are ignoring events or already started a notification chain
|
||||
if (shouldIgnoreChanges()) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_fileChecksum = calculateChecksum();
|
||||
m_fileChangeDelayTimer.start(0);
|
||||
}
|
||||
|
||||
bool FileWatcher::shouldIgnoreChanges()
|
||||
{
|
||||
return m_filePath.isEmpty() || m_ignoreFileChange || m_fileIgnoreDelayTimer.isActive()
|
||||
@ -123,15 +111,23 @@ bool FileWatcher::hasSameFileChecksum()
|
||||
return calculateChecksum() == m_fileChecksum;
|
||||
}
|
||||
|
||||
void FileWatcher::checkFileChecksum()
|
||||
void FileWatcher::checkFileChanged()
|
||||
{
|
||||
if (shouldIgnoreChanges()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!hasSameFileChecksum()) {
|
||||
onWatchedFileChanged();
|
||||
// Prevent reentrance
|
||||
m_ignoreFileChange = true;
|
||||
|
||||
// Only trigger the change notice if there is a checksum mismatch
|
||||
auto checksum = calculateChecksum();
|
||||
if (checksum != m_fileChecksum) {
|
||||
m_fileChecksum = checksum;
|
||||
m_fileChangeDelayTimer.start(0);
|
||||
}
|
||||
|
||||
m_ignoreFileChange = false;
|
||||
}
|
||||
|
||||
QByteArray FileWatcher::calculateChecksum()
|
||||
|
@ -43,8 +43,7 @@ public slots:
|
||||
void resume();
|
||||
|
||||
private slots:
|
||||
void onWatchedFileChanged();
|
||||
void checkFileChecksum();
|
||||
void checkFileChanged();
|
||||
|
||||
private:
|
||||
QByteArray calculateChecksum();
|
||||
@ -56,8 +55,8 @@ private:
|
||||
QTimer m_fileChangeDelayTimer;
|
||||
QTimer m_fileIgnoreDelayTimer;
|
||||
QTimer m_fileChecksumTimer;
|
||||
int m_fileChecksumSizeBytes;
|
||||
bool m_ignoreFileChange;
|
||||
int m_fileChecksumSizeBytes = -1;
|
||||
bool m_ignoreFileChange = false;
|
||||
};
|
||||
|
||||
class BulkFileWatcher : public QObject
|
||||
|
@ -20,6 +20,7 @@
|
||||
#ifndef KEEPASSX_GLOBAL_H
|
||||
#define KEEPASSX_GLOBAL_H
|
||||
|
||||
#include <QString>
|
||||
#include <QtGlobal>
|
||||
|
||||
#if defined(Q_OS_WIN)
|
||||
@ -42,6 +43,9 @@
|
||||
#define FILE_CASE_SENSITIVE Qt::CaseSensitive
|
||||
#endif
|
||||
|
||||
static const auto TRUE_STR = QStringLiteral("true");
|
||||
static const auto FALSE_STR = QStringLiteral("false");
|
||||
|
||||
template <typename T> struct AddConst
|
||||
{
|
||||
typedef const T Type;
|
||||
|
@ -32,6 +32,7 @@
|
||||
#include <QRegularExpression>
|
||||
#include <QStringList>
|
||||
#include <QSysInfo>
|
||||
#include <QUrl>
|
||||
#include <QUuid>
|
||||
#include <cctype>
|
||||
|
||||
@ -259,6 +260,33 @@ namespace Tools
|
||||
}
|
||||
}
|
||||
|
||||
bool checkUrlValid(const QString& urlField)
|
||||
{
|
||||
if (urlField.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
QUrl url;
|
||||
if (urlField.contains("://")) {
|
||||
url = urlField;
|
||||
} else {
|
||||
url = QUrl::fromUserInput(urlField);
|
||||
}
|
||||
|
||||
if (url.scheme() != "file" && url.host().isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check for illegal characters. Adds also the wildcard * to the list
|
||||
QRegularExpression re("[<>\\^`{|}\\*]");
|
||||
auto match = re.match(urlField);
|
||||
if (match.hasMatch()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Escape common regex symbols except for *, ?, and |
|
||||
auto regexEscape = QRegularExpression(R"re(([-[\]{}()+.,\\\/^$#]))re");
|
||||
|
||||
|
@ -41,6 +41,7 @@ namespace Tools
|
||||
bool isBase64(const QByteArray& ba);
|
||||
void sleep(int ms);
|
||||
void wait(int ms);
|
||||
bool checkUrlValid(const QString& urlField);
|
||||
QString uuidToHex(const QUuid& uuid);
|
||||
QUuid hexToUuid(const QString& uuid);
|
||||
QRegularExpression convertToRegex(const QString& string,
|
||||
|
@ -4,6 +4,7 @@ if(WITH_XC_FDOSECRETS)
|
||||
add_library(fdosecrets STATIC
|
||||
# app settings page
|
||||
FdoSecretsPlugin.cpp
|
||||
widgets/SettingsModels.cpp
|
||||
widgets/SettingsWidgetFdoSecrets.cpp
|
||||
|
||||
# per database settings page
|
||||
|
@ -60,11 +60,15 @@ void FdoSecretsPlugin::updateServiceState()
|
||||
});
|
||||
if (!m_secretService->initialize()) {
|
||||
m_secretService.reset();
|
||||
FdoSecrets::settings()->setEnabled(false);
|
||||
return;
|
||||
}
|
||||
emit secretServiceStarted();
|
||||
}
|
||||
} else {
|
||||
if (m_secretService) {
|
||||
m_secretService.reset();
|
||||
emit secretServiceStopped();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -74,6 +78,11 @@ Service* FdoSecretsPlugin::serviceInstance() const
|
||||
return m_secretService.data();
|
||||
}
|
||||
|
||||
DatabaseTabWidget* FdoSecretsPlugin::dbTabs() const
|
||||
{
|
||||
return m_dbTabs;
|
||||
}
|
||||
|
||||
void FdoSecretsPlugin::emitRequestSwitchToDatabases()
|
||||
{
|
||||
emit requestSwitchToDatabases();
|
||||
|
@ -59,6 +59,11 @@ public:
|
||||
*/
|
||||
FdoSecrets::Service* serviceInstance() const;
|
||||
|
||||
/**
|
||||
* @return The db tabs widget, containing opened databases. Can be nullptr.
|
||||
*/
|
||||
DatabaseTabWidget* dbTabs() const;
|
||||
|
||||
public slots:
|
||||
void emitRequestSwitchToDatabases();
|
||||
void emitRequestShowNotification(const QString& msg, const QString& title = {});
|
||||
@ -67,6 +72,8 @@ signals:
|
||||
void error(const QString& msg);
|
||||
void requestSwitchToDatabases();
|
||||
void requestShowNotification(const QString& msg, const QString& title, int msTimeoutHint);
|
||||
void secretServiceStarted();
|
||||
void secretServiceStopped();
|
||||
|
||||
private:
|
||||
QPointer<DatabaseTabWidget> m_dbTabs;
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include "fdosecrets/objects/Item.h"
|
||||
#include "fdosecrets/objects/Prompt.h"
|
||||
#include "fdosecrets/objects/Service.h"
|
||||
#include "fdosecrets/objects/Session.h"
|
||||
|
||||
#include "core/Config.h"
|
||||
#include "core/Database.h"
|
||||
@ -29,6 +30,7 @@
|
||||
#include "gui/DatabaseWidget.h"
|
||||
|
||||
#include <QFileInfo>
|
||||
#include <QRegularExpression>
|
||||
|
||||
namespace FdoSecrets
|
||||
{
|
||||
@ -47,6 +49,14 @@ namespace FdoSecrets
|
||||
connect(backend, &DatabaseWidget::databaseUnlocked, this, &Collection::onDatabaseLockChanged);
|
||||
connect(backend, &DatabaseWidget::databaseLocked, this, &Collection::onDatabaseLockChanged);
|
||||
|
||||
// get notified whenever unlock db dialog finishes
|
||||
connect(parent, &Service::doneUnlockDatabaseInDialog, this, [this](bool accepted, DatabaseWidget* dbWidget) {
|
||||
if (!dbWidget || dbWidget != m_backend) {
|
||||
return;
|
||||
}
|
||||
emit doneUnlockCollection(accepted);
|
||||
});
|
||||
|
||||
reloadBackend();
|
||||
}
|
||||
|
||||
@ -241,6 +251,11 @@ namespace FdoSecrets
|
||||
terms << attributeToTerm(it.key(), it.value());
|
||||
}
|
||||
|
||||
// empty terms causes EntrySearcher returns everything
|
||||
if (terms.isEmpty()) {
|
||||
return QList<Item*>{};
|
||||
}
|
||||
|
||||
QList<Item*> items;
|
||||
const auto foundEntries = EntrySearcher().search(terms, m_exposedGroup);
|
||||
items.reserve(foundEntries.size());
|
||||
@ -267,7 +282,7 @@ namespace FdoSecrets
|
||||
const auto useWildcards = false;
|
||||
const auto exactMatch = true;
|
||||
const auto caseSensitive = true;
|
||||
term.regex = Tools::convertToRegex(value, useWildcards, exactMatch, caseSensitive);
|
||||
term.regex = Tools::convertToRegex(QRegularExpression::escape(value), useWildcards, exactMatch, caseSensitive);
|
||||
|
||||
return term;
|
||||
}
|
||||
@ -284,8 +299,13 @@ namespace FdoSecrets
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!pathToObject<Session>(secret.session)) {
|
||||
return DBusReturn<>::Error(QStringLiteral(DBUS_ERROR_SECRET_NO_SESSION));
|
||||
}
|
||||
|
||||
prompt = nullptr;
|
||||
|
||||
bool newlyCreated = true;
|
||||
Item* item = nullptr;
|
||||
QString itemPath;
|
||||
StringStringMap attributes;
|
||||
@ -303,6 +323,7 @@ namespace FdoSecrets
|
||||
}
|
||||
if (!existings.value().isEmpty() && replace) {
|
||||
item = existings.value().front();
|
||||
newlyCreated = false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -328,15 +349,25 @@ namespace FdoSecrets
|
||||
|
||||
// when creation finishes in backend, we will already have item
|
||||
item = m_entryToItem.value(entry, nullptr);
|
||||
Q_ASSERT(item);
|
||||
|
||||
if (!item) {
|
||||
// may happen if entry somehow ends up in recycle bin
|
||||
return DBusReturn<>::Error(QStringLiteral(DBUS_ERROR_SECRET_NO_SUCH_OBJECT));
|
||||
}
|
||||
}
|
||||
|
||||
ret = item->setProperties(properties);
|
||||
if (ret.isError()) {
|
||||
if (newlyCreated) {
|
||||
item->doDelete();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
ret = item->setSecret(secret);
|
||||
if (ret.isError()) {
|
||||
if (newlyCreated) {
|
||||
item->doDelete();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -439,7 +470,7 @@ namespace FdoSecrets
|
||||
|
||||
auto newUuid = FdoSecrets::settings()->exposedGroup(m_backend->database());
|
||||
auto newGroup = m_backend->database()->rootGroup()->findGroupByUuid(newUuid);
|
||||
if (!newGroup) {
|
||||
if (!newGroup || inRecycleBin(newGroup)) {
|
||||
// no exposed group, delete self
|
||||
doDelete();
|
||||
return;
|
||||
@ -451,14 +482,20 @@ namespace FdoSecrets
|
||||
m_exposedGroup = newGroup;
|
||||
|
||||
// Attach signal to update exposed group settings if the group was removed.
|
||||
// The lifetime of the connection is bound to the database object, because
|
||||
// in Database::~Database, groups are also deleted, but we don't want to
|
||||
// trigger this.
|
||||
// This rely on the fact that QObject disconnects signals BEFORE deleting
|
||||
// children.
|
||||
//
|
||||
// When the group object is normally deleted due to ~Database, the databaseReplaced
|
||||
// signal should be first emitted, and we will clean up connection in reloadDatabase,
|
||||
// so this handler won't be triggered.
|
||||
QPointer<Database> db = m_backend->database().data();
|
||||
connect(m_exposedGroup.data(), &Group::groupAboutToRemove, db, [db](Group* toBeRemoved) {
|
||||
if (!db) {
|
||||
connect(m_exposedGroup.data(), &Group::groupAboutToRemove, this, [this](Group* toBeRemoved) {
|
||||
if (backendLocked()) {
|
||||
return;
|
||||
}
|
||||
auto db = m_backend->database();
|
||||
if (toBeRemoved->database() != db) {
|
||||
// should not happen, but anyway.
|
||||
// somehow our current database has been changed, and the old group is being deleted
|
||||
// possibly logic changes in replaceDatabase.
|
||||
return;
|
||||
}
|
||||
auto uuid = FdoSecrets::settings()->exposedGroup(db);
|
||||
@ -468,10 +505,17 @@ namespace FdoSecrets
|
||||
FdoSecrets::settings()->setExposedGroup(db, {});
|
||||
}
|
||||
});
|
||||
// Another possibility is the group being moved to recycle bin.
|
||||
connect(m_exposedGroup.data(), &Group::groupModified, this, [this]() {
|
||||
if (inRecycleBin(m_exposedGroup->parentGroup())) {
|
||||
// reset the exposed group to none
|
||||
FdoSecrets::settings()->setExposedGroup(m_backend->database().data(), {});
|
||||
}
|
||||
});
|
||||
|
||||
// Monitor exposed group settings
|
||||
connect(m_backend->database()->metadata()->customData(), &CustomData::customDataModified, this, [this]() {
|
||||
if (!m_exposedGroup || !m_backend) {
|
||||
if (!m_exposedGroup || backendLocked()) {
|
||||
return;
|
||||
}
|
||||
if (m_exposedGroup->uuid() == FdoSecrets::settings()->exposedGroup(m_backend->database())) {
|
||||
@ -590,9 +634,13 @@ namespace FdoSecrets
|
||||
|
||||
void Collection::cleanupConnections()
|
||||
{
|
||||
m_backend->database()->metadata()->customData()->disconnect(this);
|
||||
if (m_exposedGroup) {
|
||||
m_exposedGroup->disconnect(this);
|
||||
for (const auto group : m_exposedGroup->groupsRecursive(true)) {
|
||||
group->disconnect(this);
|
||||
}
|
||||
}
|
||||
|
||||
m_items.clear();
|
||||
}
|
||||
|
||||
@ -646,17 +694,21 @@ namespace FdoSecrets
|
||||
{
|
||||
Q_ASSERT(m_backend);
|
||||
|
||||
if (!m_backend->database()->metadata()->recycleBin()) {
|
||||
if (!group) {
|
||||
// the root group's parent is nullptr, we treat it as not in recycle bin.
|
||||
return false;
|
||||
}
|
||||
|
||||
while (group) {
|
||||
if (group->uuid() == m_backend->database()->metadata()->recycleBin()->uuid()) {
|
||||
return true;
|
||||
}
|
||||
group = group->parentGroup();
|
||||
if (!m_backend->database()->metadata()) {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
|
||||
auto recycleBin = m_backend->database()->metadata()->recycleBin();
|
||||
if (!recycleBin) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return group->uuid() == recycleBin->uuid() || group->isRecycled();
|
||||
}
|
||||
|
||||
bool Collection::inRecycleBin(Entry* entry) const
|
||||
|
@ -71,9 +71,16 @@ namespace FdoSecrets
|
||||
void aliasAdded(const QString& alias);
|
||||
void aliasRemoved(const QString& alias);
|
||||
|
||||
void doneUnlockCollection(bool accepted);
|
||||
|
||||
public:
|
||||
DBusReturn<void> setProperties(const QVariantMap& properties);
|
||||
|
||||
bool isValid() const
|
||||
{
|
||||
return backend();
|
||||
}
|
||||
|
||||
DBusReturn<void> removeAlias(QString alias);
|
||||
DBusReturn<void> addAlias(QString alias);
|
||||
const QSet<QString> aliases() const;
|
||||
@ -106,6 +113,7 @@ namespace FdoSecrets
|
||||
private slots:
|
||||
void onDatabaseLockChanged();
|
||||
void onDatabaseExposedGroupChanged();
|
||||
// force reload info from backend, potentially delete self
|
||||
void reloadBackend();
|
||||
|
||||
private:
|
||||
|
@ -158,6 +158,12 @@ namespace FdoSecrets
|
||||
return std::move(m_value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get value or handle the error by the passed in dbus object
|
||||
* @tparam P
|
||||
* @param p
|
||||
* @return
|
||||
*/
|
||||
template <typename P> T valueOrHandle(P* p) const&
|
||||
{
|
||||
if (isError()) {
|
||||
@ -169,6 +175,12 @@ namespace FdoSecrets
|
||||
return m_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get value or handle the error by the passed in dbus object
|
||||
* @tparam P
|
||||
* @param p
|
||||
* @return
|
||||
*/
|
||||
template <typename P> T&& valueOrHandle(P* p) &&
|
||||
{
|
||||
if (isError()) {
|
||||
|
@ -93,9 +93,9 @@ namespace FdoSecrets
|
||||
|
||||
MessageBox::OverrideParent override(findWindow(windowId));
|
||||
// only need to delete in backend, collection will react itself.
|
||||
service()->doCloseDatabase(m_collection->backend());
|
||||
auto accepted = service()->doCloseDatabase(m_collection->backend());
|
||||
|
||||
emit completed(false, {});
|
||||
emit completed(!accepted, {});
|
||||
|
||||
return {};
|
||||
}
|
||||
@ -189,19 +189,44 @@ namespace FdoSecrets
|
||||
|
||||
MessageBox::OverrideParent override(findWindow(windowId));
|
||||
|
||||
QList<QDBusObjectPath> unlocked;
|
||||
for (const auto& c : asConst(m_collections)) {
|
||||
if (c) {
|
||||
connect(c, &Collection::doneUnlockCollection, this, &UnlockCollectionsPrompt::collectionUnlockFinished);
|
||||
c->doUnlock();
|
||||
unlocked << c->objectPath();
|
||||
}
|
||||
}
|
||||
|
||||
emit completed(false, QVariant::fromValue(unlocked));
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void UnlockCollectionsPrompt::collectionUnlockFinished(bool accepted)
|
||||
{
|
||||
auto coll = qobject_cast<Collection*>(sender());
|
||||
if (!coll) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m_collections.contains(coll)) {
|
||||
// should not happen
|
||||
coll->disconnect(this);
|
||||
return;
|
||||
}
|
||||
|
||||
// one shot
|
||||
coll->disconnect(this);
|
||||
|
||||
if (accepted) {
|
||||
m_unlocked << coll->objectPath();
|
||||
} else {
|
||||
m_numRejected += 1;
|
||||
}
|
||||
|
||||
// if we've get all
|
||||
if (m_numRejected + m_unlocked.size() == m_collections.size()) {
|
||||
emit completed(m_unlocked.isEmpty(), QVariant::fromValue(m_unlocked));
|
||||
}
|
||||
}
|
||||
|
||||
DeleteItemPrompt::DeleteItemPrompt(Service* parent, Item* item)
|
||||
: PromptBase(parent)
|
||||
, m_item(item)
|
||||
|
@ -98,8 +98,13 @@ namespace FdoSecrets
|
||||
|
||||
DBusReturn<void> prompt(const QString& windowId) override;
|
||||
|
||||
private slots:
|
||||
void collectionUnlockFinished(bool accepted);
|
||||
|
||||
private:
|
||||
QList<QPointer<Collection>> m_collections;
|
||||
QList<QDBusObjectPath> m_unlocked;
|
||||
int m_numRejected = 0;
|
||||
};
|
||||
|
||||
class Item;
|
||||
|
@ -47,7 +47,8 @@ namespace FdoSecrets
|
||||
, m_insdieEnsureDefaultAlias(false)
|
||||
, m_serviceWatcher(nullptr)
|
||||
{
|
||||
registerWithPath(QStringLiteral(DBUS_PATH_SECRETS), new ServiceAdaptor(this));
|
||||
connect(
|
||||
m_databases, &DatabaseTabWidget::databaseUnlockDialogFinished, this, &Service::doneUnlockDatabaseInDialog);
|
||||
}
|
||||
|
||||
Service::~Service()
|
||||
@ -64,6 +65,8 @@ namespace FdoSecrets
|
||||
return false;
|
||||
}
|
||||
|
||||
registerWithPath(QStringLiteral(DBUS_PATH_SECRETS), new ServiceAdaptor(this));
|
||||
|
||||
// Connect to service unregistered signal
|
||||
m_serviceWatcher.reset(new QDBusServiceWatcher());
|
||||
connect(m_serviceWatcher.data(),
|
||||
@ -93,7 +96,24 @@ namespace FdoSecrets
|
||||
|
||||
void Service::onDatabaseTabOpened(DatabaseWidget* dbWidget, bool emitSignal)
|
||||
{
|
||||
// The Collection will monitor the database's exposed group.
|
||||
// When the Collection finds that no exposed group, it will delete itself.
|
||||
// Thus the service also needs to monitor it and recreate the collection if the user changes
|
||||
// from no exposed to exposed something.
|
||||
if (!dbWidget->isLocked()) {
|
||||
monitorDatabaseExposedGroup(dbWidget);
|
||||
}
|
||||
connect(dbWidget, &DatabaseWidget::databaseUnlocked, this, [this, dbWidget]() {
|
||||
monitorDatabaseExposedGroup(dbWidget);
|
||||
});
|
||||
|
||||
auto coll = new Collection(this, dbWidget);
|
||||
// Creation may fail if the database is not exposed.
|
||||
// This is okay, because we monitor the expose settings above
|
||||
if (!coll->isValid()) {
|
||||
coll->deleteLater();
|
||||
return;
|
||||
}
|
||||
|
||||
m_collections << coll;
|
||||
m_dbToCollection[dbWidget] = coll;
|
||||
@ -127,15 +147,6 @@ namespace FdoSecrets
|
||||
emit collectionDeleted(coll);
|
||||
});
|
||||
|
||||
// a special case: the database changed from no expose to expose something.
|
||||
// in this case, there is no collection out there monitoring it, so create a new collection
|
||||
if (!dbWidget->isLocked()) {
|
||||
monitorDatabaseExposedGroup(dbWidget);
|
||||
}
|
||||
connect(dbWidget, &DatabaseWidget::databaseUnlocked, this, [this, dbWidget]() {
|
||||
monitorDatabaseExposedGroup(dbWidget);
|
||||
});
|
||||
|
||||
if (emitSignal) {
|
||||
emit collectionCreated(coll);
|
||||
}
|
||||
@ -438,9 +449,9 @@ namespace FdoSecrets
|
||||
return m_sessions;
|
||||
}
|
||||
|
||||
void Service::doCloseDatabase(DatabaseWidget* dbWidget)
|
||||
bool Service::doCloseDatabase(DatabaseWidget* dbWidget)
|
||||
{
|
||||
m_databases->closeDatabaseTab(dbWidget);
|
||||
return m_databases->closeDatabaseTab(dbWidget);
|
||||
}
|
||||
|
||||
Collection* Service::doNewDatabase()
|
||||
@ -463,11 +474,10 @@ namespace FdoSecrets
|
||||
|
||||
void Service::doSwitchToChangeDatabaseSettings(DatabaseWidget* dbWidget)
|
||||
{
|
||||
// switch selected to current
|
||||
// unlock if needed
|
||||
if (dbWidget->isLocked()) {
|
||||
m_databases->unlockDatabaseInDialog(dbWidget, DatabaseOpenDialog::Intent::None);
|
||||
return;
|
||||
}
|
||||
// switch selected to current
|
||||
m_databases->setCurrentWidget(dbWidget);
|
||||
m_databases->changeDatabaseSettings();
|
||||
|
||||
|
@ -88,6 +88,13 @@ namespace FdoSecrets
|
||||
*/
|
||||
void error(const QString& msg);
|
||||
|
||||
/**
|
||||
* Finish signal for async action doUnlockDatabaseInDialog
|
||||
* @param accepted If false, the action is canceled by the user
|
||||
* @param dbWidget The unlocked the dbWidget if succeed
|
||||
*/
|
||||
void doneUnlockDatabaseInDialog(bool accepted, DatabaseWidget* dbWidget);
|
||||
|
||||
public:
|
||||
/**
|
||||
* List of sessions
|
||||
@ -101,9 +108,14 @@ namespace FdoSecrets
|
||||
}
|
||||
|
||||
public slots:
|
||||
void doCloseDatabase(DatabaseWidget* dbWidget);
|
||||
bool doCloseDatabase(DatabaseWidget* dbWidget);
|
||||
Collection* doNewDatabase();
|
||||
void doSwitchToChangeDatabaseSettings(DatabaseWidget* dbWidget);
|
||||
|
||||
/**
|
||||
* Async, connect to signal doneUnlockDatabaseInDialog for finish notification
|
||||
* @param dbWidget
|
||||
*/
|
||||
void doUnlockDatabaseInDialog(DatabaseWidget* dbWidget);
|
||||
|
||||
private slots:
|
||||
|
@ -85,7 +85,7 @@ protected:
|
||||
// can not call mapFromSource, which internally calls filterAcceptsRow
|
||||
auto group = groupFromSourceIndex(source_idx);
|
||||
|
||||
return group->uuid() != recycleBin->uuid();
|
||||
return group && !group->isRecycled() && group->uuid() != recycleBin->uuid();
|
||||
}
|
||||
};
|
||||
|
||||
@ -118,8 +118,13 @@ void DatabaseSettingsWidgetFdoSecrets::loadSettings(QSharedPointer<Database> db)
|
||||
m_model.reset(new GroupModelNoRecycle(m_db.data()));
|
||||
m_ui->selectGroup->setModel(m_model.data());
|
||||
|
||||
Group* recycleBin = nullptr;
|
||||
if (m_db->metadata() && m_db->metadata()->recycleBin()) {
|
||||
recycleBin = m_db->metadata()->recycleBin();
|
||||
}
|
||||
|
||||
auto group = m_db->rootGroup()->findGroupByUuid(FdoSecrets::settings()->exposedGroup(m_db));
|
||||
if (!group) {
|
||||
if (!group || group->isRecycled() || (recycleBin && group->uuid() == recycleBin->uuid())) {
|
||||
m_ui->radioDonotExpose->setChecked(true);
|
||||
} else {
|
||||
auto idx = m_model->indexFromGroup(group);
|
||||
|
396
src/fdosecrets/widgets/SettingsModels.cpp
Normal file
396
src/fdosecrets/widgets/SettingsModels.cpp
Normal file
@ -0,0 +1,396 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Aetf <aetf@unlimitedcodeworks.xyz>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 or (at your option)
|
||||
* version 3 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "SettingsModels.h"
|
||||
|
||||
#include "fdosecrets/FdoSecretsPlugin.h"
|
||||
#include "fdosecrets/FdoSecretsSettings.h"
|
||||
#include "fdosecrets/objects/Service.h"
|
||||
#include "fdosecrets/objects/Session.h"
|
||||
|
||||
#include "core/Database.h"
|
||||
#include "core/DatabaseIcons.h"
|
||||
#include "core/FilePath.h"
|
||||
#include "gui/DatabaseTabWidget.h"
|
||||
#include "gui/DatabaseWidget.h"
|
||||
|
||||
#include <QFileInfo>
|
||||
|
||||
namespace FdoSecrets
|
||||
{
|
||||
|
||||
SettingsDatabaseModel::SettingsDatabaseModel(DatabaseTabWidget* dbTabs, QObject* parent)
|
||||
: QAbstractTableModel(parent)
|
||||
, m_dbTabs(nullptr)
|
||||
{
|
||||
setTabWidget(dbTabs);
|
||||
}
|
||||
|
||||
void SettingsDatabaseModel::setTabWidget(DatabaseTabWidget* dbTabs)
|
||||
{
|
||||
auto old = m_dbTabs;
|
||||
m_dbTabs = dbTabs;
|
||||
if (old != m_dbTabs) {
|
||||
populateModel();
|
||||
}
|
||||
}
|
||||
|
||||
int SettingsDatabaseModel::rowCount(const QModelIndex& parent) const
|
||||
{
|
||||
if (parent.isValid()) {
|
||||
return 0;
|
||||
}
|
||||
return m_dbs.size();
|
||||
}
|
||||
|
||||
int SettingsDatabaseModel::columnCount(const QModelIndex& parent) const
|
||||
{
|
||||
if (parent.isValid()) {
|
||||
return 0;
|
||||
}
|
||||
return 3;
|
||||
}
|
||||
|
||||
QVariant SettingsDatabaseModel::headerData(int section, Qt::Orientation orientation, int role) const
|
||||
{
|
||||
if (orientation != Qt::Horizontal) {
|
||||
return {};
|
||||
}
|
||||
|
||||
if (role != Qt::DisplayRole) {
|
||||
return {};
|
||||
}
|
||||
|
||||
switch (section) {
|
||||
case 0:
|
||||
return tr("File Name");
|
||||
case 1:
|
||||
return tr("Group");
|
||||
case 2:
|
||||
return tr("Manage");
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
QVariant SettingsDatabaseModel::data(const QModelIndex& index, int role) const
|
||||
{
|
||||
if (!index.isValid()) {
|
||||
return {};
|
||||
}
|
||||
const auto& dbWidget = m_dbs[index.row()];
|
||||
if (!dbWidget) {
|
||||
return {};
|
||||
}
|
||||
|
||||
switch (index.column()) {
|
||||
case 0:
|
||||
return dataForName(dbWidget, role);
|
||||
case 1:
|
||||
return dataForExposedGroup(dbWidget, role);
|
||||
case 2:
|
||||
return dataForManage(dbWidget, role);
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
QVariant SettingsDatabaseModel::dataForName(DatabaseWidget* db, int role) const
|
||||
{
|
||||
switch (role) {
|
||||
case Qt::DisplayRole: {
|
||||
QFileInfo fi(db->database()->filePath());
|
||||
return fi.fileName();
|
||||
}
|
||||
case Qt::ToolTipRole:
|
||||
return db->database()->filePath();
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
QVariant SettingsDatabaseModel::dataForExposedGroup(DatabaseWidget* dbWidget, int role)
|
||||
{
|
||||
if (dbWidget->isLocked()) {
|
||||
switch (role) {
|
||||
case Qt::DisplayRole:
|
||||
return tr("Unlock to show");
|
||||
case Qt::DecorationRole:
|
||||
return filePath()->icon(QStringLiteral("apps"), QStringLiteral("object-locked"), true);
|
||||
case Qt::FontRole: {
|
||||
QFont font;
|
||||
font.setItalic(true);
|
||||
return font;
|
||||
}
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
}
|
||||
auto db = dbWidget->database();
|
||||
auto group = db->rootGroup()->findGroupByUuid(FdoSecrets::settings()->exposedGroup(db));
|
||||
if (group) {
|
||||
switch (role) {
|
||||
case Qt::DisplayRole:
|
||||
return group->name();
|
||||
case Qt::DecorationRole:
|
||||
return group->isExpired() ? databaseIcons()->iconPixmap(DatabaseIcons::ExpiredIconIndex)
|
||||
: group->iconScaledPixmap();
|
||||
case Qt::FontRole:
|
||||
if (group->isExpired()) {
|
||||
QFont font;
|
||||
font.setStrikeOut(true);
|
||||
return font;
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
} else {
|
||||
switch (role) {
|
||||
case Qt::DisplayRole:
|
||||
return tr("None");
|
||||
case Qt::DecorationRole:
|
||||
return filePath()->icon(QStringLiteral("apps"), QStringLiteral("paint-none"), true);
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QVariant SettingsDatabaseModel::dataForManage(DatabaseWidget* db, int role) const
|
||||
{
|
||||
switch (role) {
|
||||
case Qt::EditRole:
|
||||
return QVariant::fromValue(db);
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
void SettingsDatabaseModel::populateModel()
|
||||
{
|
||||
beginResetModel();
|
||||
|
||||
m_dbs.clear();
|
||||
|
||||
if (m_dbTabs) {
|
||||
// Add existing database tabs
|
||||
for (int idx = 0; idx != m_dbTabs->count(); ++idx) {
|
||||
auto dbWidget = m_dbTabs->databaseWidgetFromIndex(idx);
|
||||
databaseAdded(dbWidget, false);
|
||||
}
|
||||
// connect signals
|
||||
connect(m_dbTabs, &DatabaseTabWidget::databaseOpened, this, [this](DatabaseWidget* db) {
|
||||
databaseAdded(db, true);
|
||||
});
|
||||
connect(m_dbTabs, &DatabaseTabWidget::databaseClosed, this, &SettingsDatabaseModel::databaseRemoved);
|
||||
}
|
||||
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
void SettingsDatabaseModel::databaseAdded(DatabaseWidget* db, bool emitSignals)
|
||||
{
|
||||
int row = m_dbs.size();
|
||||
if (emitSignals) {
|
||||
beginInsertRows({}, row, row);
|
||||
}
|
||||
|
||||
m_dbs.append(db);
|
||||
connect(db, &DatabaseWidget::databaseLocked, this, [row, this]() {
|
||||
emit dataChanged(index(row, 1), index(row, 2));
|
||||
});
|
||||
connect(db, &DatabaseWidget::databaseUnlocked, this, [row, this]() {
|
||||
emit dataChanged(index(row, 1), index(row, 2));
|
||||
});
|
||||
connect(db, &DatabaseWidget::databaseModified, this, [row, this]() {
|
||||
emit dataChanged(index(row, 0), index(row, 2));
|
||||
});
|
||||
connect(db, &DatabaseWidget::databaseFilePathChanged, this, [row, this]() {
|
||||
emit dataChanged(index(row, 0), index(row, 2));
|
||||
});
|
||||
|
||||
if (emitSignals) {
|
||||
endInsertRows();
|
||||
}
|
||||
}
|
||||
|
||||
void SettingsDatabaseModel::databaseRemoved(const QString& filePath)
|
||||
{
|
||||
for (int i = 0; i != m_dbs.size(); i++) {
|
||||
if (m_dbs[i] && m_dbs[i]->database()->filePath() == filePath) {
|
||||
beginRemoveRows({}, i, i);
|
||||
|
||||
m_dbs[i]->disconnect(this);
|
||||
m_dbs.removeAt(i);
|
||||
|
||||
endRemoveRows();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SettingsSessionModel::SettingsSessionModel(FdoSecretsPlugin* plugin, QObject* parent)
|
||||
: QAbstractTableModel(parent)
|
||||
, m_service(nullptr)
|
||||
{
|
||||
setService(plugin->serviceInstance());
|
||||
connect(plugin, &FdoSecretsPlugin::secretServiceStarted, this, [plugin, this]() {
|
||||
setService(plugin->serviceInstance());
|
||||
});
|
||||
connect(plugin, &FdoSecretsPlugin::secretServiceStopped, this, [this]() { setService(nullptr); });
|
||||
}
|
||||
|
||||
void SettingsSessionModel::setService(Service* service)
|
||||
{
|
||||
auto old = m_service;
|
||||
m_service = service;
|
||||
if (old != m_service) {
|
||||
populateModel();
|
||||
}
|
||||
}
|
||||
|
||||
int SettingsSessionModel::rowCount(const QModelIndex& parent) const
|
||||
{
|
||||
if (parent.isValid()) {
|
||||
return 0;
|
||||
}
|
||||
return m_sessions.size();
|
||||
}
|
||||
|
||||
int SettingsSessionModel::columnCount(const QModelIndex& parent) const
|
||||
{
|
||||
if (parent.isValid()) {
|
||||
return 0;
|
||||
}
|
||||
return 2;
|
||||
}
|
||||
|
||||
QVariant SettingsSessionModel::headerData(int section, Qt::Orientation orientation, int role) const
|
||||
{
|
||||
if (orientation != Qt::Horizontal) {
|
||||
return {};
|
||||
}
|
||||
|
||||
if (role != Qt::DisplayRole) {
|
||||
return {};
|
||||
}
|
||||
|
||||
switch (section) {
|
||||
case 0:
|
||||
return tr("Application");
|
||||
case 1:
|
||||
return tr("Manage");
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
QVariant SettingsSessionModel::data(const QModelIndex& index, int role) const
|
||||
{
|
||||
if (!index.isValid()) {
|
||||
return {};
|
||||
}
|
||||
const auto& sess = m_sessions[index.row()];
|
||||
if (!sess) {
|
||||
return {};
|
||||
}
|
||||
|
||||
switch (index.column()) {
|
||||
case 0:
|
||||
return dataForApplication(sess, role);
|
||||
case 1:
|
||||
return dataForManage(sess, role);
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
QVariant SettingsSessionModel::dataForApplication(Session* sess, int role) const
|
||||
{
|
||||
switch (role) {
|
||||
case Qt::DisplayRole:
|
||||
return sess->peer();
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
QVariant SettingsSessionModel::dataForManage(Session* sess, int role) const
|
||||
{
|
||||
switch (role) {
|
||||
case Qt::EditRole: {
|
||||
auto v = QVariant::fromValue(sess);
|
||||
qDebug() << v << v.type() << v.userType();
|
||||
return v;
|
||||
}
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
void SettingsSessionModel::populateModel()
|
||||
{
|
||||
beginResetModel();
|
||||
|
||||
m_sessions.clear();
|
||||
|
||||
if (m_service) {
|
||||
// Add existing database tabs
|
||||
for (const auto& sess : m_service->sessions()) {
|
||||
sessionAdded(sess, false);
|
||||
}
|
||||
|
||||
// connect signals
|
||||
connect(m_service, &Service::sessionOpened, this, [this](Session* sess) { sessionAdded(sess, true); });
|
||||
connect(m_service, &Service::sessionClosed, this, &SettingsSessionModel::sessionRemoved);
|
||||
}
|
||||
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
void SettingsSessionModel::sessionAdded(Session* sess, bool emitSignals)
|
||||
{
|
||||
int row = m_sessions.size();
|
||||
if (emitSignals) {
|
||||
beginInsertRows({}, row, row);
|
||||
}
|
||||
|
||||
m_sessions.append(sess);
|
||||
|
||||
if (emitSignals) {
|
||||
endInsertRows();
|
||||
}
|
||||
}
|
||||
|
||||
void SettingsSessionModel::sessionRemoved(Session* sess)
|
||||
{
|
||||
for (int i = 0; i != m_sessions.size(); i++) {
|
||||
if (m_sessions[i] == sess) {
|
||||
beginRemoveRows({}, i, i);
|
||||
|
||||
m_sessions[i]->disconnect(this);
|
||||
m_sessions.removeAt(i);
|
||||
|
||||
endRemoveRows();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace FdoSecrets
|
96
src/fdosecrets/widgets/SettingsModels.h
Normal file
96
src/fdosecrets/widgets/SettingsModels.h
Normal file
@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Aetf <aetf@unlimitedcodeworks.xyz>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 or (at your option)
|
||||
* version 3 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef KEEPASSXC_FDOSECRETS_SETTINGSMODELS_H
|
||||
#define KEEPASSXC_FDOSECRETS_SETTINGSMODELS_H
|
||||
|
||||
#include <QAbstractTableModel>
|
||||
#include <QPointer>
|
||||
|
||||
class DatabaseTabWidget;
|
||||
class DatabaseWidget;
|
||||
class FdoSecretsPlugin;
|
||||
|
||||
namespace FdoSecrets
|
||||
{
|
||||
class SettingsDatabaseModel : public QAbstractTableModel
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit SettingsDatabaseModel(DatabaseTabWidget* dbTabs, QObject* parent = nullptr);
|
||||
|
||||
void setTabWidget(DatabaseTabWidget* dbTabs);
|
||||
|
||||
int rowCount(const QModelIndex& parent) const override;
|
||||
int columnCount(const QModelIndex& parent) const override;
|
||||
QVariant data(const QModelIndex& index, int role) const override;
|
||||
QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
|
||||
|
||||
private:
|
||||
QVariant dataForName(DatabaseWidget* db, int role) const;
|
||||
static QVariant dataForExposedGroup(DatabaseWidget* db, int role);
|
||||
QVariant dataForManage(DatabaseWidget* db, int role) const;
|
||||
|
||||
private slots:
|
||||
void populateModel();
|
||||
void databaseAdded(DatabaseWidget* db, bool emitSignals);
|
||||
void databaseRemoved(const QString& filePath);
|
||||
|
||||
private:
|
||||
// source
|
||||
QPointer<DatabaseTabWidget> m_dbTabs;
|
||||
|
||||
// internal store
|
||||
QList<QPointer<DatabaseWidget>> m_dbs;
|
||||
};
|
||||
|
||||
class Service;
|
||||
class Session;
|
||||
|
||||
class SettingsSessionModel : public QAbstractTableModel
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit SettingsSessionModel(FdoSecretsPlugin* plugin, QObject* parent = nullptr);
|
||||
|
||||
int rowCount(const QModelIndex& parent) const override;
|
||||
int columnCount(const QModelIndex& parent) const override;
|
||||
QVariant data(const QModelIndex& index, int role) const override;
|
||||
QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
|
||||
|
||||
private:
|
||||
void setService(Service* service);
|
||||
|
||||
QVariant dataForApplication(Session* sess, int role) const;
|
||||
QVariant dataForManage(Session* sess, int role) const;
|
||||
|
||||
private slots:
|
||||
void populateModel();
|
||||
void sessionAdded(Session* sess, bool emitSignals);
|
||||
void sessionRemoved(Session* sess);
|
||||
|
||||
private:
|
||||
// source
|
||||
QPointer<Service> m_service;
|
||||
|
||||
// internal copy, so we can emit with changed index
|
||||
QList<Session*> m_sessions;
|
||||
};
|
||||
|
||||
} // namespace FdoSecrets
|
||||
|
||||
#endif // KEEPASSXC_FDOSECRETS_SETTINGSMODELS_H
|
@ -20,241 +20,273 @@
|
||||
|
||||
#include "fdosecrets/FdoSecretsPlugin.h"
|
||||
#include "fdosecrets/FdoSecretsSettings.h"
|
||||
#include "fdosecrets/objects/Collection.h"
|
||||
#include "fdosecrets/objects/Prompt.h"
|
||||
#include "fdosecrets/objects/Session.h"
|
||||
#include "fdosecrets/widgets/SettingsModels.h"
|
||||
|
||||
#include "core/DatabaseIcons.h"
|
||||
#include "core/FilePath.h"
|
||||
#include "gui/DatabaseWidget.h"
|
||||
|
||||
#include <QAction>
|
||||
#include <QDebug>
|
||||
#include <QFileInfo>
|
||||
#include <QHeaderView>
|
||||
#include <QPushButton>
|
||||
#include <QTableWidget>
|
||||
#include <QItemEditorFactory>
|
||||
#include <QStyledItemDelegate>
|
||||
#include <QToolBar>
|
||||
#include <QVariant>
|
||||
|
||||
using FdoSecrets::Collection;
|
||||
using FdoSecrets::Service;
|
||||
using FdoSecrets::Session;
|
||||
using FdoSecrets::SettingsDatabaseModel;
|
||||
using FdoSecrets::SettingsSessionModel;
|
||||
|
||||
namespace
|
||||
{
|
||||
class ManageDatabase : public QToolBar
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(DatabaseWidget* dbWidget READ dbWidget WRITE setDbWidget USER true)
|
||||
|
||||
public:
|
||||
explicit ManageDatabase(FdoSecretsPlugin* plugin, QWidget* parent = nullptr)
|
||||
: QToolBar(parent)
|
||||
, m_plugin(plugin)
|
||||
{
|
||||
setFloatable(false);
|
||||
setMovable(false);
|
||||
|
||||
// use a dummy widget to center the buttons
|
||||
auto spacer = new QWidget(this);
|
||||
spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
|
||||
spacer->setVisible(true);
|
||||
addWidget(spacer);
|
||||
|
||||
// db settings
|
||||
m_dbSettingsAct = new QAction(tr("Database settings"), this);
|
||||
m_dbSettingsAct->setIcon(filePath()->icon(QStringLiteral("actions"), QStringLiteral("document-edit")));
|
||||
m_dbSettingsAct->setToolTip(tr("Edit database settings"));
|
||||
m_dbSettingsAct->setEnabled(false);
|
||||
connect(m_dbSettingsAct, &QAction::triggered, this, [this]() {
|
||||
if (!m_dbWidget) {
|
||||
return;
|
||||
}
|
||||
auto db = m_dbWidget;
|
||||
m_plugin->serviceInstance()->doSwitchToChangeDatabaseSettings(m_dbWidget);
|
||||
});
|
||||
addAction(m_dbSettingsAct);
|
||||
|
||||
// unlock/lock
|
||||
m_lockAct = new QAction(tr("Unlock database"), this);
|
||||
m_lockAct->setIcon(filePath()->icon(QStringLiteral("actions"), QStringLiteral("object-locked"), false));
|
||||
m_lockAct->setToolTip(tr("Unlock database to show more information"));
|
||||
connect(m_lockAct, &QAction::triggered, this, [this]() {
|
||||
if (!m_dbWidget) {
|
||||
return;
|
||||
}
|
||||
if (m_dbWidget->isLocked()) {
|
||||
m_plugin->serviceInstance()->doUnlockDatabaseInDialog(m_dbWidget);
|
||||
} else {
|
||||
m_dbWidget->lock();
|
||||
}
|
||||
});
|
||||
|
||||
addAction(m_lockAct);
|
||||
|
||||
// use a dummy widget to center the buttons
|
||||
spacer = new QWidget(this);
|
||||
spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
|
||||
spacer->setVisible(true);
|
||||
addWidget(spacer);
|
||||
}
|
||||
|
||||
DatabaseWidget* dbWidget() const
|
||||
{
|
||||
return m_dbWidget;
|
||||
}
|
||||
|
||||
void setDbWidget(DatabaseWidget* dbWidget)
|
||||
{
|
||||
if (m_dbWidget == dbWidget) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_dbWidget) {
|
||||
disconnect();
|
||||
}
|
||||
|
||||
m_dbWidget = dbWidget;
|
||||
|
||||
reconnect();
|
||||
}
|
||||
|
||||
private:
|
||||
void disconnect()
|
||||
{
|
||||
if (!m_dbWidget) {
|
||||
return;
|
||||
}
|
||||
m_dbWidget->disconnect(this);
|
||||
}
|
||||
|
||||
void reconnect()
|
||||
{
|
||||
if (!m_dbWidget) {
|
||||
return;
|
||||
}
|
||||
connect(m_dbWidget, &DatabaseWidget::databaseLocked, this, [this]() {
|
||||
m_lockAct->setText(tr("Unlock database"));
|
||||
m_lockAct->setIcon(filePath()->icon(QStringLiteral("actions"), QStringLiteral("object-locked"), false));
|
||||
m_lockAct->setToolTip(tr("Unlock database to show more information"));
|
||||
m_dbSettingsAct->setEnabled(false);
|
||||
});
|
||||
connect(m_dbWidget, &DatabaseWidget::databaseUnlocked, this, [this]() {
|
||||
m_lockAct->setText(tr("Lock database"));
|
||||
m_lockAct->setIcon(
|
||||
filePath()->icon(QStringLiteral("actions"), QStringLiteral("object-unlocked"), false));
|
||||
m_lockAct->setToolTip(tr("Lock database"));
|
||||
m_dbSettingsAct->setEnabled(true);
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
FdoSecretsPlugin* m_plugin = nullptr;
|
||||
QPointer<DatabaseWidget> m_dbWidget = nullptr;
|
||||
QAction* m_dbSettingsAct = nullptr;
|
||||
QAction* m_lockAct = nullptr;
|
||||
};
|
||||
|
||||
class ManageSession : public QToolBar
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(Session* session READ session WRITE setSession USER true)
|
||||
|
||||
public:
|
||||
explicit ManageSession(FdoSecretsPlugin*, QWidget* parent = nullptr)
|
||||
: QToolBar(parent)
|
||||
{
|
||||
setFloatable(false);
|
||||
setMovable(false);
|
||||
|
||||
// use a dummy widget to center the buttons
|
||||
auto spacer = new QWidget(this);
|
||||
spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
|
||||
spacer->setVisible(true);
|
||||
addWidget(spacer);
|
||||
|
||||
m_disconnectAct = new QAction(tr("Disconnect"), this);
|
||||
m_disconnectAct->setIcon(filePath()->icon(QStringLiteral("actions"), QStringLiteral("dialog-close")));
|
||||
m_disconnectAct->setToolTip(tr("Disconnect this application"));
|
||||
connect(m_disconnectAct, &QAction::triggered, this, [this]() {
|
||||
if (m_session) {
|
||||
m_session->close();
|
||||
}
|
||||
});
|
||||
addAction(m_disconnectAct);
|
||||
|
||||
// use a dummy widget to center the buttons
|
||||
spacer = new QWidget(this);
|
||||
spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
|
||||
spacer->setVisible(true);
|
||||
addWidget(spacer);
|
||||
}
|
||||
|
||||
Session* session()
|
||||
{
|
||||
return m_session;
|
||||
}
|
||||
|
||||
void setSession(Session* sess)
|
||||
{
|
||||
m_session = sess;
|
||||
}
|
||||
|
||||
private:
|
||||
Session* m_session = nullptr;
|
||||
QAction* m_disconnectAct = nullptr;
|
||||
};
|
||||
|
||||
template <typename T> class Creator : public QItemEditorCreatorBase
|
||||
{
|
||||
public:
|
||||
inline explicit Creator(FdoSecretsPlugin* plugin)
|
||||
: QItemEditorCreatorBase()
|
||||
, m_plugin(plugin)
|
||||
, m_propertyName(T::staticMetaObject.userProperty().name())
|
||||
{
|
||||
}
|
||||
|
||||
inline QWidget* createWidget(QWidget* parent) const override
|
||||
{
|
||||
return new T(m_plugin, parent);
|
||||
}
|
||||
|
||||
inline QByteArray valuePropertyName() const override
|
||||
{
|
||||
return m_propertyName;
|
||||
}
|
||||
|
||||
private:
|
||||
FdoSecretsPlugin* m_plugin;
|
||||
QByteArray m_propertyName;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
SettingsWidgetFdoSecrets::SettingsWidgetFdoSecrets(FdoSecretsPlugin* plugin, QWidget* parent)
|
||||
: QWidget(parent)
|
||||
, m_ui(new Ui::SettingsWidgetFdoSecrets())
|
||||
, m_factory(new QItemEditorFactory)
|
||||
, m_plugin(plugin)
|
||||
{
|
||||
m_ui->setupUi(this);
|
||||
|
||||
auto sessHeader = m_ui->tableSessions->horizontalHeader();
|
||||
sessHeader->setSelectionMode(QAbstractItemView::NoSelection);
|
||||
sessHeader->setSectionsClickable(false);
|
||||
sessHeader->setSectionResizeMode(0, QHeaderView::Stretch); // application
|
||||
sessHeader->setSectionResizeMode(1, QHeaderView::ResizeToContents); // disconnect button
|
||||
auto sessModel = new SettingsSessionModel(plugin, this);
|
||||
m_ui->tableSessions->setModel(sessModel);
|
||||
setupView(m_ui->tableSessions, 1, qMetaTypeId<Session*>(), new Creator<ManageSession>(m_plugin));
|
||||
|
||||
auto dbHeader = m_ui->tableDatabases->horizontalHeader();
|
||||
dbHeader->setSelectionMode(QAbstractItemView::NoSelection);
|
||||
dbHeader->setSectionsClickable(false);
|
||||
dbHeader->setSectionResizeMode(0, QHeaderView::Stretch); // file name
|
||||
dbHeader->setSectionResizeMode(1, QHeaderView::Stretch); // group
|
||||
dbHeader->setSectionResizeMode(2, QHeaderView::ResizeToContents); // manage button
|
||||
// config header after setting model, otherwise the header doesn't have enough sections
|
||||
auto sessViewHeader = m_ui->tableSessions->horizontalHeader();
|
||||
sessViewHeader->setSelectionMode(QAbstractItemView::NoSelection);
|
||||
sessViewHeader->setSectionsClickable(false);
|
||||
sessViewHeader->setSectionResizeMode(0, QHeaderView::Stretch); // application
|
||||
sessViewHeader->setSectionResizeMode(1, QHeaderView::ResizeToContents); // disconnect button
|
||||
|
||||
auto dbModel = new SettingsDatabaseModel(plugin->dbTabs(), this);
|
||||
m_ui->tableDatabases->setModel(dbModel);
|
||||
setupView(m_ui->tableDatabases, 2, qMetaTypeId<DatabaseWidget*>(), new Creator<ManageDatabase>(m_plugin));
|
||||
|
||||
// config header after setting model, otherwise the header doesn't have enough sections
|
||||
auto dbViewHeader = m_ui->tableDatabases->horizontalHeader();
|
||||
dbViewHeader->setSelectionMode(QAbstractItemView::NoSelection);
|
||||
dbViewHeader->setSectionsClickable(false);
|
||||
dbViewHeader->setSectionResizeMode(0, QHeaderView::Stretch); // file name
|
||||
dbViewHeader->setSectionResizeMode(1, QHeaderView::Stretch); // group
|
||||
dbViewHeader->setSectionResizeMode(2, QHeaderView::ResizeToContents); // manage button
|
||||
|
||||
m_ui->tabWidget->setEnabled(m_ui->enableFdoSecretService->isChecked());
|
||||
connect(m_ui->enableFdoSecretService, &QCheckBox::toggled, m_ui->tabWidget, &QTabWidget::setEnabled);
|
||||
}
|
||||
|
||||
void SettingsWidgetFdoSecrets::setupView(QAbstractItemView* view,
|
||||
int manageColumn,
|
||||
int editorTypeId,
|
||||
QItemEditorCreatorBase* creator)
|
||||
{
|
||||
auto manageButtonDelegate = new QStyledItemDelegate(this);
|
||||
m_factory->registerEditor(editorTypeId, creator);
|
||||
manageButtonDelegate->setItemEditorFactory(m_factory.data());
|
||||
view->setItemDelegateForColumn(manageColumn, manageButtonDelegate);
|
||||
connect(view->model(),
|
||||
&QAbstractItemModel::rowsInserted,
|
||||
this,
|
||||
[view, manageColumn](const QModelIndex&, int first, int last) {
|
||||
for (int i = first; i <= last; ++i) {
|
||||
auto idx = view->model()->index(i, manageColumn);
|
||||
view->openPersistentEditor(idx);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
SettingsWidgetFdoSecrets::~SettingsWidgetFdoSecrets() = default;
|
||||
|
||||
void SettingsWidgetFdoSecrets::populateSessions(bool enabled)
|
||||
{
|
||||
m_ui->tableSessions->setRowCount(0);
|
||||
|
||||
auto service = m_plugin->serviceInstance();
|
||||
if (!service || !enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const auto& sess : service->sessions()) {
|
||||
addSessionRow(sess);
|
||||
}
|
||||
}
|
||||
|
||||
void SettingsWidgetFdoSecrets::addSessionRow(Session* sess)
|
||||
{
|
||||
auto row = m_ui->tableSessions->rowCount();
|
||||
m_ui->tableSessions->insertRow(row);
|
||||
|
||||
// column 0: application name
|
||||
auto item = new QTableWidgetItem(sess->peer());
|
||||
item->setData(Qt::UserRole, QVariant::fromValue(sess));
|
||||
m_ui->tableSessions->setItem(row, 0, item);
|
||||
|
||||
// column 1: disconnect button
|
||||
auto btn = new QPushButton(tr("Disconnect"));
|
||||
connect(btn, &QPushButton::clicked, sess, &Session::close);
|
||||
m_ui->tableSessions->setCellWidget(row, 1, btn);
|
||||
|
||||
// column 2: hidden uuid
|
||||
m_ui->tableSessions->setItem(row, 2, new QTableWidgetItem(sess->id()));
|
||||
}
|
||||
|
||||
void SettingsWidgetFdoSecrets::removeSessionRow(Session* sess)
|
||||
{
|
||||
int row = 0;
|
||||
while (row != m_ui->tableSessions->rowCount()) {
|
||||
auto item = m_ui->tableSessions->item(row, 0);
|
||||
const auto itemSess = item->data(Qt::UserRole).value<Session*>();
|
||||
if (itemSess == sess) {
|
||||
break;
|
||||
}
|
||||
++row;
|
||||
}
|
||||
if (row == m_ui->tableSessions->rowCount()) {
|
||||
qWarning() << "Unknown Fdo Secret Service session" << sess->id() << "while removing collection from table";
|
||||
return;
|
||||
}
|
||||
|
||||
m_ui->tableSessions->removeRow(row);
|
||||
}
|
||||
|
||||
void SettingsWidgetFdoSecrets::populateDatabases(bool enabled)
|
||||
{
|
||||
m_ui->tableDatabases->setRowCount(0);
|
||||
|
||||
auto service = m_plugin->serviceInstance();
|
||||
if (!service || !enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto ret = service->collections();
|
||||
if (ret.isError()) {
|
||||
return;
|
||||
}
|
||||
for (const auto& coll : ret.value()) {
|
||||
addDatabaseRow(coll);
|
||||
}
|
||||
}
|
||||
|
||||
void SettingsWidgetFdoSecrets::addDatabaseRow(Collection* coll)
|
||||
{
|
||||
auto row = m_ui->tableDatabases->rowCount();
|
||||
m_ui->tableDatabases->insertRow(row);
|
||||
|
||||
// column 0: File name
|
||||
QFileInfo fi(coll->backend()->database()->filePath());
|
||||
auto item = new QTableWidgetItem(fi.fileName());
|
||||
item->setData(Qt::UserRole, QVariant::fromValue(coll));
|
||||
m_ui->tableDatabases->setItem(row, 0, item);
|
||||
|
||||
// column 2: manage button: hboxlayout: unlock/lock settings
|
||||
// create this first so we have a widget to bind connection to,
|
||||
// which can then be auto deleted when the row is deleted.
|
||||
auto widget = createManageButtons(coll);
|
||||
m_ui->tableDatabases->setCellWidget(row, 2, widget);
|
||||
|
||||
// column 1: Group name
|
||||
auto itemGroupName = new QTableWidgetItem();
|
||||
updateExposedGroupItem(itemGroupName, coll);
|
||||
|
||||
connect(coll, &Collection::collectionLockChanged, widget, [this, itemGroupName, coll](bool) {
|
||||
updateExposedGroupItem(itemGroupName, coll);
|
||||
});
|
||||
|
||||
m_ui->tableDatabases->setItem(row, 1, itemGroupName);
|
||||
}
|
||||
|
||||
QWidget* SettingsWidgetFdoSecrets::createManageButtons(Collection* coll)
|
||||
{
|
||||
auto toolbar = new QToolBar;
|
||||
toolbar->setFloatable(false);
|
||||
toolbar->setMovable(false);
|
||||
|
||||
// db settings
|
||||
auto dbSettingsAct = new QAction(tr("Database settings"), toolbar);
|
||||
dbSettingsAct->setIcon(filePath()->icon(QStringLiteral("actions"), QStringLiteral("document-edit")));
|
||||
dbSettingsAct->setToolTip(tr("Edit database settings"));
|
||||
dbSettingsAct->setEnabled(!coll->locked().value());
|
||||
connect(dbSettingsAct, &QAction::triggered, this, [this, coll]() {
|
||||
auto db = coll->backend();
|
||||
m_plugin->serviceInstance()->doSwitchToChangeDatabaseSettings(db);
|
||||
});
|
||||
toolbar->addAction(dbSettingsAct);
|
||||
|
||||
// unlock/lock
|
||||
auto lockAct = new QAction(tr("Unlock database"), toolbar);
|
||||
lockAct->setIcon(filePath()->icon(QStringLiteral("actions"), QStringLiteral("object-locked"), true));
|
||||
lockAct->setToolTip(tr("Unlock database to show more information"));
|
||||
connect(coll, &Collection::collectionLockChanged, lockAct, [lockAct, dbSettingsAct](bool locked) {
|
||||
if (locked) {
|
||||
lockAct->setIcon(filePath()->icon(QStringLiteral("actions"), QStringLiteral("object-locked"), true));
|
||||
lockAct->setToolTip(tr("Unlock database to show more information"));
|
||||
} else {
|
||||
lockAct->setIcon(filePath()->icon(QStringLiteral("actions"), QStringLiteral("object-unlocked"), true));
|
||||
lockAct->setToolTip(tr("Lock database"));
|
||||
}
|
||||
dbSettingsAct->setEnabled(!locked);
|
||||
});
|
||||
connect(lockAct, &QAction::triggered, this, [coll]() {
|
||||
if (coll->locked().value()) {
|
||||
coll->doUnlock();
|
||||
} else {
|
||||
coll->doLock();
|
||||
}
|
||||
});
|
||||
toolbar->addAction(lockAct);
|
||||
|
||||
return toolbar;
|
||||
}
|
||||
|
||||
void SettingsWidgetFdoSecrets::updateExposedGroupItem(QTableWidgetItem* item, Collection* coll)
|
||||
{
|
||||
if (coll->locked().value()) {
|
||||
item->setText(tr("Unlock to show"));
|
||||
item->setIcon(filePath()->icon(QStringLiteral("apps"), QStringLiteral("object-locked"), true));
|
||||
QFont font;
|
||||
font.setItalic(true);
|
||||
item->setFont(font);
|
||||
return;
|
||||
}
|
||||
|
||||
auto db = coll->backend()->database();
|
||||
auto group = db->rootGroup()->findGroupByUuid(FdoSecrets::settings()->exposedGroup(db));
|
||||
if (group) {
|
||||
item->setText(group->name());
|
||||
item->setIcon(group->isExpired() ? databaseIcons()->iconPixmap(DatabaseIcons::ExpiredIconIndex)
|
||||
: group->iconScaledPixmap());
|
||||
if (group->isExpired()) {
|
||||
QFont font;
|
||||
font.setStrikeOut(true);
|
||||
item->setFont(font);
|
||||
}
|
||||
} else {
|
||||
item->setText(tr("None"));
|
||||
item->setIcon(filePath()->icon(QStringLiteral("apps"), QStringLiteral("paint-none"), true));
|
||||
}
|
||||
}
|
||||
|
||||
void SettingsWidgetFdoSecrets::removeDatabaseRow(Collection* coll)
|
||||
{
|
||||
int row = 0;
|
||||
while (row != m_ui->tableDatabases->rowCount()) {
|
||||
auto item = m_ui->tableDatabases->item(row, 0);
|
||||
const auto itemColl = item->data(Qt::UserRole).value<Collection*>();
|
||||
if (itemColl == coll) {
|
||||
break;
|
||||
}
|
||||
++row;
|
||||
}
|
||||
if (row == m_ui->tableDatabases->rowCount()) {
|
||||
qWarning() << "Unknown Fdo Secret Service collection" << coll->name() << "while removing collection from table";
|
||||
return;
|
||||
}
|
||||
|
||||
m_ui->tableDatabases->removeRow(row);
|
||||
}
|
||||
|
||||
void SettingsWidgetFdoSecrets::loadSettings()
|
||||
{
|
||||
m_ui->enableFdoSecretService->setChecked(FdoSecrets::settings()->isEnabled());
|
||||
@ -269,52 +301,4 @@ void SettingsWidgetFdoSecrets::saveSettings()
|
||||
FdoSecrets::settings()->setNoConfirmDeleteItem(m_ui->noConfirmDeleteItem->isChecked());
|
||||
}
|
||||
|
||||
void SettingsWidgetFdoSecrets::showEvent(QShowEvent* event)
|
||||
{
|
||||
QWidget::showEvent(event);
|
||||
|
||||
QMetaObject::invokeMethod(this, "updateTables", Qt::QueuedConnection, Q_ARG(bool, true));
|
||||
}
|
||||
|
||||
void SettingsWidgetFdoSecrets::hideEvent(QHideEvent* event)
|
||||
{
|
||||
QWidget::hideEvent(event);
|
||||
|
||||
QMetaObject::invokeMethod(this, "updateTables", Qt::QueuedConnection, Q_ARG(bool, false));
|
||||
}
|
||||
|
||||
void SettingsWidgetFdoSecrets::updateTables(bool enabled)
|
||||
{
|
||||
if (enabled) {
|
||||
// update the table
|
||||
populateDatabases(m_ui->enableFdoSecretService->isChecked());
|
||||
populateSessions(m_ui->enableFdoSecretService->isChecked());
|
||||
|
||||
// re-layout the widget to adjust the table cell size
|
||||
adjustSize();
|
||||
|
||||
connect(m_ui->enableFdoSecretService, &QCheckBox::toggled, this, &SettingsWidgetFdoSecrets::populateSessions);
|
||||
connect(m_ui->enableFdoSecretService, &QCheckBox::toggled, this, &SettingsWidgetFdoSecrets::populateDatabases);
|
||||
|
||||
auto service = m_plugin->serviceInstance();
|
||||
if (service) {
|
||||
connect(service, &Service::sessionOpened, this, &SettingsWidgetFdoSecrets::addSessionRow);
|
||||
connect(service, &Service::sessionClosed, this, &SettingsWidgetFdoSecrets::removeSessionRow);
|
||||
connect(service, &Service::collectionCreated, this, &SettingsWidgetFdoSecrets::addDatabaseRow);
|
||||
connect(service, &Service::collectionDeleted, this, &SettingsWidgetFdoSecrets::removeDatabaseRow);
|
||||
}
|
||||
} else {
|
||||
disconnect(
|
||||
m_ui->enableFdoSecretService, &QCheckBox::toggled, this, &SettingsWidgetFdoSecrets::populateSessions);
|
||||
disconnect(
|
||||
m_ui->enableFdoSecretService, &QCheckBox::toggled, this, &SettingsWidgetFdoSecrets::populateDatabases);
|
||||
|
||||
auto service = m_plugin->serviceInstance();
|
||||
if (service) {
|
||||
disconnect(service, &Service::sessionOpened, this, &SettingsWidgetFdoSecrets::addSessionRow);
|
||||
disconnect(service, &Service::sessionClosed, this, &SettingsWidgetFdoSecrets::removeSessionRow);
|
||||
disconnect(service, &Service::collectionCreated, this, &SettingsWidgetFdoSecrets::addDatabaseRow);
|
||||
disconnect(service, &Service::collectionDeleted, this, &SettingsWidgetFdoSecrets::removeDatabaseRow);
|
||||
}
|
||||
}
|
||||
}
|
||||
#include "SettingsWidgetFdoSecrets.moc"
|
||||
|
@ -21,7 +21,9 @@
|
||||
#include <QScopedPointer>
|
||||
#include <QWidget>
|
||||
|
||||
class QTableWidgetItem;
|
||||
class QAbstractItemView;
|
||||
class QItemEditorCreatorBase;
|
||||
class QItemEditorFactory;
|
||||
|
||||
namespace FdoSecrets
|
||||
{
|
||||
@ -48,28 +50,12 @@ public slots:
|
||||
void loadSettings();
|
||||
void saveSettings();
|
||||
|
||||
private slots:
|
||||
void populateSessions(bool enabled);
|
||||
void populateDatabases(bool enabled);
|
||||
void addSessionRow(FdoSecrets::Session* sess);
|
||||
void removeSessionRow(FdoSecrets::Session* sess);
|
||||
void addDatabaseRow(FdoSecrets::Collection* coll);
|
||||
void removeDatabaseRow(FdoSecrets::Collection* coll);
|
||||
|
||||
void updateTables(bool enabled);
|
||||
|
||||
protected:
|
||||
void showEvent(QShowEvent* event) override;
|
||||
|
||||
void hideEvent(QHideEvent* event) override;
|
||||
|
||||
private:
|
||||
QWidget* createManageButtons(FdoSecrets::Collection* coll);
|
||||
|
||||
void updateExposedGroupItem(QTableWidgetItem* item, FdoSecrets::Collection* coll);
|
||||
void setupView(QAbstractItemView* view, int manageColumn, int editorTypeId, QItemEditorCreatorBase* creator);
|
||||
|
||||
private:
|
||||
QScopedPointer<Ui::SettingsWidgetFdoSecrets> m_ui;
|
||||
QScopedPointer<QItemEditorFactory> m_factory;
|
||||
FdoSecretsPlugin* m_plugin;
|
||||
};
|
||||
|
||||
|
@ -75,7 +75,7 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTableWidget" name="tableDatabases">
|
||||
<widget class="QTableView" name="tableDatabases">
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::NoFocus</enum>
|
||||
</property>
|
||||
@ -91,21 +91,6 @@
|
||||
<attribute name="verticalHeaderVisible">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>File Name</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Group</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Manage</string>
|
||||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
@ -123,7 +108,7 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTableWidget" name="tableSessions">
|
||||
<widget class="QTableView" name="tableSessions">
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::NoFocus</enum>
|
||||
</property>
|
||||
@ -139,16 +124,6 @@
|
||||
<attribute name="verticalHeaderVisible">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Application</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Manage</string>
|
||||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
|
@ -83,7 +83,7 @@ void OpVaultReader::fillFromSectionField(Entry* entry, const QString& sectionNam
|
||||
}
|
||||
attrValue = query.toString(QUrl::FullyEncoded);
|
||||
}
|
||||
entry->attributes()->set(Totp::ATTRIBUTE_SETTINGS, attrValue, true);
|
||||
entry->setTotp(Totp::parseSettings(attrValue));
|
||||
} else if (attrName.startsWith("expir", Qt::CaseInsensitive)) {
|
||||
QDateTime expiry;
|
||||
if (kind == "date") {
|
||||
|
@ -51,7 +51,7 @@ void DatabaseOpenDialog::setTargetDatabaseWidget(DatabaseWidget* dbWidget)
|
||||
disconnect(this, nullptr, m_dbWidget, nullptr);
|
||||
}
|
||||
m_dbWidget = dbWidget;
|
||||
connect(this, SIGNAL(dialogFinished(bool)), dbWidget, SLOT(unlockDatabase(bool)));
|
||||
connect(this, &DatabaseOpenDialog::dialogFinished, dbWidget, &DatabaseWidget::unlockDatabase);
|
||||
}
|
||||
|
||||
void DatabaseOpenDialog::setIntent(DatabaseOpenDialog::Intent intent)
|
||||
@ -90,6 +90,6 @@ void DatabaseOpenDialog::complete(bool accepted)
|
||||
} else {
|
||||
reject();
|
||||
}
|
||||
emit dialogFinished(accepted);
|
||||
emit dialogFinished(accepted, m_dbWidget);
|
||||
clearForms();
|
||||
}
|
||||
|
@ -50,7 +50,7 @@ public:
|
||||
void clearForms();
|
||||
|
||||
signals:
|
||||
void dialogFinished(bool);
|
||||
void dialogFinished(bool accepted, DatabaseWidget* dbWidget);
|
||||
|
||||
public slots:
|
||||
void complete(bool accepted);
|
||||
|
@ -204,9 +204,14 @@ void DatabaseOpenWidget::openDatabase()
|
||||
|
||||
m_db.reset(new Database());
|
||||
QString error;
|
||||
|
||||
QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
|
||||
m_ui->passwordFormFrame->setEnabled(false);
|
||||
QCoreApplication::processEvents();
|
||||
bool ok = m_db->open(m_filename, masterKey, &error, false);
|
||||
QApplication::restoreOverrideCursor();
|
||||
m_ui->passwordFormFrame->setEnabled(true);
|
||||
|
||||
if (!ok) {
|
||||
if (m_ui->editPassword->text().isEmpty() && !m_retryUnlockWithEmptyPassword) {
|
||||
QScopedPointer<QMessageBox> msgBox(new QMessageBox(this));
|
||||
@ -370,9 +375,11 @@ void DatabaseOpenWidget::browseKeyFile()
|
||||
QString filename = fileDialog()->getOpenFileName(this, tr("Select key file"), QString(), filters);
|
||||
|
||||
if (QFileInfo(filename).canonicalFilePath() == QFileInfo(m_filename).canonicalFilePath()) {
|
||||
MessageBox::warning(this, tr("Cannot use database file as key file"),
|
||||
tr("You cannot use your database file as a key file.\nIf you do not have a key file, please leave the field empty."),
|
||||
MessageBox::Button::Ok);
|
||||
MessageBox::warning(this,
|
||||
tr("Cannot use database file as key file"),
|
||||
tr("You cannot use your database file as a key file.\nIf you do not have a key file, "
|
||||
"please leave the field empty."),
|
||||
MessageBox::Button::Ok);
|
||||
filename = "";
|
||||
}
|
||||
|
||||
|
@ -132,7 +132,7 @@
|
||||
<number>15</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QFrame" name="verticalFrame">
|
||||
<widget class="QFrame" name="passwordFormFrame">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>400</width>
|
||||
|
@ -63,6 +63,8 @@ DatabaseTabWidget::DatabaseTabWidget(QWidget* parent)
|
||||
connect(autoType(), SIGNAL(globalAutoTypeTriggered()), SLOT(performGlobalAutoType()));
|
||||
connect(autoType(), SIGNAL(autotypePerformed()), SLOT(relockPendingDatabase()));
|
||||
connect(autoType(), SIGNAL(autotypeRejected()), SLOT(relockPendingDatabase()));
|
||||
connect(m_databaseOpenDialog.data(), &DatabaseOpenDialog::dialogFinished,
|
||||
this, &DatabaseTabWidget::databaseUnlockDialogFinished);
|
||||
// clang-format on
|
||||
|
||||
#ifdef Q_OS_MACOS
|
||||
@ -166,7 +168,8 @@ void DatabaseTabWidget::addDatabaseTab(const QString& filePath,
|
||||
for (int i = 0, c = count(); i < c; ++i) {
|
||||
auto* dbWidget = databaseWidgetFromIndex(i);
|
||||
Q_ASSERT(dbWidget);
|
||||
if (dbWidget && dbWidget->database()->canonicalFilePath().compare(canonicalFilePath, FILE_CASE_SENSITIVE) == 0) {
|
||||
if (dbWidget
|
||||
&& dbWidget->database()->canonicalFilePath().compare(canonicalFilePath, FILE_CASE_SENSITIVE) == 0) {
|
||||
dbWidget->performUnlockDatabase(password, keyfile);
|
||||
if (!inBackground) {
|
||||
// switch to existing tab if file is already open
|
||||
|
@ -90,6 +90,7 @@ signals:
|
||||
void tabNameChanged();
|
||||
void messageGlobal(const QString&, MessageWidget::MessageType type);
|
||||
void messageDismissGlobal();
|
||||
void databaseUnlockDialogFinished(bool accepted, DatabaseWidget* dbWidget);
|
||||
|
||||
private slots:
|
||||
void toggleTabbar();
|
||||
|
@ -275,6 +275,11 @@ bool DatabaseWidget::isEntryEditActive() const
|
||||
return currentWidget() == m_editEntryWidget;
|
||||
}
|
||||
|
||||
bool DatabaseWidget::isGroupEditActive() const
|
||||
{
|
||||
return currentWidget() == m_editGroupWidget;
|
||||
}
|
||||
|
||||
bool DatabaseWidget::isEditWidgetModified() const
|
||||
{
|
||||
if (currentWidget() == m_editEntryWidget) {
|
||||
@ -387,6 +392,8 @@ void DatabaseWidget::createEntry()
|
||||
|
||||
void DatabaseWidget::replaceDatabase(QSharedPointer<Database> db)
|
||||
{
|
||||
Q_ASSERT(!isEntryEditActive() && !isGroupEditActive());
|
||||
|
||||
// Save off new parent UUID which will be valid when creating a new entry
|
||||
QUuid newParentUuid;
|
||||
if (m_newParent) {
|
||||
@ -1370,7 +1377,7 @@ bool DatabaseWidget::lock()
|
||||
if (m_db->isModified()) {
|
||||
bool saved = false;
|
||||
// Attempt to save on exit, but don't block locking if it fails
|
||||
if (config()->get("AutoSaveOnExit").toBool()) {
|
||||
if (config()->get("AutoSaveOnExit").toBool() || config()->get("AutoSaveAfterEveryChange").toBool()) {
|
||||
saved = save();
|
||||
}
|
||||
|
||||
@ -1421,7 +1428,8 @@ bool DatabaseWidget::lock()
|
||||
|
||||
void DatabaseWidget::reloadDatabaseFile()
|
||||
{
|
||||
if (!m_db || isLocked()) {
|
||||
// Ignore reload if we are locked or currently editing an entry or group
|
||||
if (!m_db || isLocked() || isEntryEditActive() || isGroupEditActive()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1441,6 +1449,11 @@ void DatabaseWidget::reloadDatabaseFile()
|
||||
}
|
||||
}
|
||||
|
||||
// Lock out interactions
|
||||
m_entryView->setDisabled(true);
|
||||
m_groupView->setDisabled(true);
|
||||
QApplication::processEvents();
|
||||
|
||||
QString error;
|
||||
auto db = QSharedPointer<Database>::create(m_db->filePath());
|
||||
if (db->open(database()->key(), &error)) {
|
||||
@ -1480,6 +1493,10 @@ void DatabaseWidget::reloadDatabaseFile()
|
||||
// Mark db as modified since existing data may differ from file or file was deleted
|
||||
m_db->markAsModified();
|
||||
}
|
||||
|
||||
// Return control
|
||||
m_entryView->setDisabled(false);
|
||||
m_groupView->setDisabled(false);
|
||||
}
|
||||
|
||||
int DatabaseWidget::numberOfSelectedEntries() const
|
||||
@ -1620,11 +1637,20 @@ bool DatabaseWidget::save()
|
||||
m_blockAutoSave = true;
|
||||
++m_saveAttempts;
|
||||
|
||||
// TODO: Make this async, but lock out the database widget to prevent re-entrance
|
||||
// TODO: Make this async
|
||||
// Lock out interactions
|
||||
m_entryView->setDisabled(true);
|
||||
m_groupView->setDisabled(true);
|
||||
QApplication::processEvents();
|
||||
|
||||
bool useAtomicSaves = config()->get("UseAtomicSaves", true).toBool();
|
||||
QString errorMessage;
|
||||
bool ok = m_db->save(&errorMessage, useAtomicSaves, config()->get("BackupBeforeSave").toBool());
|
||||
|
||||
// Return control
|
||||
m_entryView->setDisabled(false);
|
||||
m_groupView->setDisabled(false);
|
||||
|
||||
if (ok) {
|
||||
m_saveAttempts = 0;
|
||||
m_blockAutoSave = false;
|
||||
@ -1744,7 +1770,7 @@ void DatabaseWidget::processAutoOpen()
|
||||
}
|
||||
|
||||
for (const auto* entry : autoopenGroup->entries()) {
|
||||
if (entry->url().isEmpty() || entry->password().isEmpty()) {
|
||||
if (entry->url().isEmpty() || (entry->password().isEmpty() && entry->username().isEmpty())) {
|
||||
continue;
|
||||
}
|
||||
QFileInfo filepath;
|
||||
|
@ -81,6 +81,7 @@ public:
|
||||
bool isLocked() const;
|
||||
bool isSearchActive() const;
|
||||
bool isEntryEditActive() const;
|
||||
bool isGroupEditActive() const;
|
||||
|
||||
QString getCurrentSearch();
|
||||
void refreshSearch();
|
||||
|
@ -36,6 +36,11 @@ QFont Font::fixedFont()
|
||||
fixedFont = consolasFont;
|
||||
}
|
||||
#endif
|
||||
#ifdef Q_OS_MACOS
|
||||
// Qt doesn't choose a monospace font correctly on macOS
|
||||
const QFont defaultFont;
|
||||
fixedFont = QFontDatabase().font("Menlo", defaultFont.styleName(), defaultFont.pointSize());
|
||||
#endif
|
||||
|
||||
return fixedFont;
|
||||
}
|
||||
|
@ -885,8 +885,8 @@ void MainWindow::switchToPasswordGen(bool enabled)
|
||||
if (enabled) {
|
||||
m_ui->passwordGeneratorWidget->loadSettings();
|
||||
m_ui->passwordGeneratorWidget->regeneratePassword();
|
||||
m_ui->passwordGeneratorWidget->setStandaloneMode(true);
|
||||
m_ui->stackedWidget->setCurrentIndex(PasswordGeneratorScreen);
|
||||
m_ui->passwordGeneratorWidget->setStandaloneMode(true);
|
||||
} else {
|
||||
m_ui->passwordGeneratorWidget->saveSettings();
|
||||
switchToDatabases();
|
||||
|
@ -60,7 +60,7 @@ SearchWidget::SearchWidget(QWidget* parent)
|
||||
.arg(QKeySequence(QKeySequence::Find).toString(QKeySequence::NativeText)));
|
||||
m_ui->searchEdit->installEventFilter(this);
|
||||
|
||||
m_searchMenu = new QMenu();
|
||||
m_searchMenu = new QMenu(this);
|
||||
m_actionCaseSensitive = m_searchMenu->addAction(tr("Case sensitive"), this, SLOT(updateCaseSensitive()));
|
||||
m_actionCaseSensitive->setObjectName("actionSearchCaseSensitive");
|
||||
m_actionCaseSensitive->setCheckable(true);
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Weslly Honorato <weslly@protonmail.com>
|
||||
* Copyright (C) 2017 Weslly Honorato <weslly@protonmail.com>
|
||||
* Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
@ -42,7 +42,7 @@ TotpDialog::TotpDialog(QWidget* parent, Entry* entry)
|
||||
resetCounter();
|
||||
updateProgressBar();
|
||||
|
||||
connect(parent, SIGNAL(lockedDatabase()), SLOT(close()));
|
||||
connect(parent, SIGNAL(databaseLocked()), SLOT(close()));
|
||||
connect(&m_totpUpdateTimer, SIGNAL(timeout()), this, SLOT(updateProgressBar()));
|
||||
connect(&m_totpUpdateTimer, SIGNAL(timeout()), this, SLOT(updateSeconds()));
|
||||
m_totpUpdateTimer.start(m_step * 10);
|
||||
|
59
src/gui/URLEdit.cpp
Normal file
59
src/gui/URLEdit.cpp
Normal file
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Felix Geyer <debfx@fobos.de>
|
||||
* Copyright (C) 2019 KeePassXC Team <team@keepassxc.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 or (at your option)
|
||||
* version 3 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "URLEdit.h"
|
||||
|
||||
#include <QRegularExpression>
|
||||
|
||||
#include "core/Config.h"
|
||||
#include "core/FilePath.h"
|
||||
#include "core/Tools.h"
|
||||
#include "gui/Font.h"
|
||||
|
||||
const QColor URLEdit::ErrorColor = QColor(255, 125, 125);
|
||||
|
||||
URLEdit::URLEdit(QWidget* parent)
|
||||
: QLineEdit(parent)
|
||||
{
|
||||
const QIcon errorIcon = filePath()->icon("status", "dialog-error");
|
||||
m_errorAction = addAction(errorIcon, QLineEdit::TrailingPosition);
|
||||
m_errorAction->setVisible(false);
|
||||
m_errorAction->setToolTip(tr("Invalid URL"));
|
||||
|
||||
updateStylesheet();
|
||||
}
|
||||
|
||||
void URLEdit::enableVerifyMode()
|
||||
{
|
||||
updateStylesheet();
|
||||
|
||||
connect(this, SIGNAL(textChanged(QString)), SLOT(updateStylesheet()));
|
||||
}
|
||||
|
||||
void URLEdit::updateStylesheet()
|
||||
{
|
||||
const QString stylesheetTemplate("QLineEdit { background: %1; }");
|
||||
|
||||
if (!Tools::checkUrlValid(text())) {
|
||||
setStyleSheet(stylesheetTemplate.arg(ErrorColor.name()));
|
||||
m_errorAction->setVisible(true);
|
||||
} else {
|
||||
m_errorAction->setVisible(false);
|
||||
setStyleSheet("");
|
||||
}
|
||||
}
|
43
src/gui/URLEdit.h
Normal file
43
src/gui/URLEdit.h
Normal file
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Felix Geyer <debfx@fobos.de>
|
||||
* Copyright (C) 2019 KeePassXC Team <team@keepassxc.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 or (at your option)
|
||||
* version 3 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef KEEPASSX_URLEDIT_H
|
||||
#define KEEPASSX_URLEDIT_H
|
||||
|
||||
#include <QAction>
|
||||
#include <QLineEdit>
|
||||
#include <QPointer>
|
||||
|
||||
class URLEdit : public QLineEdit
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
static const QColor ErrorColor;
|
||||
|
||||
explicit URLEdit(QWidget* parent = nullptr);
|
||||
void enableVerifyMode();
|
||||
|
||||
private slots:
|
||||
void updateStylesheet();
|
||||
|
||||
private:
|
||||
QPointer<QAction> m_errorAction;
|
||||
};
|
||||
|
||||
#endif // KEEPASSX_URLEDIT_H
|
@ -48,4 +48,3 @@ const QSharedPointer<Database> DatabaseSettingsWidget::getDatabase() const
|
||||
{
|
||||
return m_db;
|
||||
}
|
||||
|
||||
|
@ -167,6 +167,7 @@ void EditEntryWidget::setupMain()
|
||||
#ifdef WITH_XC_NETWORKING
|
||||
connect(m_mainUi->fetchFaviconButton, SIGNAL(clicked()), m_iconsWidget, SLOT(downloadFavicon()));
|
||||
connect(m_mainUi->urlEdit, SIGNAL(textChanged(QString)), m_iconsWidget, SLOT(setUrl(QString)));
|
||||
m_mainUi->urlEdit->enableVerifyMode();
|
||||
#endif
|
||||
connect(m_mainUi->expireCheck, SIGNAL(toggled(bool)), m_mainUi->expireDatePicker, SLOT(setEnabled(bool)));
|
||||
connect(m_mainUi->notesEnabled, SIGNAL(toggled(bool)), this, SLOT(toggleHideNotes(bool)));
|
||||
@ -201,7 +202,7 @@ void EditEntryWidget::setupAdvanced()
|
||||
connect(m_advancedUi->editAttributeButton, SIGNAL(clicked()), SLOT(editCurrentAttribute()));
|
||||
connect(m_advancedUi->removeAttributeButton, SIGNAL(clicked()), SLOT(removeCurrentAttribute()));
|
||||
connect(m_advancedUi->protectAttributeButton, SIGNAL(toggled(bool)), SLOT(protectCurrentAttribute(bool)));
|
||||
connect(m_advancedUi->revealAttributeButton, SIGNAL(clicked(bool)), SLOT(revealCurrentAttribute()));
|
||||
connect(m_advancedUi->revealAttributeButton, SIGNAL(clicked(bool)), SLOT(toggleCurrentAttributeVisibility()));
|
||||
connect(m_advancedUi->attributesView->selectionModel(),
|
||||
SIGNAL(currentChanged(QModelIndex,QModelIndex)),
|
||||
SLOT(updateCurrentAttribute()));
|
||||
@ -271,9 +272,14 @@ void EditEntryWidget::setupBrowser()
|
||||
m_additionalURLsDataModel->setEntryAttributes(m_entryAttributes);
|
||||
m_browserUi->additionalURLsView->setModel(m_additionalURLsDataModel);
|
||||
|
||||
// Use a custom item delegate to align the icon to the right side
|
||||
auto iconDelegate = new URLModelIconDelegate(m_browserUi->additionalURLsView);
|
||||
m_browserUi->additionalURLsView->setItemDelegate(iconDelegate);
|
||||
|
||||
// clang-format off
|
||||
connect(m_browserUi->skipAutoSubmitCheckbox, SIGNAL(toggled(bool)), SLOT(updateBrowserModified()));
|
||||
connect(m_browserUi->hideEntryCheckbox, SIGNAL(toggled(bool)), SLOT(updateBrowserModified()));
|
||||
connect(m_browserUi->onlyHttpAuthCheckbox, SIGNAL(toggled(bool)), SLOT(updateBrowserModified()));
|
||||
connect(m_browserUi->addURLButton, SIGNAL(clicked()), SLOT(insertURL()));
|
||||
connect(m_browserUi->removeURLButton, SIGNAL(clicked()), SLOT(removeCurrentURL()));
|
||||
connect(m_browserUi->editURLButton, SIGNAL(clicked()), SLOT(editCurrentURL()));
|
||||
@ -300,8 +306,10 @@ void EditEntryWidget::updateBrowser()
|
||||
|
||||
auto skip = m_browserUi->skipAutoSubmitCheckbox->isChecked();
|
||||
auto hide = m_browserUi->hideEntryCheckbox->isChecked();
|
||||
m_customData->set(BrowserService::OPTION_SKIP_AUTO_SUBMIT, (skip ? QString("true") : QString("false")));
|
||||
m_customData->set(BrowserService::OPTION_HIDE_ENTRY, (hide ? QString("true") : QString("false")));
|
||||
auto onlyHttpAuth = m_browserUi->onlyHttpAuthCheckbox->isChecked();
|
||||
m_customData->set(BrowserService::OPTION_SKIP_AUTO_SUBMIT, (skip ? TRUE_STR : FALSE_STR));
|
||||
m_customData->set(BrowserService::OPTION_HIDE_ENTRY, (hide ? TRUE_STR : FALSE_STR));
|
||||
m_customData->set(BrowserService::OPTION_ONLY_HTTP_AUTH, (onlyHttpAuth ? TRUE_STR : FALSE_STR));
|
||||
}
|
||||
|
||||
void EditEntryWidget::insertURL()
|
||||
@ -465,6 +473,7 @@ void EditEntryWidget::setupEntryUpdate()
|
||||
if (config()->get("Browser/Enabled", false).toBool()) {
|
||||
connect(m_browserUi->skipAutoSubmitCheckbox, SIGNAL(toggled(bool)), SLOT(setModified()));
|
||||
connect(m_browserUi->hideEntryCheckbox, SIGNAL(toggled(bool)), SLOT(setModified()));
|
||||
connect(m_browserUi->onlyHttpAuthCheckbox, SIGNAL(toggled(bool)), SLOT(setModified()));
|
||||
connect(m_browserUi->addURLButton, SIGNAL(toggled(bool)), SLOT(setModified()));
|
||||
connect(m_browserUi->removeURLButton, SIGNAL(toggled(bool)), SLOT(setModified()));
|
||||
connect(m_browserUi->editURLButton, SIGNAL(toggled(bool)), SLOT(setModified()));
|
||||
@ -959,18 +968,25 @@ void EditEntryWidget::setForms(Entry* entry, bool restore)
|
||||
#ifdef WITH_XC_BROWSER
|
||||
if (m_customData->contains(BrowserService::OPTION_SKIP_AUTO_SUBMIT)) {
|
||||
// clang-format off
|
||||
m_browserUi->skipAutoSubmitCheckbox->setChecked(m_customData->value(BrowserService::OPTION_SKIP_AUTO_SUBMIT) == "true");
|
||||
m_browserUi->skipAutoSubmitCheckbox->setChecked(m_customData->value(BrowserService::OPTION_SKIP_AUTO_SUBMIT) == TRUE_STR);
|
||||
// clang-format on
|
||||
} else {
|
||||
m_browserUi->skipAutoSubmitCheckbox->setChecked(false);
|
||||
}
|
||||
|
||||
if (m_customData->contains(BrowserService::OPTION_HIDE_ENTRY)) {
|
||||
m_browserUi->hideEntryCheckbox->setChecked(m_customData->value(BrowserService::OPTION_HIDE_ENTRY) == "true");
|
||||
m_browserUi->hideEntryCheckbox->setChecked(m_customData->value(BrowserService::OPTION_HIDE_ENTRY) == TRUE_STR);
|
||||
} else {
|
||||
m_browserUi->hideEntryCheckbox->setChecked(false);
|
||||
}
|
||||
|
||||
if (m_customData->contains(BrowserService::OPTION_ONLY_HTTP_AUTH)) {
|
||||
m_browserUi->onlyHttpAuthCheckbox->setChecked(m_customData->value(BrowserService::OPTION_ONLY_HTTP_AUTH)
|
||||
== TRUE_STR);
|
||||
} else {
|
||||
m_browserUi->onlyHttpAuthCheckbox->setChecked(false);
|
||||
}
|
||||
|
||||
m_browserUi->addURLButton->setEnabled(!m_history);
|
||||
m_browserUi->removeURLButton->setEnabled(false);
|
||||
m_browserUi->editURLButton->setEnabled(false);
|
||||
@ -1297,6 +1313,7 @@ void EditEntryWidget::displayAttribute(QModelIndex index, bool showProtected)
|
||||
// Block signals to prevent modified being set
|
||||
m_advancedUi->protectAttributeButton->blockSignals(true);
|
||||
m_advancedUi->attributesEdit->blockSignals(true);
|
||||
m_advancedUi->revealAttributeButton->setText(tr("Reveal"));
|
||||
|
||||
if (index.isValid()) {
|
||||
QString key = m_attributesModel->keyByIndex(index);
|
||||
@ -1348,7 +1365,7 @@ void EditEntryWidget::protectCurrentAttribute(bool state)
|
||||
}
|
||||
}
|
||||
|
||||
void EditEntryWidget::revealCurrentAttribute()
|
||||
void EditEntryWidget::toggleCurrentAttributeVisibility()
|
||||
{
|
||||
if (!m_advancedUi->attributesEdit->isEnabled()) {
|
||||
QModelIndex index = m_advancedUi->attributesView->currentIndex();
|
||||
@ -1359,6 +1376,10 @@ void EditEntryWidget::revealCurrentAttribute()
|
||||
m_advancedUi->attributesEdit->setEnabled(true);
|
||||
m_advancedUi->attributesEdit->blockSignals(oldBlockSignals);
|
||||
}
|
||||
m_advancedUi->revealAttributeButton->setText(tr("Hide"));
|
||||
} else {
|
||||
protectCurrentAttribute(true);
|
||||
m_advancedUi->revealAttributeButton->setText(tr("Reveal"));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -93,7 +93,7 @@ private slots:
|
||||
void removeCurrentAttribute();
|
||||
void updateCurrentAttribute();
|
||||
void protectCurrentAttribute(bool state);
|
||||
void revealCurrentAttribute();
|
||||
void toggleCurrentAttributeVisibility();
|
||||
void updateAutoTypeEnabled();
|
||||
void openAutotypeHelp();
|
||||
void insertAutoTypeAssoc();
|
||||
|
@ -50,6 +50,16 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="onlyHttpAuthCheckbox">
|
||||
<property name="toolTip">
|
||||
<string>Only send this setting to the browser for HTTP Auth dialogs. If enabled, normal login forms will not show this entry for selection.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Use this entry only with HTTP Basic Auth</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
@ -130,6 +140,7 @@
|
||||
<tabstops>
|
||||
<tabstop>skipAutoSubmitCheckbox</tabstop>
|
||||
<tabstop>hideEntryCheckbox</tabstop>
|
||||
<tabstop>onlyHttpAuthCheckbox</tabstop>
|
||||
<tabstop>additionalURLsView</tabstop>
|
||||
<tabstop>addURLButton</tabstop>
|
||||
<tabstop>removeURLButton</tabstop>
|
||||
|
@ -30,7 +30,7 @@
|
||||
<item row="5" column="1">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_6">
|
||||
<item>
|
||||
<widget class="QLineEdit" name="urlEdit">
|
||||
<widget class="URLEdit" name="urlEdit">
|
||||
<property name="accessibleName">
|
||||
<string>Url field</string>
|
||||
</property>
|
||||
@ -256,6 +256,12 @@
|
||||
<header>gui/PasswordEdit.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>URLEdit</class>
|
||||
<extends>QLineEdit</extends>
|
||||
<header>gui/URLEdit.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<tabstops>
|
||||
<tabstop>titleEdit</tabstop>
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include "EntryURLModel.h"
|
||||
|
||||
#include "core/Entry.h"
|
||||
#include "core/FilePath.h"
|
||||
#include "core/Tools.h"
|
||||
|
||||
#include <algorithm>
|
||||
@ -26,6 +27,7 @@
|
||||
EntryURLModel::EntryURLModel(QObject* parent)
|
||||
: QStandardItemModel(parent)
|
||||
, m_entryAttributes(nullptr)
|
||||
, m_errorIcon(filePath()->icon("status", "dialog-error"))
|
||||
{
|
||||
}
|
||||
|
||||
@ -53,6 +55,33 @@ void EntryURLModel::setEntryAttributes(EntryAttributes* entryAttributes)
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
QVariant EntryURLModel::data(const QModelIndex& index, int role) const
|
||||
{
|
||||
if (!index.isValid()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const auto key = keyByIndex(index);
|
||||
if (key.isEmpty()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const auto value = m_entryAttributes->value(key);
|
||||
const auto urlValid = Tools::checkUrlValid(value);
|
||||
|
||||
if (role == Qt::BackgroundRole && !urlValid) {
|
||||
return QColor(255, 125, 125);
|
||||
} else if (role == Qt::DecorationRole && !urlValid) {
|
||||
return m_errorIcon;
|
||||
} else if (role == Qt::DisplayRole || role == Qt::EditRole) {
|
||||
return value;
|
||||
} else if (role == Qt::ToolTipRole && !urlValid) {
|
||||
return tr("Invalid URL");
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
bool EntryURLModel::setData(const QModelIndex& index, const QVariant& value, int role)
|
||||
{
|
||||
if (!index.isValid() || role != Qt::EditRole || value.type() != QVariant::String || value.toString().isEmpty()) {
|
||||
|
@ -20,9 +20,23 @@
|
||||
#define KEEPASSXC_ENTRYURLMODEL_H
|
||||
|
||||
#include <QStandardItemModel>
|
||||
#include <QStyledItemDelegate>
|
||||
|
||||
class EntryAttributes;
|
||||
|
||||
class URLModelIconDelegate : public QStyledItemDelegate
|
||||
{
|
||||
public:
|
||||
using QStyledItemDelegate::QStyledItemDelegate;
|
||||
|
||||
protected:
|
||||
void initStyleOption(QStyleOptionViewItem* option, const QModelIndex& index) const override
|
||||
{
|
||||
QStyledItemDelegate::initStyleOption(option, index);
|
||||
option->decorationPosition = QStyleOptionViewItem::Right;
|
||||
}
|
||||
};
|
||||
|
||||
class EntryURLModel : public QStandardItemModel
|
||||
{
|
||||
Q_OBJECT
|
||||
@ -32,6 +46,7 @@ public:
|
||||
void setEntryAttributes(EntryAttributes* entryAttributes);
|
||||
void insertRow(const QString& key, const QString& value);
|
||||
bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override;
|
||||
QVariant data(const QModelIndex& index, int role) const override;
|
||||
QModelIndex indexByKey(const QString& key) const;
|
||||
QString keyByIndex(const QModelIndex& index) const;
|
||||
|
||||
@ -41,6 +56,7 @@ private slots:
|
||||
private:
|
||||
QList<QPair<QString, QString>> m_urls;
|
||||
EntryAttributes* m_entryAttributes;
|
||||
QIcon m_errorIcon;
|
||||
};
|
||||
|
||||
#endif // KEEPASSXC_ENTRYURLMODEL_H
|
||||
|
@ -20,11 +20,6 @@
|
||||
|
||||
#import <AppKit/NSWorkspace.h>
|
||||
#import <CoreVideo/CVPixelBuffer.h>
|
||||
#import <Availability.h>
|
||||
|
||||
#if __MAC_OS_X_VERSION_MAX_ALLOWED < 101200
|
||||
static const NSEventMask NSEventMaskKeyDown = NSKeyDownMask;
|
||||
#endif
|
||||
|
||||
@implementation AppKitImpl
|
||||
|
||||
|
@ -32,7 +32,6 @@ class KeyFileEditWidget : public KeyComponentWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
|
||||
public:
|
||||
explicit KeyFileEditWidget(DatabaseSettingsWidget* parent);
|
||||
Q_DISABLE_COPY(KeyFileEditWidget);
|
||||
|
@ -95,8 +95,6 @@ void PasswordEditWidget::initComponentEditWidget(QWidget* widget)
|
||||
|
||||
void PasswordEditWidget::hideEvent(QHideEvent* event)
|
||||
{
|
||||
Q_ASSERT(m_compUi->enterPasswordEdit);
|
||||
|
||||
if (!isVisible() && m_compUi->enterPasswordEdit) {
|
||||
m_compUi->enterPasswordEdit->setText("");
|
||||
m_compUi->repeatPasswordEdit->setText("");
|
||||
|
@ -105,8 +105,10 @@ void ElidedLabel::updateElidedText()
|
||||
const QFontMetrics metrix(font());
|
||||
displayText = metrix.elidedText(m_rawText, m_elideMode, width() - 2);
|
||||
}
|
||||
setText(m_url.isEmpty() ? displayText : htmlLinkTemplate.arg(m_url, displayText));
|
||||
setOpenExternalLinks(!m_url.isEmpty());
|
||||
|
||||
bool hasUrl = !m_url.isEmpty();
|
||||
setText(hasUrl ? htmlLinkTemplate.arg(m_url.toHtmlEscaped(), displayText) : displayText);
|
||||
setOpenExternalLinks(!hasUrl);
|
||||
}
|
||||
|
||||
void ElidedLabel::resizeEvent(QResizeEvent* event)
|
||||
|
@ -164,7 +164,7 @@ endif()
|
||||
|
||||
if(WITH_XC_CRYPTO_SSH)
|
||||
add_unit_test(NAME testopensshkey SOURCES TestOpenSSHKey.cpp
|
||||
LIBS ${TEST_LIBRARIES})
|
||||
LIBS ${TEST_LIBRARIES})
|
||||
endif()
|
||||
|
||||
add_unit_test(NAME testentry SOURCES TestEntry.cpp
|
||||
@ -192,7 +192,7 @@ add_unit_test(NAME testcsvparser SOURCES TestCsvParser.cpp
|
||||
LIBS ${TEST_LIBRARIES})
|
||||
|
||||
add_unit_test(NAME testrandomgenerator SOURCES TestRandomGenerator.cpp
|
||||
LIBS testsupport ${TEST_LIBRARIES})
|
||||
LIBS testsupport ${TEST_LIBRARIES})
|
||||
|
||||
add_unit_test(NAME testentrysearcher SOURCES TestEntrySearcher.cpp
|
||||
LIBS ${TEST_LIBRARIES})
|
||||
@ -206,7 +206,7 @@ add_unit_test(NAME testykchallengeresponsekey
|
||||
|
||||
if(WITH_XC_KEESHARE)
|
||||
add_unit_test(NAME testsharing SOURCES TestSharing.cpp
|
||||
LIBS testsupport ${TEST_LIBRARIES})
|
||||
LIBS testsupport ${TEST_LIBRARIES})
|
||||
endif()
|
||||
|
||||
add_unit_test(NAME testdatabase SOURCES TestDatabase.cpp
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include "TestBrowser.h"
|
||||
#include "TestGlobal.h"
|
||||
#include "browser/BrowserSettings.h"
|
||||
#include "core/Tools.h"
|
||||
#include "crypto/Crypto.h"
|
||||
#include "sodium/crypto_box.h"
|
||||
#include <QString>
|
||||
@ -56,7 +57,7 @@ void TestBrowser::testChangePublicKeys()
|
||||
auto response = m_browserAction->handleAction(json);
|
||||
QCOMPARE(response["action"].toString(), QString("change-public-keys"));
|
||||
QCOMPARE(response["publicKey"].toString() == PUBLICKEY, false);
|
||||
QCOMPARE(response["success"].toString(), QString("true"));
|
||||
QCOMPARE(response["success"].toString(), TRUE_STR);
|
||||
}
|
||||
|
||||
void TestBrowser::testEncryptMessage()
|
||||
@ -179,29 +180,22 @@ void TestBrowser::testSearchEntries()
|
||||
auto db = QSharedPointer<Database>::create();
|
||||
auto* root = db->rootGroup();
|
||||
|
||||
QList<QString> urls;
|
||||
urls.push_back("https://github.com/login_page");
|
||||
urls.push_back("https://github.com/login");
|
||||
urls.push_back("https://github.com/");
|
||||
urls.push_back("github.com/login");
|
||||
urls.push_back("http://github.com");
|
||||
urls.push_back("http://github.com/login");
|
||||
urls.push_back("github.com");
|
||||
urls.push_back("github.com/login");
|
||||
urls.push_back("https://github"); // Invalid URL
|
||||
urls.push_back("github.com");
|
||||
QStringList urls = {"https://github.com/login_page",
|
||||
"https://github.com/login",
|
||||
"https://github.com/",
|
||||
"github.com/login",
|
||||
"http://github.com",
|
||||
"http://github.com/login",
|
||||
"github.com",
|
||||
"github.com/login",
|
||||
"https://github", // Invalid URL
|
||||
"github.com"};
|
||||
|
||||
for (int i = 0; i < urls.length(); ++i) {
|
||||
auto entry = new Entry();
|
||||
entry->setGroup(root);
|
||||
entry->beginUpdate();
|
||||
entry->setUrl(urls[i]);
|
||||
entry->setUsername(QString("User %1").arg(i));
|
||||
entry->endUpdate();
|
||||
}
|
||||
createEntries(urls, root);
|
||||
|
||||
browserSettings()->setMatchUrlScheme(false);
|
||||
auto result = m_browserService->searchEntries(db, "github.com", "https://github.com"); // db, hostname, url
|
||||
auto result =
|
||||
m_browserService->searchEntries(db, "https://github.com", "https://github.com/session"); // db, url, submitUrl
|
||||
|
||||
QCOMPARE(result.length(), 9);
|
||||
QCOMPARE(result[0]->url(), QString("https://github.com/login_page"));
|
||||
@ -213,7 +207,7 @@ void TestBrowser::testSearchEntries()
|
||||
|
||||
// With matching there should be only 3 results + 4 without a scheme
|
||||
browserSettings()->setMatchUrlScheme(true);
|
||||
result = m_browserService->searchEntries(db, "github.com", "https://github.com"); // db, hostname, url
|
||||
result = m_browserService->searchEntries(db, "https://github.com", "https://github.com/session");
|
||||
QCOMPARE(result.length(), 7);
|
||||
QCOMPARE(result[0]->url(), QString("https://github.com/login_page"));
|
||||
QCOMPARE(result[1]->url(), QString("https://github.com/login"));
|
||||
@ -226,22 +220,13 @@ void TestBrowser::testSearchEntriesWithPort()
|
||||
auto db = QSharedPointer<Database>::create();
|
||||
auto* root = db->rootGroup();
|
||||
|
||||
QList<QString> urls;
|
||||
urls.push_back("http://127.0.0.1:443");
|
||||
urls.push_back("http://127.0.0.1:80");
|
||||
QStringList urls = {"http://127.0.0.1:443", "http://127.0.0.1:80"};
|
||||
|
||||
for (int i = 0; i < urls.length(); ++i) {
|
||||
auto entry = new Entry();
|
||||
entry->setGroup(root);
|
||||
entry->beginUpdate();
|
||||
entry->setUrl(urls[i]);
|
||||
entry->setUsername(QString("User %1").arg(i));
|
||||
entry->endUpdate();
|
||||
}
|
||||
createEntries(urls, root);
|
||||
|
||||
auto result = m_browserService->searchEntries(db, "127.0.0.1", "http://127.0.0.1:443"); // db, hostname, url
|
||||
auto result = m_browserService->searchEntries(db, "http://127.0.0.1:443", "http://127.0.0.1");
|
||||
QCOMPARE(result.length(), 1);
|
||||
QCOMPARE(result[0]->url(), urls[0]);
|
||||
QCOMPARE(result[0]->url(), QString("http://127.0.0.1:443"));
|
||||
}
|
||||
|
||||
void TestBrowser::testSearchEntriesWithAdditionalURLs()
|
||||
@ -249,70 +234,55 @@ void TestBrowser::testSearchEntriesWithAdditionalURLs()
|
||||
auto db = QSharedPointer<Database>::create();
|
||||
auto* root = db->rootGroup();
|
||||
|
||||
QList<Entry*> entries;
|
||||
QList<QString> urls;
|
||||
urls.push_back("https://github.com/");
|
||||
urls.push_back("https://www.example.com");
|
||||
urls.push_back("http://domain.com");
|
||||
QStringList urls = {"https://github.com/", "https://www.example.com", "http://domain.com"};
|
||||
|
||||
for (int i = 0; i < urls.length(); ++i) {
|
||||
auto entry = new Entry();
|
||||
entry->setGroup(root);
|
||||
entry->beginUpdate();
|
||||
entry->setUrl(urls[i]);
|
||||
entry->setUsername(QString("User %1").arg(i));
|
||||
entry->endUpdate();
|
||||
entries.push_back(entry);
|
||||
}
|
||||
auto entries = createEntries(urls, root);
|
||||
|
||||
// Add an additional URL to the first entry
|
||||
entries.first()->attributes()->set(BrowserService::ADDITIONAL_URL, "https://keepassxc.org");
|
||||
|
||||
auto result = m_browserService->searchEntries(db, "github.com", "https://github.com"); // db, hostname, url
|
||||
auto result = m_browserService->searchEntries(db, "https://github.com", "https://github.com/session");
|
||||
QCOMPARE(result.length(), 1);
|
||||
QCOMPARE(result[0]->url(), urls[0]);
|
||||
QCOMPARE(result[0]->url(), QString("https://github.com/"));
|
||||
|
||||
// Search the additional URL. It should return the same entry
|
||||
auto additionalResult = m_browserService->searchEntries(db, "keepassxc.org", "https://keepassxc.org");
|
||||
auto additionalResult = m_browserService->searchEntries(db, "https://keepassxc.org", "https://keepassxc.org");
|
||||
QCOMPARE(additionalResult.length(), 1);
|
||||
QCOMPARE(additionalResult[0]->url(), urls[0]);
|
||||
QCOMPARE(additionalResult[0]->url(), QString("https://github.com/"));
|
||||
}
|
||||
|
||||
void TestBrowser::testInvalidEntries()
|
||||
{
|
||||
auto db = QSharedPointer<Database>::create();
|
||||
auto* root = db->rootGroup();
|
||||
const QString url("https://github.com");
|
||||
const QString submitUrl("https://github.com/session");
|
||||
|
||||
QList<QString> urls;
|
||||
urls.push_back("https://github.com/login");
|
||||
urls.push_back("https:///github.com/"); // Extra '/'
|
||||
urls.push_back("http://github.com/**//*");
|
||||
urls.push_back("http://*.github.com/login");
|
||||
urls.push_back("//github.com"); // fromUserInput() corrects this one.
|
||||
urls.push_back("github.com/{}<>");
|
||||
QStringList urls = {
|
||||
"https://github.com/login",
|
||||
"https:///github.com/", // Extra '/'
|
||||
"http://github.com/**//*",
|
||||
"http://*.github.com/login",
|
||||
"//github.com", // fromUserInput() corrects this one.
|
||||
"github.com/{}<>",
|
||||
"http:/example.com",
|
||||
};
|
||||
|
||||
for (int i = 0; i < urls.length(); ++i) {
|
||||
auto entry = new Entry();
|
||||
entry->setGroup(root);
|
||||
entry->beginUpdate();
|
||||
entry->setUrl(urls[i]);
|
||||
entry->setUsername(QString("User %1").arg(i));
|
||||
entry->endUpdate();
|
||||
}
|
||||
createEntries(urls, root);
|
||||
|
||||
browserSettings()->setMatchUrlScheme(true);
|
||||
auto result = m_browserService->searchEntries(db, "github.com", "https://github.com"); // db, hostname, url
|
||||
auto result = m_browserService->searchEntries(db, "https://github.com", "https://github.com/session");
|
||||
QCOMPARE(result.length(), 2);
|
||||
QCOMPARE(result[0]->url(), QString("https://github.com/login"));
|
||||
QCOMPARE(result[1]->url(), QString("//github.com"));
|
||||
|
||||
// Test the URL's directly
|
||||
QCOMPARE(m_browserService->handleURL(urls[0], "github.com", "https://github.com"), true);
|
||||
QCOMPARE(m_browserService->handleURL(urls[1], "github.com", "https://github.com"), false);
|
||||
QCOMPARE(m_browserService->handleURL(urls[2], "github.com", "https://github.com"), false);
|
||||
QCOMPARE(m_browserService->handleURL(urls[3], "github.com", "https://github.com"), false);
|
||||
QCOMPARE(m_browserService->handleURL(urls[4], "github.com", "https://github.com"), true);
|
||||
QCOMPARE(m_browserService->handleURL(urls[5], "github.com", "https://github.com"), false);
|
||||
QCOMPARE(m_browserService->handleURL(urls[0], url, submitUrl), true);
|
||||
QCOMPARE(m_browserService->handleURL(urls[1], url, submitUrl), false);
|
||||
QCOMPARE(m_browserService->handleURL(urls[2], url, submitUrl), false);
|
||||
QCOMPARE(m_browserService->handleURL(urls[3], url, submitUrl), false);
|
||||
QCOMPARE(m_browserService->handleURL(urls[4], url, submitUrl), true);
|
||||
QCOMPARE(m_browserService->handleURL(urls[5], url, submitUrl), false);
|
||||
}
|
||||
|
||||
void TestBrowser::testSubdomainsAndPaths()
|
||||
@ -320,44 +290,74 @@ void TestBrowser::testSubdomainsAndPaths()
|
||||
auto db = QSharedPointer<Database>::create();
|
||||
auto* root = db->rootGroup();
|
||||
|
||||
QList<QString> urls;
|
||||
urls.push_back("https://www.github.com/login/page.xml");
|
||||
urls.push_back("https://login.github.com/");
|
||||
urls.push_back("https://github.com");
|
||||
urls.push_back("http://www.github.com");
|
||||
urls.push_back("http://login.github.com/pathtonowhere");
|
||||
urls.push_back(".github.com"); // Invalid URL
|
||||
urls.push_back("www.github.com/");
|
||||
urls.push_back("https://github"); // Invalid URL
|
||||
QStringList urls = {
|
||||
"https://www.github.com/login/page.xml",
|
||||
"https://login.github.com/",
|
||||
"https://github.com",
|
||||
"http://www.github.com",
|
||||
"http://login.github.com/pathtonowhere",
|
||||
".github.com", // Invalid URL
|
||||
"www.github.com/",
|
||||
"https://github" // Invalid URL
|
||||
};
|
||||
|
||||
for (int i = 0; i < urls.length(); ++i) {
|
||||
auto entry = new Entry();
|
||||
entry->setGroup(root);
|
||||
entry->beginUpdate();
|
||||
entry->setUrl(urls[i]);
|
||||
entry->setUsername(QString("User %1").arg(i));
|
||||
entry->endUpdate();
|
||||
}
|
||||
createEntries(urls, root);
|
||||
|
||||
browserSettings()->setMatchUrlScheme(false);
|
||||
auto result = m_browserService->searchEntries(db, "github.com", "https://github.com"); // db, hostname, url
|
||||
auto result = m_browserService->searchEntries(db, "https://github.com", "https://github.com/session");
|
||||
QCOMPARE(result.length(), 1);
|
||||
QCOMPARE(result[0]->url(), QString("https://github.com"));
|
||||
|
||||
QCOMPARE(result.length(), 6);
|
||||
QCOMPARE(result[0]->url(), urls[0]);
|
||||
QCOMPARE(result[1]->url(), urls[1]);
|
||||
QCOMPARE(result[2]->url(), urls[2]);
|
||||
QCOMPARE(result[3]->url(), urls[3]);
|
||||
QCOMPARE(result[4]->url(), urls[4]);
|
||||
QCOMPARE(result[5]->url(), urls[6]);
|
||||
|
||||
// With matching there should be only 3 results
|
||||
browserSettings()->setMatchUrlScheme(true);
|
||||
result = m_browserService->searchEntries(db, "github.com", "https://github.com"); // db, hostname, url
|
||||
// With www subdomain
|
||||
result = m_browserService->searchEntries(db, "https://www.github.com", "https://www.github.com/session");
|
||||
QCOMPARE(result.length(), 4);
|
||||
QCOMPARE(result[0]->url(), urls[0]);
|
||||
QCOMPARE(result[1]->url(), urls[1]);
|
||||
QCOMPARE(result[2]->url(), urls[2]);
|
||||
QCOMPARE(result[3]->url(), urls[6]);
|
||||
QCOMPARE(result[0]->url(), QString("https://www.github.com/login/page.xml"));
|
||||
QCOMPARE(result[1]->url(), QString("https://github.com")); // Accepts any subdomain
|
||||
QCOMPARE(result[2]->url(), QString("http://www.github.com"));
|
||||
QCOMPARE(result[3]->url(), QString("www.github.com/"));
|
||||
|
||||
// With scheme matching there should be only 1 result
|
||||
browserSettings()->setMatchUrlScheme(true);
|
||||
result = m_browserService->searchEntries(db, "https://github.com", "https://github.com/session");
|
||||
QCOMPARE(result.length(), 1);
|
||||
QCOMPARE(result[0]->url(), QString("https://github.com"));
|
||||
|
||||
// Test site with subdomain in the site URL
|
||||
QStringList entryURLs = {
|
||||
"https://accounts.example.com",
|
||||
"https://accounts.example.com/path",
|
||||
"https://subdomain.example.com/",
|
||||
"https://another.accounts.example.com/",
|
||||
"https://another.subdomain.example.com/",
|
||||
"https://example.com/",
|
||||
"https://example" // Invalid URL
|
||||
};
|
||||
|
||||
createEntries(entryURLs, root);
|
||||
|
||||
result = m_browserService->searchEntries(db, "https://accounts.example.com", "https://accounts.example.com");
|
||||
QCOMPARE(result.length(), 3);
|
||||
QCOMPARE(result[0]->url(), QString("https://accounts.example.com"));
|
||||
QCOMPARE(result[1]->url(), QString("https://accounts.example.com/path"));
|
||||
QCOMPARE(result[2]->url(), QString("https://example.com/")); // Accepts any subdomain
|
||||
|
||||
result = m_browserService->searchEntries(
|
||||
db, "https://another.accounts.example.com", "https://another.accounts.example.com");
|
||||
QCOMPARE(result.length(), 4);
|
||||
QCOMPARE(result[0]->url(),
|
||||
QString("https://accounts.example.com")); // Accepts any subdomain under accounts.example.com
|
||||
QCOMPARE(result[1]->url(), QString("https://accounts.example.com/path"));
|
||||
QCOMPARE(result[2]->url(), QString("https://another.accounts.example.com/"));
|
||||
QCOMPARE(result[3]->url(), QString("https://example.com/")); // Accepts one or more subdomains
|
||||
|
||||
// Test local files. It should be a direct match.
|
||||
QStringList localFiles = {"file:///Users/testUser/tests/test.html"};
|
||||
|
||||
createEntries(localFiles, root);
|
||||
|
||||
// With local files, url is always set to the file scheme + ://. Submit URL holds the actual URL.
|
||||
result = m_browserService->searchEntries(db, "file://", "file:///Users/testUser/tests/test.html");
|
||||
QCOMPARE(result.length(), 1);
|
||||
}
|
||||
|
||||
void TestBrowser::testSortEntries()
|
||||
@ -365,28 +365,18 @@ void TestBrowser::testSortEntries()
|
||||
auto db = QSharedPointer<Database>::create();
|
||||
auto* root = db->rootGroup();
|
||||
|
||||
QList<QString> urls;
|
||||
urls.push_back("https://github.com/login_page");
|
||||
urls.push_back("https://github.com/login");
|
||||
urls.push_back("https://github.com/");
|
||||
urls.push_back("github.com/login");
|
||||
urls.push_back("http://github.com");
|
||||
urls.push_back("http://github.com/login");
|
||||
urls.push_back("github.com");
|
||||
urls.push_back("github.com/login");
|
||||
urls.push_back("https://github"); // Invalid URL
|
||||
urls.push_back("github.com");
|
||||
QStringList urls = {"https://github.com/login_page",
|
||||
"https://github.com/login",
|
||||
"https://github.com/",
|
||||
"github.com/login",
|
||||
"http://github.com",
|
||||
"http://github.com/login",
|
||||
"github.com",
|
||||
"github.com/login",
|
||||
"https://github", // Invalid URL
|
||||
"github.com"};
|
||||
|
||||
QList<Entry*> entries;
|
||||
for (int i = 0; i < urls.length(); ++i) {
|
||||
auto entry = new Entry();
|
||||
entry->setGroup(root);
|
||||
entry->beginUpdate();
|
||||
entry->setUrl(urls[i]);
|
||||
entry->setUsername(QString("User %1").arg(i));
|
||||
entry->endUpdate();
|
||||
entries.push_back(entry);
|
||||
}
|
||||
auto entries = createEntries(urls, root);
|
||||
|
||||
browserSettings()->setBestMatchOnly(false);
|
||||
auto result =
|
||||
@ -400,7 +390,6 @@ void TestBrowser::testSortEntries()
|
||||
QCOMPARE(result[2]->url(), QString("https://github.com/login"));
|
||||
QCOMPARE(result[3]->username(), QString("User 3"));
|
||||
QCOMPARE(result[3]->url(), QString("github.com/login"));
|
||||
|
||||
}
|
||||
|
||||
void TestBrowser::testGetDatabaseGroups()
|
||||
@ -458,3 +447,38 @@ void TestBrowser::testGetDatabaseGroups()
|
||||
auto lastChild = lastChildren.at(0);
|
||||
QCOMPARE(lastChild.toObject()["name"].toString(), QString("group2_1_1"));
|
||||
}
|
||||
|
||||
QList<Entry*> TestBrowser::createEntries(QStringList& urls, Group* root) const
|
||||
{
|
||||
QList<Entry*> entries;
|
||||
for (int i = 0; i < urls.length(); ++i) {
|
||||
auto entry = new Entry();
|
||||
entry->setGroup(root);
|
||||
entry->beginUpdate();
|
||||
entry->setUrl(urls[i]);
|
||||
entry->setUsername(QString("User %1").arg(i));
|
||||
entry->endUpdate();
|
||||
entries.push_back(entry);
|
||||
}
|
||||
|
||||
return entries;
|
||||
}
|
||||
void TestBrowser::testValidURLs()
|
||||
{
|
||||
QHash<QString, bool> urls;
|
||||
urls["https://github.com/login"] = true;
|
||||
urls["https:///github.com/"] = false;
|
||||
urls["http://github.com/**//*"] = false;
|
||||
urls["http://*.github.com/login"] = false;
|
||||
urls["//github.com"] = true;
|
||||
urls["github.com/{}<>"] = false;
|
||||
urls["http:/example.com"] = false;
|
||||
urls["cmd://C:/Toolchains/msys2/usr/bin/mintty \"ssh jon@192.168.0.1:22\""] = true;
|
||||
urls["file:///Users/testUser/Code/test.html"] = true;
|
||||
|
||||
QHashIterator<QString, bool> i(urls);
|
||||
while (i.hasNext()) {
|
||||
i.next();
|
||||
QCOMPARE(Tools::checkUrlValid(i.key()), i.value());
|
||||
}
|
||||
}
|
||||
|
@ -47,8 +47,11 @@ private slots:
|
||||
void testSubdomainsAndPaths();
|
||||
void testSortEntries();
|
||||
void testGetDatabaseGroups();
|
||||
void testValidURLs();
|
||||
|
||||
private:
|
||||
QList<Entry*> createEntries(QStringList& urls, Group* root) const;
|
||||
|
||||
QScopedPointer<BrowserAction> m_browserAction;
|
||||
QScopedPointer<BrowserService> m_browserService;
|
||||
};
|
||||
|
@ -23,13 +23,13 @@
|
||||
#include "core/Global.h"
|
||||
#include "core/Tools.h"
|
||||
#include "crypto/Crypto.h"
|
||||
#include "keys/drivers/YubiKey.h"
|
||||
#include "format/Kdbx3Reader.h"
|
||||
#include "format/Kdbx3Writer.h"
|
||||
#include "format/Kdbx4Reader.h"
|
||||
#include "format/Kdbx4Writer.h"
|
||||
#include "format/KdbxXmlReader.h"
|
||||
#include "format/KeePass2.h"
|
||||
#include "keys/drivers/YubiKey.h"
|
||||
|
||||
#include "cli/Add.h"
|
||||
#include "cli/AddGroup.h"
|
||||
@ -1422,6 +1422,107 @@ void TestCli::testMerge()
|
||||
QCOMPARE(m_stdoutFile->readAll(), QByteArray(""));
|
||||
}
|
||||
|
||||
void TestCli::testMergeWithKeys()
|
||||
{
|
||||
Create createCmd;
|
||||
QVERIFY(!createCmd.name.isEmpty());
|
||||
QVERIFY(createCmd.getDescriptionLine().contains(createCmd.name));
|
||||
|
||||
Merge mergeCmd;
|
||||
QVERIFY(!mergeCmd.name.isEmpty());
|
||||
QVERIFY(mergeCmd.getDescriptionLine().contains(mergeCmd.name));
|
||||
|
||||
Kdbx4Writer writer;
|
||||
Kdbx4Reader reader;
|
||||
|
||||
QScopedPointer<QTemporaryDir> testDir(new QTemporaryDir());
|
||||
|
||||
QString sourceDatabaseFilename = testDir->path() + "/testSourceDatabase.kdbx";
|
||||
QString sourceKeyfilePath = testDir->path() + "/testSourceKeyfile.txt";
|
||||
|
||||
QString targetDatabaseFilename = testDir->path() + "/testTargetDatabase.kdbx";
|
||||
QString targetKeyfilePath = testDir->path() + "/testTargetKeyfile.txt";
|
||||
|
||||
qint64 pos = m_stdoutFile->pos();
|
||||
|
||||
Utils::Test::setNextPassword("a");
|
||||
createCmd.execute({"create", sourceDatabaseFilename, "-k", sourceKeyfilePath});
|
||||
|
||||
Utils::Test::setNextPassword("b");
|
||||
createCmd.execute({"create", targetDatabaseFilename, "-k", targetKeyfilePath});
|
||||
|
||||
Utils::Test::setNextPassword("a");
|
||||
auto sourceDatabase = QSharedPointer<Database>(
|
||||
Utils::unlockDatabase(sourceDatabaseFilename, true, sourceKeyfilePath, "", Utils::STDOUT));
|
||||
QVERIFY(sourceDatabase);
|
||||
|
||||
Utils::Test::setNextPassword("b");
|
||||
auto targetDatabase = QSharedPointer<Database>(
|
||||
Utils::unlockDatabase(targetDatabaseFilename, true, targetKeyfilePath, "", Utils::STDOUT));
|
||||
QVERIFY(targetDatabase);
|
||||
|
||||
auto* rootGroup = new Group();
|
||||
rootGroup->setName("root");
|
||||
rootGroup->setUuid(QUuid::createUuid());
|
||||
auto* group = new Group();
|
||||
group->setUuid(QUuid::createUuid());
|
||||
group->setParent(rootGroup);
|
||||
group->setName("Internet");
|
||||
|
||||
auto* entry = new Entry();
|
||||
entry->setUuid(QUuid::createUuid());
|
||||
entry->setTitle("Some Website");
|
||||
entry->setPassword("secretsecretsecret");
|
||||
group->addEntry(entry);
|
||||
|
||||
sourceDatabase->setRootGroup(rootGroup);
|
||||
|
||||
auto* otherRootGroup = new Group();
|
||||
otherRootGroup->setName("root");
|
||||
otherRootGroup->setUuid(QUuid::createUuid());
|
||||
auto* otherGroup = new Group();
|
||||
otherGroup->setUuid(QUuid::createUuid());
|
||||
otherGroup->setParent(otherRootGroup);
|
||||
otherGroup->setName("Internet");
|
||||
|
||||
auto* otherEntry = new Entry();
|
||||
otherEntry->setUuid(QUuid::createUuid());
|
||||
otherEntry->setTitle("Some Website 2");
|
||||
otherEntry->setPassword("secretsecretsecret 2");
|
||||
otherGroup->addEntry(otherEntry);
|
||||
|
||||
targetDatabase->setRootGroup(otherRootGroup);
|
||||
|
||||
QFile sourceDatabaseFile(sourceDatabaseFilename);
|
||||
sourceDatabaseFile.open(QIODevice::WriteOnly);
|
||||
QVERIFY(writer.writeDatabase(&sourceDatabaseFile, sourceDatabase.data()));
|
||||
sourceDatabaseFile.flush();
|
||||
sourceDatabaseFile.close();
|
||||
|
||||
QFile targetDatabaseFile(targetDatabaseFilename);
|
||||
targetDatabaseFile.open(QIODevice::WriteOnly);
|
||||
QVERIFY(writer.writeDatabase(&targetDatabaseFile, targetDatabase.data()));
|
||||
targetDatabaseFile.flush();
|
||||
targetDatabaseFile.close();
|
||||
|
||||
pos = m_stdoutFile->pos();
|
||||
Utils::Test::setNextPassword("b");
|
||||
Utils::Test::setNextPassword("a");
|
||||
mergeCmd.execute({"merge",
|
||||
"-k",
|
||||
targetKeyfilePath,
|
||||
"--key-file-from",
|
||||
sourceKeyfilePath,
|
||||
targetDatabaseFile.fileName(),
|
||||
sourceDatabaseFile.fileName()});
|
||||
|
||||
m_stdoutFile->seek(pos);
|
||||
QList<QByteArray> lines = m_stdoutFile->readAll().split('\n');
|
||||
QVERIFY(lines.contains(QString("Successfully merged %1 into %2.")
|
||||
.arg(sourceDatabaseFile.fileName(), targetDatabaseFile.fileName())
|
||||
.toUtf8()));
|
||||
}
|
||||
|
||||
void TestCli::testMove()
|
||||
{
|
||||
Move moveCmd;
|
||||
|
@ -66,6 +66,7 @@ private slots:
|
||||
void testList();
|
||||
void testLocate();
|
||||
void testMerge();
|
||||
void testMergeWithKeys();
|
||||
void testMove();
|
||||
void testOpen();
|
||||
void testRemove();
|
||||
|
@ -21,9 +21,9 @@
|
||||
|
||||
#include "core/EntrySearcher.h"
|
||||
#include "fdosecrets/GcryptMPI.h"
|
||||
#include "fdosecrets/objects/SessionCipher.h"
|
||||
#include "fdosecrets/objects/Collection.h"
|
||||
#include "fdosecrets/objects/Item.h"
|
||||
#include "fdosecrets/objects/SessionCipher.h"
|
||||
|
||||
#include "crypto/Crypto.h"
|
||||
|
||||
@ -96,8 +96,8 @@ void TestFdoSecrets::testDhIetf1024Sha256Aes128CbcPkcs7()
|
||||
|
||||
void TestFdoSecrets::testCrazyAttributeKey()
|
||||
{
|
||||
using FdoSecrets::Item;
|
||||
using FdoSecrets::Collection;
|
||||
using FdoSecrets::Item;
|
||||
|
||||
const QScopedPointer<Group> root(new Group());
|
||||
const QScopedPointer<Entry> e1(new Entry());
|
||||
@ -112,3 +112,35 @@ void TestFdoSecrets::testCrazyAttributeKey()
|
||||
const auto res = EntrySearcher().search({term}, root.data());
|
||||
QCOMPARE(res.count(), 1);
|
||||
}
|
||||
|
||||
void TestFdoSecrets::testSpecialCharsInAttributeValue()
|
||||
{
|
||||
using FdoSecrets::Collection;
|
||||
using FdoSecrets::Item;
|
||||
|
||||
const QScopedPointer<Group> root(new Group());
|
||||
QScopedPointer<Entry> e1(new Entry());
|
||||
e1->setGroup(root.data());
|
||||
|
||||
e1->setTitle("titleA");
|
||||
e1->attributes()->set("testAttribute", "OAuth::[test.name@gmail.com]");
|
||||
|
||||
QScopedPointer<Entry> e2(new Entry());
|
||||
e2->setGroup(root.data());
|
||||
e2->setTitle("titleB");
|
||||
e2->attributes()->set("testAttribute", "Abc:*+.-");
|
||||
|
||||
// search for custom entries via programatic API
|
||||
{
|
||||
const auto term = Collection::attributeToTerm("testAttribute", "OAuth::[test.name@gmail.com]");
|
||||
const auto res = EntrySearcher().search({term}, root.data());
|
||||
QCOMPARE(res.count(), 1);
|
||||
QCOMPARE(res[0]->title(), QStringLiteral("titleA"));
|
||||
}
|
||||
{
|
||||
const auto term = Collection::attributeToTerm("testAttribute", "Abc:*+.-");
|
||||
const auto res = EntrySearcher().search({term}, root.data());
|
||||
QCOMPARE(res.count(), 1);
|
||||
QCOMPARE(res[0]->title(), QStringLiteral("titleB"));
|
||||
}
|
||||
}
|
||||
|
@ -31,6 +31,7 @@ private slots:
|
||||
void testGcryptMPI();
|
||||
void testDhIetf1024Sha256Aes128CbcPkcs7();
|
||||
void testCrazyAttributeKey();
|
||||
void testSpecialCharsInAttributeValue();
|
||||
};
|
||||
|
||||
#endif // KEEPASSXC_TESTFDOSECRETS_H
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include "TestGlobal.h"
|
||||
#include "mock/MockClock.h"
|
||||
|
||||
#include <QScopedPointer>
|
||||
#include <QSignalSpy>
|
||||
|
||||
#include "core/Metadata.h"
|
||||
@ -798,16 +799,16 @@ void TestGroup::testAddEntryWithPath()
|
||||
|
||||
void TestGroup::testIsRecycled()
|
||||
{
|
||||
Database* db = new Database();
|
||||
db->metadata()->setRecycleBinEnabled(true);
|
||||
Database db;
|
||||
db.metadata()->setRecycleBinEnabled(true);
|
||||
|
||||
Group* group1 = new Group();
|
||||
group1->setName("group1");
|
||||
group1->setParent(db->rootGroup());
|
||||
group1->setParent(db.rootGroup());
|
||||
|
||||
Group* group2 = new Group();
|
||||
group2->setName("group2");
|
||||
group2->setParent(db->rootGroup());
|
||||
group2->setParent(db.rootGroup());
|
||||
|
||||
Group* group3 = new Group();
|
||||
group3->setName("group3");
|
||||
@ -815,16 +816,16 @@ void TestGroup::testIsRecycled()
|
||||
|
||||
Group* group4 = new Group();
|
||||
group4->setName("group4");
|
||||
group4->setParent(db->rootGroup());
|
||||
group4->setParent(db.rootGroup());
|
||||
|
||||
db->recycleGroup(group2);
|
||||
db.recycleGroup(group2);
|
||||
|
||||
QVERIFY(!group1->isRecycled());
|
||||
QVERIFY(group2->isRecycled());
|
||||
QVERIFY(group3->isRecycled());
|
||||
QVERIFY(!group4->isRecycled());
|
||||
|
||||
db->recycleGroup(group4);
|
||||
db.recycleGroup(group4);
|
||||
QVERIFY(group4->isRecycled());
|
||||
}
|
||||
|
||||
@ -1052,12 +1053,12 @@ void TestGroup::testChildrenSort()
|
||||
|
||||
void TestGroup::testHierarchy()
|
||||
{
|
||||
Group* group1 = new Group();
|
||||
group1->setName("group1");
|
||||
Group group1;
|
||||
group1.setName("group1");
|
||||
|
||||
Group* group2 = new Group();
|
||||
group2->setName("group2");
|
||||
group2->setParent(group1);
|
||||
group2->setParent(&group1);
|
||||
|
||||
Group* group3 = new Group();
|
||||
group3->setName("group3");
|
||||
@ -1085,11 +1086,11 @@ void TestGroup::testHierarchy()
|
||||
void TestGroup::testApplyGroupIconRecursively()
|
||||
{
|
||||
// Create a database with two nested groups with one entry each
|
||||
Database* database = new Database();
|
||||
Database database;
|
||||
|
||||
Group* subgroup = new Group();
|
||||
subgroup->setName("Subgroup");
|
||||
subgroup->setParent(database->rootGroup());
|
||||
subgroup->setParent(database.rootGroup());
|
||||
QVERIFY(subgroup);
|
||||
|
||||
Group* subsubgroup = new Group();
|
||||
@ -1108,10 +1109,10 @@ void TestGroup::testApplyGroupIconRecursively()
|
||||
// Set an icon per number to the root group and apply recursively
|
||||
// -> all groups and entries have the same icon
|
||||
const int rootIconNumber = 42;
|
||||
database->rootGroup()->setIcon(rootIconNumber);
|
||||
QVERIFY(database->rootGroup()->iconNumber() == rootIconNumber);
|
||||
database->rootGroup()->applyGroupIconToChildGroups();
|
||||
database->rootGroup()->applyGroupIconToChildEntries();
|
||||
database.rootGroup()->setIcon(rootIconNumber);
|
||||
QVERIFY(database.rootGroup()->iconNumber() == rootIconNumber);
|
||||
database.rootGroup()->applyGroupIconToChildGroups();
|
||||
database.rootGroup()->applyGroupIconToChildEntries();
|
||||
QVERIFY(subgroup->iconNumber() == rootIconNumber);
|
||||
QVERIFY(subgroupEntry->iconNumber() == rootIconNumber);
|
||||
QVERIFY(subsubgroup->iconNumber() == rootIconNumber);
|
||||
@ -1124,7 +1125,7 @@ void TestGroup::testApplyGroupIconRecursively()
|
||||
QVERIFY(subsubgroup->iconNumber() == subsubgroupIconNumber);
|
||||
subsubgroup->applyGroupIconToChildGroups();
|
||||
subsubgroup->applyGroupIconToChildEntries();
|
||||
QVERIFY(database->rootGroup()->iconNumber() == rootIconNumber);
|
||||
QVERIFY(database.rootGroup()->iconNumber() == rootIconNumber);
|
||||
QVERIFY(subgroup->iconNumber() == rootIconNumber);
|
||||
QVERIFY(subgroupEntry->iconNumber() == rootIconNumber);
|
||||
QVERIFY(subsubgroup->iconNumber() == subsubgroupIconNumber);
|
||||
@ -1135,11 +1136,11 @@ void TestGroup::testApplyGroupIconRecursively()
|
||||
const QUuid subgroupIconUuid = QUuid::createUuid();
|
||||
QImage subgroupIcon(16, 16, QImage::Format_RGB32);
|
||||
subgroupIcon.setPixel(0, 0, qRgb(255, 0, 0));
|
||||
database->metadata()->addCustomIcon(subgroupIconUuid, subgroupIcon);
|
||||
database.metadata()->addCustomIcon(subgroupIconUuid, subgroupIcon);
|
||||
subgroup->setIcon(subgroupIconUuid);
|
||||
subgroup->applyGroupIconToChildGroups();
|
||||
subgroup->applyGroupIconToChildEntries();
|
||||
QVERIFY(database->rootGroup()->iconNumber() == rootIconNumber);
|
||||
QVERIFY(database.rootGroup()->iconNumber() == rootIconNumber);
|
||||
QCOMPARE(subgroup->iconUuid(), subgroupIconUuid);
|
||||
QCOMPARE(subgroup->icon(), subgroupIcon);
|
||||
QCOMPARE(subgroupEntry->iconUuid(), subgroupIconUuid);
|
||||
@ -1150,10 +1151,10 @@ void TestGroup::testApplyGroupIconRecursively()
|
||||
QCOMPARE(subsubgroupEntry->icon(), subgroupIcon);
|
||||
|
||||
// Reset all icons to root icon
|
||||
database->rootGroup()->setIcon(rootIconNumber);
|
||||
QVERIFY(database->rootGroup()->iconNumber() == rootIconNumber);
|
||||
database->rootGroup()->applyGroupIconToChildGroups();
|
||||
database->rootGroup()->applyGroupIconToChildEntries();
|
||||
database.rootGroup()->setIcon(rootIconNumber);
|
||||
QVERIFY(database.rootGroup()->iconNumber() == rootIconNumber);
|
||||
database.rootGroup()->applyGroupIconToChildGroups();
|
||||
database.rootGroup()->applyGroupIconToChildEntries();
|
||||
QVERIFY(subgroup->iconNumber() == rootIconNumber);
|
||||
QVERIFY(subgroupEntry->iconNumber() == rootIconNumber);
|
||||
QVERIFY(subsubgroup->iconNumber() == rootIconNumber);
|
||||
@ -1161,10 +1162,10 @@ void TestGroup::testApplyGroupIconRecursively()
|
||||
|
||||
// Apply only for child groups
|
||||
const int iconForGroups = 10;
|
||||
database->rootGroup()->setIcon(iconForGroups);
|
||||
QVERIFY(database->rootGroup()->iconNumber() == iconForGroups);
|
||||
database->rootGroup()->applyGroupIconToChildGroups();
|
||||
QVERIFY(database->rootGroup()->iconNumber() == iconForGroups);
|
||||
database.rootGroup()->setIcon(iconForGroups);
|
||||
QVERIFY(database.rootGroup()->iconNumber() == iconForGroups);
|
||||
database.rootGroup()->applyGroupIconToChildGroups();
|
||||
QVERIFY(database.rootGroup()->iconNumber() == iconForGroups);
|
||||
QVERIFY(subgroup->iconNumber() == iconForGroups);
|
||||
QVERIFY(subgroupEntry->iconNumber() == rootIconNumber);
|
||||
QVERIFY(subsubgroup->iconNumber() == iconForGroups);
|
||||
@ -1172,10 +1173,10 @@ void TestGroup::testApplyGroupIconRecursively()
|
||||
|
||||
// Apply only for child entries
|
||||
const int iconForEntries = 20;
|
||||
database->rootGroup()->setIcon(iconForEntries);
|
||||
QVERIFY(database->rootGroup()->iconNumber() == iconForEntries);
|
||||
database->rootGroup()->applyGroupIconToChildEntries();
|
||||
QVERIFY(database->rootGroup()->iconNumber() == iconForEntries);
|
||||
database.rootGroup()->setIcon(iconForEntries);
|
||||
QVERIFY(database.rootGroup()->iconNumber() == iconForEntries);
|
||||
database.rootGroup()->applyGroupIconToChildEntries();
|
||||
QVERIFY(database.rootGroup()->iconNumber() == iconForEntries);
|
||||
QVERIFY(subgroup->iconNumber() == iconForGroups);
|
||||
QVERIFY(subgroupEntry->iconNumber() == iconForEntries);
|
||||
QVERIFY(subsubgroup->iconNumber() == iconForGroups);
|
||||
@ -1184,15 +1185,15 @@ void TestGroup::testApplyGroupIconRecursively()
|
||||
|
||||
void TestGroup::testUsernamesRecursive()
|
||||
{
|
||||
Database* database = new Database();
|
||||
Database database;
|
||||
|
||||
// Create a subgroup
|
||||
Group* subgroup = new Group();
|
||||
subgroup->setName("Subgroup");
|
||||
subgroup->setParent(database->rootGroup());
|
||||
subgroup->setParent(database.rootGroup());
|
||||
|
||||
// Generate entries in the root group and the subgroup
|
||||
Entry* rootGroupEntry = database->rootGroup()->addEntryWithPath("Root group entry");
|
||||
Entry* rootGroupEntry = database.rootGroup()->addEntryWithPath("Root group entry");
|
||||
rootGroupEntry->setUsername("Name1");
|
||||
|
||||
Entry* subgroupEntry = subgroup->addEntryWithPath("Subgroup entry");
|
||||
@ -1201,7 +1202,7 @@ void TestGroup::testUsernamesRecursive()
|
||||
Entry* subgroupEntryReusingUsername = subgroup->addEntryWithPath("Another subgroup entry");
|
||||
subgroupEntryReusingUsername->setUsername("Name2");
|
||||
|
||||
QList<QString> usernames = database->rootGroup()->usernamesRecursive();
|
||||
QList<QString> usernames = database.rootGroup()->usernamesRecursive();
|
||||
QCOMPARE(usernames.size(), 2);
|
||||
QVERIFY(usernames.contains("Name1"));
|
||||
QVERIFY(usernames.contains("Name2"));
|
||||
|
@ -49,24 +49,24 @@ QPair<QString, QString>* split1PTextExportKV(QByteArray& line)
|
||||
return new QPair<QString, QString>(k, v);
|
||||
}
|
||||
|
||||
QJsonArray* read1PasswordTextExport(QFile& f)
|
||||
QSharedPointer<QJsonArray> read1PasswordTextExport(QFile& f)
|
||||
{
|
||||
auto result = new QJsonArray;
|
||||
auto current = new QJsonObject;
|
||||
|
||||
if (!f.open(QIODevice::ReadOnly)) {
|
||||
qCritical("Unable to open your text export file for reading");
|
||||
return nullptr;
|
||||
return {};
|
||||
}
|
||||
|
||||
auto result = QSharedPointer<QJsonArray>::create();
|
||||
QJsonObject current;
|
||||
|
||||
while (!f.atEnd()) {
|
||||
auto line = f.readLine(1024);
|
||||
|
||||
if (line.size() == 1 and line[0] == '\n') {
|
||||
if (!current->isEmpty()) {
|
||||
result->append(*current);
|
||||
if (!current.isEmpty()) {
|
||||
result->append(current);
|
||||
}
|
||||
current = new QJsonObject;
|
||||
current = QJsonObject();
|
||||
continue;
|
||||
}
|
||||
const auto kv = split1PTextExportKV(line);
|
||||
@ -95,14 +95,14 @@ QJsonArray* read1PasswordTextExport(QFile& f)
|
||||
}
|
||||
}
|
||||
auto v = lines.join("");
|
||||
(*current)[k] = v;
|
||||
current[k] = v;
|
||||
} else {
|
||||
(*current)[k] = kv->second;
|
||||
current[k] = kv->second;
|
||||
}
|
||||
delete kv;
|
||||
}
|
||||
if (!current->isEmpty()) {
|
||||
result->append(*current);
|
||||
if (!current.isEmpty()) {
|
||||
result->append(current);
|
||||
}
|
||||
f.close();
|
||||
|
||||
@ -120,10 +120,9 @@ void TestOpVaultReader::initTestCase()
|
||||
m_password = "freddy";
|
||||
|
||||
QFile testData(m_opVaultTextExportPath);
|
||||
QJsonArray* data = read1PasswordTextExport(testData);
|
||||
auto data = read1PasswordTextExport(testData);
|
||||
QVERIFY(data);
|
||||
QCOMPARE(data->size(), 27);
|
||||
delete data;
|
||||
|
||||
m_categoryMap.insert("001", "Login");
|
||||
m_categoryMap.insert("002", "Credit Card");
|
||||
@ -149,9 +148,9 @@ void TestOpVaultReader::testReadIntoDatabase()
|
||||
{
|
||||
QDir opVaultDir(m_opVaultPath);
|
||||
|
||||
auto reader = new OpVaultReader();
|
||||
auto db = reader->readDatabase(opVaultDir, m_password);
|
||||
QVERIFY2(!reader->hasError(), qPrintable(reader->errorString()));
|
||||
OpVaultReader reader;
|
||||
QScopedPointer<Database> db(reader.readDatabase(opVaultDir, m_password));
|
||||
QVERIFY2(!reader.hasError(), qPrintable(reader.errorString()));
|
||||
QVERIFY(db);
|
||||
QVERIFY(!db->children().isEmpty());
|
||||
|
||||
@ -179,7 +178,6 @@ void TestOpVaultReader::testReadIntoDatabase()
|
||||
QUuid u = Tools::hexToUuid(value["uuid"].toString());
|
||||
objectsByUuid[u] = value;
|
||||
}
|
||||
delete testData;
|
||||
QCOMPARE(objectsByUuid.size(), 27);
|
||||
|
||||
for (QUuid u : objectsByUuid.keys()) {
|
||||
@ -240,11 +238,11 @@ void TestOpVaultReader::testKeyDerivation()
|
||||
|
||||
void TestOpVaultReader::testBandEntry1()
|
||||
{
|
||||
auto reader = new OpVaultReader();
|
||||
OpVaultReader reader;
|
||||
QByteArray json(R"({"hello": "world"})");
|
||||
QJsonDocument doc = QJsonDocument::fromJson(json);
|
||||
QJsonObject data;
|
||||
QByteArray entryKey;
|
||||
QByteArray entryHmacKey;
|
||||
QVERIFY(!reader->decryptBandEntry(doc.object(), data, entryKey, entryHmacKey));
|
||||
QVERIFY(!reader.decryptBandEntry(doc.object(), data, entryKey, entryHmacKey));
|
||||
}
|
||||
|
@ -79,6 +79,10 @@ static QString dbFileName = QStringLiteral(KEEPASSX_TEST_DATA_DIR).append("/NewD
|
||||
|
||||
void TestGui::initTestCase()
|
||||
{
|
||||
Application::setApplicationName("KeePassXC");
|
||||
Application::setApplicationVersion(KEEPASSXC_VERSION);
|
||||
QApplication::setQuitOnLastWindowClosed(false);
|
||||
|
||||
QVERIFY(Crypto::init());
|
||||
Config::createTempFileInstance();
|
||||
// Disable autosave so we can test the modified file indicator
|
||||
@ -91,11 +95,12 @@ void TestGui::initTestCase()
|
||||
// Disable the update check first time alert
|
||||
config()->set("UpdateCheckMessageShown", true);
|
||||
|
||||
m_mainWindow.reset(new MainWindow());
|
||||
Bootstrap::restoreMainWindowState(*m_mainWindow);
|
||||
Bootstrap::bootstrapApplication();
|
||||
|
||||
m_mainWindow.reset(new MainWindow());
|
||||
m_tabWidget = m_mainWindow->findChild<DatabaseTabWidget*>("tabWidget");
|
||||
m_mainWindow->show();
|
||||
m_mainWindow->resize(1024, 768);
|
||||
}
|
||||
|
||||
// Every test starts with opening the temp database
|
||||
@ -176,7 +181,7 @@ void TestGui::testSettingsDefaultTabOrder()
|
||||
|
||||
void TestGui::testCreateDatabase()
|
||||
{
|
||||
QTimer::singleShot(0, this, SLOT(createDatabaseCallback()));
|
||||
QTimer::singleShot(50, this, SLOT(createDatabaseCallback()));
|
||||
triggerAction("actionDatabaseNew");
|
||||
|
||||
// there is a new empty db
|
||||
@ -1435,8 +1440,9 @@ int TestGui::addCannedEntries()
|
||||
|
||||
void TestGui::checkDatabase(QString dbFileName)
|
||||
{
|
||||
if (dbFileName.isEmpty())
|
||||
if (dbFileName.isEmpty()) {
|
||||
dbFileName = m_dbFilePath;
|
||||
}
|
||||
|
||||
auto key = QSharedPointer<CompositeKey>::create();
|
||||
key->addKey(QSharedPointer<PasswordKey>::create("a"));
|
||||
|
@ -71,7 +71,7 @@ bool TemporaryFile::copyFromFile(const QString& otherFileName)
|
||||
}
|
||||
|
||||
QByteArray data;
|
||||
while(!(data = otherFile.read(1024)).isEmpty()) {
|
||||
while (!(data = otherFile.read(1024)).isEmpty()) {
|
||||
write(data);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user