mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2024-12-28 08:49:42 -05:00
Release 2.3.2
- Enable high entropy ASLR on Windows [#1747] - Enhance favicon fetching [#1786] - Fix crash on Windows due to autotype [#1691] - Fix dark tray icon changing all icons [#1680] - Fix --pw-stdin not using getPassword function [#1686] - Fix placeholders being resolved in notes [#1907] - Enable auto-type start delay to be configurable [#1908] - Browser: Fix native messaging reply size [#1719] - Browser: Increase maximum buffer size [#1720] - Browser: Enhance usability and functionality [#1810, #1822, #1830, #1884, #1906] - SSH Agent: Parse aes-256-cbc/ctr keys [#1682] - SSH Agent: Enhance usability and functionality [#1677, #1679, #1681, #1787]
This commit is contained in:
commit
2bb72eba02
16
CHANGELOG
16
CHANGELOG
@ -1,3 +1,19 @@
|
||||
2.3.2 (2018-05-07)
|
||||
=========================
|
||||
|
||||
- Enable high entropy ASLR on Windows [#1747]
|
||||
- Enhance favicon fetching [#1786]
|
||||
- Fix crash on Windows due to autotype [#1691]
|
||||
- Fix dark tray icon changing all icons [#1680]
|
||||
- Fix --pw-stdin not using getPassword function [#1686]
|
||||
- Fix placeholders being resolved in notes [#1907]
|
||||
- Enable auto-type start delay to be configurable [#1908]
|
||||
- Browser: Fix native messaging reply size [#1719]
|
||||
- Browser: Increase maximum buffer size [#1720]
|
||||
- Browser: Enhance usability and functionality [#1810, #1822, #1830, #1884, #1906]
|
||||
- SSH Agent: Parse aes-256-cbc/ctr keys [#1682]
|
||||
- SSH Agent: Enhance usability and functionality [#1677, #1679, #1681, #1787]
|
||||
|
||||
2.3.1 (2018-03-06)
|
||||
=========================
|
||||
|
||||
|
@ -70,7 +70,7 @@ set(CMAKE_AUTOUIC ON)
|
||||
|
||||
set(KEEPASSXC_VERSION_MAJOR "2")
|
||||
set(KEEPASSXC_VERSION_MINOR "3")
|
||||
set(KEEPASSXC_VERSION_PATCH "1")
|
||||
set(KEEPASSXC_VERSION_PATCH "2")
|
||||
set(KEEPASSXC_VERSION "${KEEPASSXC_VERSION_MAJOR}.${KEEPASSXC_VERSION_MINOR}.${KEEPASSXC_VERSION_PATCH}")
|
||||
|
||||
set(KEEPASSXC_BUILD_TYPE "Snapshot" CACHE STRING "Set KeePassXC build type to distinguish between stable releases and snapshots")
|
||||
@ -245,8 +245,8 @@ if(MINGW)
|
||||
set(CMAKE_RC_COMPILE_OBJECT "<CMAKE_RC_COMPILER> <FLAGS> -O coff <DEFINES> -i <SOURCE> -o <OBJECT>")
|
||||
if(NOT (CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo"))
|
||||
# Enable DEP and ASLR
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--nxcompat -Wl,--dynamicbase")
|
||||
set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,--nxcompat -Wl,--dynamicbase")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--nxcompat -Wl,--dynamicbase -Wl,--high-entropy-va")
|
||||
set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,--nxcompat -Wl,--dynamicbase -Wl,--high-entropy-va")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
@ -33,7 +33,7 @@ so please check out your distribution's package list to see if KeePassXC is avai
|
||||
[Mozilla Firefox](https://addons.mozilla.org/en-US/firefox/addon/keepasshttp-connector/) and
|
||||
[Google Chrome or Chromium](https://chrome.google.com/webstore/detail/keepasshttp-connector/dafgdjggglmmknipkhngniifhplpcldb), and
|
||||
[passafari](https://github.com/mmichaa/passafari.safariextension/) in Safari. [[See note about KeePassHTTP]](#Note_about_KeePassHTTP)
|
||||
- Browser integration with KeePassXC-Browser using [native messaging](https://developer.chrome.com/extensions/nativeMessaging) for [Mozilla Firefox](https://addons.mozilla.org/en-US/firefox/addon/keepassxc-browser/) and [Google Chrome or Chromium](https://chrome.google.com/webstore/detail/keepassxc-browser/iopaggbpplllidnfmcghoonnokmjoicf)
|
||||
- Browser integration with KeePassXC-Browser using [native messaging](https://developer.chrome.com/extensions/nativeMessaging) for [Mozilla Firefox](https://addons.mozilla.org/en-US/firefox/addon/keepassxc-browser/) and [Google Chrome or Chromium](https://chrome.google.com/webstore/detail/keepassxc-browser/oboonakemofpalcgghocfoadofidjkkk)
|
||||
- Many bug fixes
|
||||
|
||||
For a full list of features and changes, read the [CHANGELOG](CHANGELOG) document.
|
||||
|
@ -50,6 +50,24 @@
|
||||
</screenshots>
|
||||
|
||||
<releases>
|
||||
<release version="2.3.2" date="2018-05-07">
|
||||
<description>
|
||||
<ul>
|
||||
<li>Enable high entropy ASLR on Windows [#1747]</li>
|
||||
<li>Enhance favicon fetching [#1786]</li>
|
||||
<li>Fix crash on Windows due to autotype [#1691]</li>
|
||||
<li>Fix dark tray icon changing all icons [#1680]</li>
|
||||
<li>Fix --pw-stdin not using getPassword function [#1686]</li>
|
||||
<li>Fix placeholders being resolved in notes [#1907]</li>
|
||||
<li>Enable auto-type start delay to be configurable [#1908]</li>
|
||||
<li>Browser: Fix native messaging reply size [#1719]</li>
|
||||
<li>Browser: Increase maximum buffer size [#1720]</li>
|
||||
<li>Browser: Enhance usability and functionality [#1810, #1822, #1830, #1884, #1906]</li>
|
||||
<li>SSH Agent: Parse aes-256-cbc/ctr keys [#1682]</li>
|
||||
<li>SSH Agent: Enhance usability and functionality [#1677, #1679, #1681, #1787]</li>
|
||||
</ul>
|
||||
</description>
|
||||
</release>
|
||||
<release version="2.3.1" date="2018-03-06">
|
||||
<description>
|
||||
<ul>
|
||||
|
4102
share/translations/keepassx_bn.ts
Normal file
4102
share/translations/keepassx_bn.ts
Normal file
File diff suppressed because it is too large
Load Diff
@ -73,12 +73,13 @@ Nucli: %3 %4</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Special thanks from the KeePassXC team go to debfx for creating the original KeePassX.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Agraïments de l'equip de KeePassXC a debfx per crear el KeePassX original.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Build Type: %1
|
||||
</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Tipus de construcció: %1
|
||||
</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@ -110,7 +111,7 @@ Seleccioneu si voleu permetre l'accés.</translation>
|
||||
<name>AgentSettingsWidget</name>
|
||||
<message>
|
||||
<source>Enable SSH Agent (requires restart)</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Habilita l'agent SSH (requereix reiniciar-se)</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@ -129,7 +130,7 @@ Seleccioneu si voleu permetre l'accés.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>The Syntax of your Auto-Type statement is incorrect!</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>La sintaxi de l'Auto-Type no és correcte!</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>This Auto-Type command contains a very long delay. Do you really want to proceed?</source>
|
||||
@ -193,7 +194,7 @@ Seleccioneu si voleu permetre l'accés.</translation>
|
||||
<name>BrowserAccessControlDialog</name>
|
||||
<message>
|
||||
<source>KeePassXC-Browser Confirm Access</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Confirma l'accés al navegador KeePassXC</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Remember this decision</source>
|
||||
@ -222,11 +223,11 @@ Seleccioneu si voleu permetre l'accés.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>This is required for accessing your databases with KeePassXC-Browser</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Requerit per l'accés a les teues bases de dades amb el navegador KeePassXC</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Enable KeepassXC browser integration</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Habilita la integració de KeePassXC amb el navegador</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>General</source>
|
||||
@ -234,7 +235,7 @@ Seleccioneu si voleu permetre l'accés.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Enable integration for these browsers:</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Habilita la integració amb els següents navegadors:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>&Google Chrome</source>
|
||||
@ -255,7 +256,7 @@ Seleccioneu si voleu permetre l'accés.</translation>
|
||||
<message>
|
||||
<source>Show a &notification when credentials are requested</source>
|
||||
<extracomment>Credentials mean login data requested via browser extension</extracomment>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Mostra una &notificació quan es demanen credencials</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Re&quest to unlock the database if it is locked</source>
|
||||
@ -263,11 +264,11 @@ Seleccioneu si voleu permetre l'accés.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Only entries with the same scheme (http://, https://, ...) are returned.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Només es retornen les entrades amb el mateix patró (http://, https://, ...)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>&Match URL scheme (e.g., https://...)</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Torna les coincidències a&mb patrons URL (e.g., https://...)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Only returns the best matches for a specific URL instead of all entries for the whole domain.</source>
|
||||
@ -275,25 +276,25 @@ Seleccioneu si voleu permetre l'accés.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>&Return only best-matching credentials</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>&Retorna només les millors coincidències de les credencials</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Sort &matching credentials by title</source>
|
||||
<extracomment>Credentials mean login data requested via browser extension</extracomment>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Ordena les entrades coincidents per títol (ctrl + &m)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Sort matching credentials by &username</source>
|
||||
<extracomment>Credentials mean login data requested via browser extension</extracomment>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Ordena les entrades coincidents per nom d'&usuari</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>&Disconnect all browsers</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>&Desconnecta tots els navegadors</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Forget all remembered &permissions</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Oblida tots els &permisos recordats</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Advanced</source>
|
||||
@ -302,12 +303,12 @@ Seleccioneu si voleu permetre l'accés.</translation>
|
||||
<message>
|
||||
<source>Never &ask before accessing credentials</source>
|
||||
<extracomment>Credentials mean login data requested via browser extension</extracomment>
|
||||
<translation type="unfinished"/>
|
||||
<translation>No torn&ar a preguntar abans d'accedir a les credencials</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Never ask before &updating credentials</source>
|
||||
<extracomment>Credentials mean login data requested via browser extension</extracomment>
|
||||
<translation type="unfinished"/>
|
||||
<translation>No preguntar abans d'act&ualitzar les credencials</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Only the selected database has to be connected with a client.</source>
|
||||
@ -316,7 +317,7 @@ Seleccioneu si voleu permetre l'accés.</translation>
|
||||
<message>
|
||||
<source>Searc&h in all opened databases for matching credentials</source>
|
||||
<extracomment>Credentials mean login data requested via browser extension</extracomment>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Cerca en totes les bases de dades obertes, entrades coincidents (crtl + &h)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Automatically creating or updating string fields is not supported.</source>
|
||||
@ -354,19 +355,19 @@ Seleccioneu si voleu permetre l'accés.</translation>
|
||||
<message>
|
||||
<source>Browse...</source>
|
||||
<extracomment>Button for opening file dialog</extracomment>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Navega...</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><b>Warning:</b> The following options can be dangerous!</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation><b>Atenció:</b> Canviar les següents opcions és perillós!</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Executable Files (*.exe);;All Files (*.*)</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Arxius executables (*.exe);;Tots els arxius (*.*)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Executable Files (*)</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Arxius executables (*)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Select custom proxy location</source>
|
||||
@ -392,7 +393,7 @@ give it a unique name to identify and accept it.</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>Save and allow access</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Desa i autoritza l'accés</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>KeePassXC: Overwrite existing key?</source>
|
||||
@ -401,7 +402,8 @@ give it a unique name to identify and accept it.</source>
|
||||
<message>
|
||||
<source>A shared encryption key with the name "%1" already exists.
|
||||
Do you want to overwrite it?</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Ja existeix una clau de xifratge compartida amb el nom "%1".
|
||||
Voleu sobreescriure-la?</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>KeePassXC: Update Entry</source>
|
||||
@ -427,7 +429,7 @@ Per favor, desbloqueu la base de dades seleccionada o escolliu-ne una altra.</t
|
||||
</message>
|
||||
<message>
|
||||
<source>The active database does not contain a settings entry.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>La base de dades activa no conté cap entrada de configuració.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>KeePassXC: No keys found</source>
|
||||
@ -435,7 +437,7 @@ Per favor, desbloqueu la base de dades seleccionada o escolliu-ne una altra.</t
|
||||
</message>
|
||||
<message>
|
||||
<source>No shared encryption keys found in KeePassXC Settings.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>No s'han trobat claus de xifratge compartides en la configuració de KeePassHttp.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>KeePassXC: Removed keys from database</source>
|
||||
@ -443,11 +445,11 @@ Per favor, desbloqueu la base de dades seleccionada o escolliu-ne una altra.</t
|
||||
</message>
|
||||
<message numerus="yes">
|
||||
<source>Successfully removed %n encryption key(s) from KeePassXC settings.</source>
|
||||
<translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
|
||||
<translation><numerusform>Reeixidament eliminat %n encriptació clau (s) de configuració KeePassXC.</numerusform><numerusform>Eliminada(es) correctament %n clau(s) de xifratge de la configuració de KeePassXC.</numerusform></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Removing stored permissions…</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Eliminant permisos emmagatzemats...</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Abort</source>
|
||||
@ -1565,7 +1567,7 @@ Do you want to merge your changes?</source>
|
||||
<message>
|
||||
<source>Browse...</source>
|
||||
<extracomment>Button for opening file dialog</extracomment>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Navega...</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Attachment</source>
|
||||
@ -3078,7 +3080,7 @@ This version is not meant for production use.</source>
|
||||
</message>
|
||||
<message>
|
||||
<source><b>Warning:</b> The following options can be dangerous!</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation><b>Atenció:</b> Canviar les següents opcions és perillós!</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><p>KeePassHTTP has been deprecated and will be removed in the future.<br>Please switch to KeePassXC-Browser instead! For help with migration, visit our <a href="https://keepassxc.org/docs/keepassxc-browser-migration">migration guide</a>.</p></source>
|
||||
|
@ -2623,6 +2623,10 @@ This is a one-way migration. You won't be able to open the imported databas
|
||||
<source>KeePassXC - Error</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Database password: </source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>MainWindow</name>
|
||||
@ -3645,10 +3649,6 @@ Available commands:
|
||||
<source>No agent running, cannot add identity.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Agent refused this identity.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>No agent running, cannot remove identity.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
@ -3657,6 +3657,22 @@ Available commands:
|
||||
<source>Agent does not have this identity.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Agent refused this identity. Possible reasons include:</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>The key has already been added.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Restricted lifetime is not supported by the agent (check options).</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>A confirmation request is not supported by the agent (check options).</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>SearchWidget</name>
|
||||
@ -3887,10 +3903,6 @@ Please unlock the selected database or choose another one which is unlocked.</so
|
||||
<source>Global Auto-Type shortcut</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Auto-Type delay</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source> ms</source>
|
||||
<comment>Milliseconds</comment>
|
||||
@ -3920,6 +3932,14 @@ Please unlock the selected database or choose another one which is unlocked.</so
|
||||
<source>General</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Auto-Type typing delay</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Auto-Type start delay</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>SettingsWidgetSecurity</name>
|
||||
@ -4059,6 +4079,17 @@ Please unlock the selected database or choose another one which is unlocked.</so
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>UrlFetchProgressDialog</name>
|
||||
<message>
|
||||
<source>Download Progress</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Downloading %1.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>WelcomeWidget</name>
|
||||
<message>
|
||||
|
4096
share/translations/keepassx_en_GB.ts
Normal file
4096
share/translations/keepassx_en_GB.ts
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -374,7 +374,7 @@ Ole hyvä ja valitse sallitaanko pääsy.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>We're sorry, but KeePassXC-Browser is not supported for Snap releases at the moment.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Valitettavasti KeePassXC-Browser ei ole vielä tuettu Snap-julkaisuissa.</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@ -1976,7 +1976,7 @@ Tämä voi vikaannuttaa tietoa käyttävän liitännäisen.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Attachments (icon)</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Liitteet (kuvake)</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
|
@ -73,7 +73,7 @@ Kernel: %3 %4</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Special thanks from the KeePassXC team go to debfx for creating the original KeePassX.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Terima kasih dari tim KeePassXC kepada debfx yang telah membuat KeepassX original.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Build Type: %1
|
||||
@ -218,7 +218,7 @@ Silakan pilih apakah Anda ingin mengizinkannya.</translation>
|
||||
<name>BrowserOptionDialog</name>
|
||||
<message>
|
||||
<source>Dialog</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Dialog</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>This is required for accessing your databases with KeePassXC-Browser</source>
|
||||
@ -374,7 +374,7 @@ Silakan pilih apakah Anda ingin mengizinkannya.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>We're sorry, but KeePassXC-Browser is not supported for Snap releases at the moment.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Maaf, KeePassXC-Browser saat ini tidak mendukung rilisan Snap.</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@ -447,7 +447,7 @@ Silakan buka kunci atau pilih yang lainnya yang tidak terkunci.</translation>
|
||||
</message>
|
||||
<message numerus="yes">
|
||||
<source>Successfully removed %n encryption key(s) from KeePassXC settings.</source>
|
||||
<translation type="unfinished"><numerusform></numerusform></translation>
|
||||
<translation><numerusform>Berhasil membuang %n kunci enkripsi dari pengaturan KeePassXC.</numerusform></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Removing stored permissions…</source>
|
||||
@ -463,7 +463,7 @@ Silakan buka kunci atau pilih yang lainnya yang tidak terkunci.</translation>
|
||||
</message>
|
||||
<message numerus="yes">
|
||||
<source>Successfully removed permissions from %n entry(s).</source>
|
||||
<translation type="unfinished"><numerusform></numerusform></translation>
|
||||
<translation><numerusform>Berhasil membuang perizinan dari %n entri.</numerusform></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>KeePassXC: No entry with permissions found!</source>
|
||||
@ -856,7 +856,7 @@ If you keep this number, your database may be too easy to crack!</source>
|
||||
<message numerus="yes">
|
||||
<source> MiB</source>
|
||||
<comment>Abbreviation for Mebibytes (KDF settings)</comment>
|
||||
<translation type="unfinished"><numerusform></numerusform></translation>
|
||||
<translation><numerusform> MiB</numerusform></translation>
|
||||
</message>
|
||||
<message numerus="yes">
|
||||
<source> thread(s)</source>
|
||||
@ -1378,15 +1378,15 @@ Apakah Anda ingin menggabungkan ubahan Anda?</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Apply generated password?</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Gunakan sandi yang dibuat?</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Do you want to apply the generated password to this entry?</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Apakah Anda ingin menggunakan sandi yang dibuat untuk entri ini?</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Entry updated successfully.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Entri berhasil diperbarui.</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@ -1421,11 +1421,11 @@ Apakah Anda ingin menggabungkan ubahan Anda?</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Foreground Color:</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Warna Latar Depan:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Background Color:</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Warna Latar Belakang:</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@ -1742,7 +1742,7 @@ Apakah Anda ingin menggabungkan ubahan Anda?</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Plugin Data</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Data Plugin</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Remove</source>
|
||||
@ -1750,12 +1750,13 @@ Apakah Anda ingin menggabungkan ubahan Anda?</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Delete plugin data?</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Hapus data plugin?</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Do you really want to delete the selected plugin data?
|
||||
This may cause the affected plugins to malfunction.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Apakah Anda benar-benar ingin menghapus data plugin?
|
||||
Ini mungkin akan menyebabkan plugin terkait tidak berfungsi.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Key</source>
|
||||
@ -1813,7 +1814,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 type="unfinished"><numerusform></numerusform></translation>
|
||||
<translation><numerusform>Apakah Anda yakin ingin membuang %n lampiran?</numerusform></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Confirm Remove</source>
|
||||
@ -1972,7 +1973,7 @@ This may cause the affected plugins to malfunction.</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>Attachments (icon)</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Lampiran (ikon)</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@ -2997,7 +2998,7 @@ Versi ini tidak dimaksudkan untuk penggunaan sehari-hari.</translation>
|
||||
<name>OptionDialog</name>
|
||||
<message>
|
||||
<source>Dialog</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Dialog</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>This is required for accessing your databases from ChromeIPass or PassIFox</source>
|
||||
@ -3566,11 +3567,11 @@ Perintah yang tersedia:
|
||||
</message>
|
||||
<message>
|
||||
<source>Generate a new random diceware passphrase.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Buat frasa sandi diceware acak yang baru.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Word count for the diceware passphrase.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Jumlah kata untuk frasa sandi diceware.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>count</source>
|
||||
@ -3579,7 +3580,8 @@ Perintah yang tersedia:
|
||||
<message>
|
||||
<source>Wordlist for the diceware generator.
|
||||
[Default: EFF English]</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Daftar kata untuk generator diceware.
|
||||
[Bawaan: Inggris EFF]</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Generate a new random password.</source>
|
||||
@ -3607,7 +3609,7 @@ Perintah yang tersedia:
|
||||
</message>
|
||||
<message>
|
||||
<source>Use extended ASCII in the generated password.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Gunakan ASCII yang diperluas di dalam sandi yang dibuat.</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@ -3962,7 +3964,7 @@ Silakan buka kunci atau pilih yang lainnya yang tidak terkunci.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Re-lock previously locked database after performing Auto-Type</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Kunci ulang basis data yang sebelumnya terkunci setelah menjalankan Ketik-Otomatis</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -375,7 +375,7 @@ Geef aan of je toegang wilt verlenen of niet.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>We're sorry, but KeePassXC-Browser is not supported for Snap releases at the moment.</source>
|
||||
<translation>Het spijt ons, maar KeePassXC-Browser wordt momenteel niet ondersteund voor tussentijdse versies.</translation>
|
||||
<translation>Het spijt ons, maar KeePassXC-Browser wordt momenteel niet ondersteund voor Snap releases.</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@ -1361,7 +1361,7 @@ Wil je de wijzigingen samenvoegen?</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Press reveal to view or edit</source>
|
||||
<translation>Druk Tonen om te bekijken of bewerken</translation>
|
||||
<translation>Druk Weergeven om te bekijken of bewerken</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Tomorrow</source>
|
||||
@ -1416,7 +1416,7 @@ Wil je de wijzigingen samenvoegen?</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Reveal</source>
|
||||
<translation>Tonen</translation>
|
||||
<translation>Weergeven</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Attachments</source>
|
||||
@ -1470,7 +1470,7 @@ Wil je de wijzigingen samenvoegen?</translation>
|
||||
<name>EditEntryWidgetHistory</name>
|
||||
<message>
|
||||
<source>Show</source>
|
||||
<translation>Tonen</translation>
|
||||
<translation>Weergeven</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Restore</source>
|
||||
@ -1513,7 +1513,7 @@ Wil je de wijzigingen samenvoegen?</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Toggle the checkbox to reveal the notes section.</source>
|
||||
<translation>Schakelen aan om notities te tonen.</translation>
|
||||
<translation>Selecteer om notities weer te geven.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Username:</source>
|
||||
@ -3483,7 +3483,7 @@ Beschikbare opdrachten:
|
||||
</message>
|
||||
<message>
|
||||
<source>Names of the attributes to show. This option can be specified more than once, with each attribute shown one-per-line in the given order. If no attributes are specified, a summary of the default attributes is given.</source>
|
||||
<translation>Namen van de te tonen kenmerken. Deze optie kan meer dan eens worden opgegeven, waarbij elk kenmerk op een regel wordt getoond in de opgegeven volgorde. Als er geen kenmerken worden opgegeven, wordt een samenvatting van de standaardkenmerken gegeven.</translation>
|
||||
<translation>Namen van de weer te geven kenmerken. Deze optie kan meer dan eens worden opgegeven, waarbij elk kenmerk op een regel wordt getoond in de opgegeven volgorde. Als er geen kenmerken worden opgegeven, wordt een samenvatting van de standaardkenmerken gegeven.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>attribute</source>
|
||||
@ -3837,7 +3837,7 @@ Ontgrendel de geselecteerde database of kies een ontgrendelde database.</transla
|
||||
</message>
|
||||
<message>
|
||||
<source>Show a system tray icon</source>
|
||||
<translation>Icoon in het systeemvak tonen</translation>
|
||||
<translation>Icoon in het systeemvak weergeven</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Hide window to system tray when minimized</source>
|
||||
|
@ -3548,7 +3548,7 @@ Dostępne polecenia:
|
||||
</message>
|
||||
<message>
|
||||
<source>Legacy Browser Integration</source>
|
||||
<translation>Integracja ze starą przeglądarką</translation>
|
||||
<translation>Przestarzała integracja z przeglądarką</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Browser Integration</source>
|
||||
|
@ -23,11 +23,11 @@
|
||||
</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">Ver Colaborações no GitHub</a></translation>
|
||||
<translation><a href="https://github.com/keepassxreboot/keepassxc/graphs/contributors">Ver colaborações no GitHub</a></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Debug Info</source>
|
||||
<translation>Informações de Depuração</translation>
|
||||
<translation>Informações de depuração</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Include the following information whenever you report a bug:</source>
|
||||
@ -69,11 +69,11 @@ Kernel: %3 %4</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Project Maintainers:</source>
|
||||
<translation>Mantedores do Projeto:</translation>
|
||||
<translation>Mantedores do projeto:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Special thanks from the KeePassXC team go to debfx for creating the original KeePassX.</source>
|
||||
<translation>A equipe KeePassXC agradece especialmente a debfx pela criação do KeePassX original.</translation>
|
||||
<translation>A equipe do KeePassXC agradece especialmente a debfx pela criação do KeePassX original.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Build Type: %1
|
||||
@ -111,7 +111,7 @@ Selecione se deseja permitir o acesso.</translation>
|
||||
<name>AgentSettingsWidget</name>
|
||||
<message>
|
||||
<source>Enable SSH Agent (requires restart)</source>
|
||||
<translation>Habilitar Agente SSH (requer reinicialização)</translation>
|
||||
<translation>Habilitar agente SSH (requer reinicialização)</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@ -122,27 +122,27 @@ Selecione se deseja permitir o acesso.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Auto-Type - KeePassXC</source>
|
||||
<translation>Auto-Digitação - KeePassXC</translation>
|
||||
<translation>Autodigitação - KeePassXC</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Auto-Type</source>
|
||||
<translation>Auto-Digitação</translation>
|
||||
<translation>Autodigitação</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>The Syntax of your Auto-Type statement is incorrect!</source>
|
||||
<translation>A sintaxe da sua sequência de Auto-Digitação está incorreta!</translation>
|
||||
<translation>A sintaxe da sua sequência de autodigitação está incorreta!</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>This Auto-Type command contains a very long delay. Do you really want to proceed?</source>
|
||||
<translation>Este comando de Auto-Digitação contém um tempo de espera muito longo. Você tem certeza que deseja continuar?</translation>
|
||||
<translation>Este comando de autodigitação contém um tempo de espera muito longo. Você tem certeza de que deseja continuar?</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>This Auto-Type command contains very slow key presses. Do you really want to proceed?</source>
|
||||
<translation>Este comando Autotipo contém pressionamentos de teclas muito lentos. Você realmente deseja prosseguir?</translation>
|
||||
<translation>Este comando de autodigitação contém pressionamentos de teclas muito lentos. Você tem certeza de que deseja continuar?</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>This Auto-Type command contains arguments which are repeated very often. Do you really want to proceed?</source>
|
||||
<translation>Este comando Auto-Type contém os argumentos que são repetidos muitas vezes. Você realmente deseja prosseguir?</translation>
|
||||
<translation>Este comando de autodigitação contém parâmetros que são repetidos muitas vezes. Você tem certeza de que deseja continuar?</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@ -183,11 +183,11 @@ Selecione se deseja permitir o acesso.</translation>
|
||||
<name>AutoTypeSelectDialog</name>
|
||||
<message>
|
||||
<source>Auto-Type - KeePassXC</source>
|
||||
<translation>Auto-Digitação - KeePassXC</translation>
|
||||
<translation>Autodigitação - KeePassXC</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Select entry to Auto-Type:</source>
|
||||
<translation>Escolha uma entrada para Auto-Digitar:</translation>
|
||||
<translation>Escolha uma entrada para digitar automaticamente:</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
|
@ -360,11 +360,11 @@ Please select whether you want to allow access.</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>Executable Files (*.exe);;All Files (*.*)</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Fișiere Executabile (*.exe);;Toate Fișierele (*.*)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Executable Files (*)</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Fișiere Executabile (*)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Select custom proxy location</source>
|
||||
@ -390,11 +390,11 @@ give it a unique name to identify and accept it.</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>Save and allow access</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Salvează și permite acces</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>KeePassXC: Overwrite existing key?</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>KeePassXC: Suprascriere cheie existentă?</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>A shared encryption key with the name "%1" already exists.
|
||||
@ -593,7 +593,7 @@ Please consider generating a new key file.</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>Codec</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Codec</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Text is qualified by</source>
|
||||
@ -641,7 +641,7 @@ Please consider generating a new key file.</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>Imported from CSV file</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Importat din fișier CSV</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Original data: </source>
|
||||
@ -797,7 +797,7 @@ You can now save it.</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>Encryption</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Criptare</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Number of rounds too high</source>
|
||||
@ -816,7 +816,7 @@ If you keep this number, your database may take hours or days (or even longer) t
|
||||
</message>
|
||||
<message>
|
||||
<source>Cancel</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Anulare</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Number of rounds too low</source>
|
||||
@ -852,7 +852,7 @@ If you keep this number, your database may be too easy to crack!</source>
|
||||
<name>DatabaseSettingsWidgetEncryption</name>
|
||||
<message>
|
||||
<source>Encryption Algorithm:</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Algoritm Criptare:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>AES: 256 Bit (default)</source>
|
||||
@ -864,7 +864,7 @@ If you keep this number, your database may be too easy to crack!</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>Key Derivation Function:</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Funcție Derivare Cheie:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Transform rounds:</source>
|
||||
@ -876,18 +876,18 @@ If you keep this number, your database may be too easy to crack!</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>Memory Usage:</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Utilizare Memorie:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Parallelism:</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Paralelism:</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>DatabaseSettingsWidgetGeneral</name>
|
||||
<message>
|
||||
<source>Database Meta Data</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Meta Date Bază de Date</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Database name:</source>
|
||||
@ -903,7 +903,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>Setări Istoric</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Max. history items:</source>
|
||||
@ -923,7 +923,7 @@ If you keep this number, your database may be too easy to crack!</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>Additional Database Settings</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Setări Adiționale Bază de Date</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Enable &compression (recommended)</source>
|
||||
@ -1006,7 +1006,7 @@ Salvați modificările?</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Writing the database failed.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Scrierea în baza de date a eșuat.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Passwords</source>
|
||||
@ -1141,11 +1141,11 @@ Disable safe saves and try again?</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>File has changed</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Fișierul a fost modificat</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>The database file has changed. Do you want to load the changes?</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Fișierul bazei de date a fost modificat. Doriți să încărcați ultimele modificări?</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Merge Request</source>
|
||||
@ -1264,7 +1264,7 @@ Do you want to merge your changes?</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>Icon</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Icon</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Auto-Type</source>
|
||||
@ -1583,7 +1583,7 @@ Do you want to merge your changes?</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>Icon</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Icon</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Properties</source>
|
||||
@ -1653,11 +1653,11 @@ Do you want to merge your changes?</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>Add custom icon</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Adaugă icon personalizat</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Delete custom icon</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Șterge icon personalizat</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Download favicon</source>
|
||||
@ -1665,7 +1665,7 @@ Do you want to merge your changes?</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>Unable to fetch favicon.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Nu pot descărca favicon.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Hint: You can enable Google as a fallback under Tools>Settings>Security</source>
|
||||
@ -1685,11 +1685,11 @@ Do you want to merge your changes?</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>Can't read icon</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Nu pot citi icon</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Custom icon already exists</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Icon personalizat deja există</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Confirm Delete</source>
|
||||
@ -1945,7 +1945,7 @@ This may cause the affected plugins to malfunction.</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>Attachments (icon)</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Atașamente (icon)</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@ -2849,7 +2849,7 @@ This is a one-way migration. You won't be able to open the imported databas
|
||||
</message>
|
||||
<message>
|
||||
<source>Writing the database failed.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Scrierea în baza de date a eșuat.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Please touch the button on your YubiKey!</source>
|
||||
@ -3645,7 +3645,7 @@ give it a unique name to identify and accept it.</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>KeePassXC: Overwrite existing key?</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>KeePassXC: Suprascriere cheie existentă?</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>A shared encryption-key with the name "%1" already exists.
|
||||
@ -3918,7 +3918,7 @@ Please unlock the selected database or choose another one which is unlocked.</so
|
||||
</message>
|
||||
<message>
|
||||
<source>Use Google as fallback for downloading website icons</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Folosește Google ca variantă de rezervă pentru descărcare favicon site</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Re-lock previously locked database after performing Auto-Type</source>
|
||||
|
@ -35,7 +35,7 @@
|
||||
</message>
|
||||
<message>
|
||||
<source>Copy to clipboard</source>
|
||||
<translation>Скопіювати у буфер обміну</translation>
|
||||
<translation>Скопіювати в кишеню</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Version %1
|
||||
@ -73,12 +73,13 @@ Kernel: %3 %4</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>Special thanks from the KeePassXC team go to debfx for creating the original KeePassX.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Команда KeePassXC щиро дякує debfx за створення первісної версії KeePassX.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Build Type: %1
|
||||
</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Тип збірки: %1
|
||||
</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@ -103,7 +104,7 @@ Kernel: %3 %4</source>
|
||||
<source>%1 has requested access to passwords for the following item(s).
|
||||
Please select whether you want to allow access.</source>
|
||||
<translation>%1 запросила доступ до гасел для таких записів(-у).
|
||||
Будь ласка, вкажіть чи хочете ви дозволити доступ?</translation>
|
||||
Будь ласка, вкажіть чи хочете Ви дозволити доступ?</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@ -211,7 +212,7 @@ Please select whether you want to allow access.</source>
|
||||
<source>%1 has requested access to passwords for the following item(s).
|
||||
Please select whether you want to allow access.</source>
|
||||
<translation>%1 запросила доступ до гасел для таких записів(-у).
|
||||
Будь ласка, вкажіть чи хочете ви дозволити доступ?</translation>
|
||||
Будь ласка, вкажіть чи хочете Ви дозволити доступ?</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@ -374,7 +375,7 @@ Please select whether you want to allow access.</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>We're sorry, but KeePassXC-Browser is not supported for Snap releases at the moment.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Вибачте, але переглядач KeePassXC поки що не працює у версіях Snap.</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@ -443,11 +444,11 @@ Please unlock the selected database or choose another one which is unlocked.</so
|
||||
</message>
|
||||
<message>
|
||||
<source>KeePassXC: Removed keys from database</source>
|
||||
<translation>KeePassXC: ключі видалені зі сховища</translation>
|
||||
<translation>KeePassXC: ключі видалено зі сховища</translation>
|
||||
</message>
|
||||
<message numerus="yes">
|
||||
<source>Successfully removed %n encryption key(s) from KeePassXC settings.</source>
|
||||
<translation type="unfinished"><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform></translation>
|
||||
<translation><numerusform>Успішно видалено %n шифрувальний ключ з налаштувань KeePassXC.</numerusform><numerusform>Успішно видалено %n шифрувальні ключа з налаштувань KeePassXC.</numerusform><numerusform>Успішно видалено %n шифрувальних ключів з налаштувань KeePassXC.</numerusform></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Removing stored permissions…</source>
|
||||
@ -459,11 +460,11 @@ Please unlock the selected database or choose another one which is unlocked.</so
|
||||
</message>
|
||||
<message>
|
||||
<source>KeePassXC: Removed permissions</source>
|
||||
<translation>KeePassXC: привілеї видалені</translation>
|
||||
<translation>KeePassXC: привілеї видалено</translation>
|
||||
</message>
|
||||
<message numerus="yes">
|
||||
<source>Successfully removed permissions from %n entry(s).</source>
|
||||
<translation type="unfinished"><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform></translation>
|
||||
<translation><numerusform>Успішно видалено привілеї для %n запису.</numerusform><numerusform>Успішно видалено привілеї для %n записів.</numerusform><numerusform>Успішно видалено привілеї для %n записів.</numerusform></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>KeePassXC: No entry with permissions found!</source>
|
||||
@ -788,7 +789,7 @@ Please consider generating a new key file.</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>Database opened fine. Nothing to do.</source>
|
||||
<translation>Сховище відкрите без помилок. Обробка не потрібна.</translation>
|
||||
<translation>Сховище відкрито без помилок. Обробка не потрібна.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Success</source>
|
||||
@ -797,8 +798,8 @@ Please consider generating a new key file.</source>
|
||||
<message>
|
||||
<source>The database has been successfully repaired
|
||||
You can now save it.</source>
|
||||
<translation>Лагодження сховища успішно завершене.
|
||||
Тепер ви можете його зберегти.</translation>
|
||||
<translation>Лагодження сховища успішно завершено.
|
||||
Тепер Ви можете його зберегти.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Unable to repair the database.</source>
|
||||
@ -830,7 +831,7 @@ If you keep this number, your database may take hours or days (or even longer) t
|
||||
</message>
|
||||
<message>
|
||||
<source>Understood, keep number</source>
|
||||
<translation>Зрозуміло, зберігайте число</translation>
|
||||
<translation>Зрозуміло, кількість залишено</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Cancel</source>
|
||||
@ -860,12 +861,12 @@ If you keep this number, your database may be too easy to crack!</source>
|
||||
<message numerus="yes">
|
||||
<source> MiB</source>
|
||||
<comment>Abbreviation for Mebibytes (KDF settings)</comment>
|
||||
<translation type="unfinished"><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform></translation>
|
||||
<translation><numerusform>МіБ</numerusform><numerusform>МіБ</numerusform><numerusform>МіБ</numerusform></translation>
|
||||
</message>
|
||||
<message numerus="yes">
|
||||
<source> thread(s)</source>
|
||||
<comment>Threads for parallel execution (KDF settings)</comment>
|
||||
<translation type="unfinished"><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform></translation>
|
||||
<translation><numerusform>потік</numerusform><numerusform>потоки</numerusform><numerusform>потоків</numerusform></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@ -1382,15 +1383,15 @@ Do you want to merge your changes?</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>Apply generated password?</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Застосувати створене гасло?</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Do you want to apply the generated password to this entry?</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Бажаєте застосувати створене гасло до цього запису?</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Entry updated successfully.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Запис успішно оновлено.</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@ -1425,11 +1426,11 @@ Do you want to merge your changes?</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>Foreground Color:</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Колір переднього плану:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Background Color:</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation> Колір тла:</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@ -1464,7 +1465,7 @@ Do you want to merge your changes?</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>Use a specific sequence for this association:</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Використовувати певну послідовність для цієї прив'язки:</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@ -1569,7 +1570,7 @@ Do you want to merge your changes?</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>Copy to clipboard</source>
|
||||
<translation>Скопіювати у буфер обміну</translation>
|
||||
<translation>Скопіювати в кишеню</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Private key</source>
|
||||
@ -1746,7 +1747,7 @@ Do you want to merge your changes?</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>Plugin Data</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Дані модуля</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Remove</source>
|
||||
@ -1754,20 +1755,21 @@ Do you want to merge your changes?</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>Delete plugin data?</source>
|
||||
<translation type="unfinished"/>
|
||||
<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 type="unfinished"/>
|
||||
<translation>Ви дійсно хочете видалити всі дані позначеного модуля?
|
||||
Це може призвести до порушень у роботі інших модулів.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Key</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Ключ</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Value</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Значення</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@ -1817,7 +1819,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 type="unfinished"><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform></translation>
|
||||
<translation><numerusform>Ви дійсно хочете видалити %n вкладення?</numerusform><numerusform>Ви дійсно хочете видалити %n вкладення?</numerusform><numerusform>Ви дійсно хочете видалити %n вкладень?</numerusform></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Confirm Remove</source>
|
||||
@ -1976,7 +1978,7 @@ This may cause the affected plugins to malfunction.</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>Attachments (icon)</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Вкладення (значок)</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@ -2075,7 +2077,7 @@ This may cause the affected plugins to malfunction.</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>Wrong key or database file is corrupt.</source>
|
||||
<translation>Неправильний ключ або файл сховища пошкоджено.</translation>
|
||||
<translation>Неправильний ключ або пошкоджене сховище.</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@ -2221,7 +2223,7 @@ This may cause the affected plugins to malfunction.</source>
|
||||
<message>
|
||||
<source>Invalid symmetric cipher IV size.</source>
|
||||
<comment>IV = Initialization Vector for symmetric cipher</comment>
|
||||
<translation>Хибний розмір IV симетричного шифру.</translation>
|
||||
<translation>Хибний розмір симетричного шифру IV.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Unable to calculate master key</source>
|
||||
@ -2441,11 +2443,11 @@ This is a one-way migration. You won't be able to open the imported databas
|
||||
</message>
|
||||
<message>
|
||||
<source>Unsupported encryption algorithm.</source>
|
||||
<translation>Алгоритм шифрування не підтримується.</translation>
|
||||
<translation>Цей алгоритм шифрування не підтримується.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Unsupported KeePass database version.</source>
|
||||
<translation>Версія сховища KeePass не підтримується.</translation>
|
||||
<translation>Ця версія сховища KeePass не підтримується.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Unable to read encryption IV</source>
|
||||
@ -2486,7 +2488,7 @@ This is a one-way migration. You won't be able to open the imported databas
|
||||
</message>
|
||||
<message>
|
||||
<source>Wrong key or database file is corrupt.</source>
|
||||
<translation>Неправильний ключ або файл сховища пошкоджено.</translation>
|
||||
<translation>Неправильний ключ або пошкоджене сховище.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Key transformation failed</source>
|
||||
@ -2655,7 +2657,7 @@ This is a one-way migration. You won't be able to open the imported databas
|
||||
</message>
|
||||
<message>
|
||||
<source>Copy att&ribute to clipboard</source>
|
||||
<translation>Копіювати атрибут до буферу обміну</translation>
|
||||
<translation>Копіювати атрибут до кишені</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Time-based one-time password</source>
|
||||
@ -2743,7 +2745,7 @@ This is a one-way migration. You won't be able to open the imported databas
|
||||
</message>
|
||||
<message>
|
||||
<source>&Find</source>
|
||||
<translation>Пошук</translation>
|
||||
<translation>Знайти</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Copy &username</source>
|
||||
@ -2751,7 +2753,7 @@ This is a one-way migration. You won't be able to open the imported databas
|
||||
</message>
|
||||
<message>
|
||||
<source>Copy username to clipboard</source>
|
||||
<translation>Копіювати ім’я користувача в буфер обміну</translation>
|
||||
<translation>Копіювати ім’я користувача в кишеню</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Cop&y password</source>
|
||||
@ -2759,7 +2761,7 @@ This is a one-way migration. You won't be able to open the imported databas
|
||||
</message>
|
||||
<message>
|
||||
<source>Copy password to clipboard</source>
|
||||
<translation>Копіювати гасло в буфер обміну</translation>
|
||||
<translation>Копіювати гасло в кишеню</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>&Settings</source>
|
||||
@ -2978,23 +2980,23 @@ This version is not meant for production use.</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>Unsupported key type: %1</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Непідтримуваний вид ключа: %1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Unknown cipher: %1</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Невідомий шифр: %1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Cipher IV is too short for MD5 kdf</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Шифр IV занадто короткий для ФОК MD5</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Unknown KDF: %1</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Невідома ФОК: %1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Unknown key type: %1</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Невідомий тип ключа: %1</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@ -3372,7 +3374,7 @@ Using default port 19455.</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>Timeout in seconds before clearing the clipboard.</source>
|
||||
<translation>Час очікування у </translation>
|
||||
<translation>Час очікування перед очищенням кишені.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Edit an entry.</source>
|
||||
@ -3468,7 +3470,7 @@ Available commands:
|
||||
</message>
|
||||
<message>
|
||||
<source>Path of the database to merge from.</source>
|
||||
<translation>Шлях до сховища, яке об'єднати.</translation>
|
||||
<translation>Шлях до сховища, яке підлягає об'єднанню.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Use the same credentials for both database files.</source>
|
||||
@ -3476,7 +3478,7 @@ Available commands:
|
||||
</message>
|
||||
<message>
|
||||
<source>Key file of the database to merge from.</source>
|
||||
<translation>Файл ключа для сховища, з якого треба взяти дані для об'єднання.</translation>
|
||||
<translation>Файл ключа для сховища, яке підлягає об'єднанню.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Show an entry's information.</source>
|
||||
@ -3570,48 +3572,49 @@ Available commands:
|
||||
</message>
|
||||
<message>
|
||||
<source>Generate a new random diceware passphrase.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Створити новий вираз гасла методом гральних кісточок (diceware).</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Word count for the diceware passphrase.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Кількість слів у виразі гасла.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>count</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>кількість</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Wordlist for the diceware generator.
|
||||
[Default: EFF English]</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Список слів для виробника гасел методом diceware.
|
||||
[Типово: англійська версія EFF]</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Generate a new random password.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Створити нове випадкове гасло.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Length of the generated password.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Довжина створюваного гасла.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Use lowercase characters in the generated password.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Використовувати малі літери для створення гасла.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Use uppercase characters in the generated password.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Використовувати великі літери для створення гасла.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Use numbers in the generated password.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Використовувати числа для створення гасла.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Use special characters in the generated password.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Використовувати спеціальні символи для створення гасла.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Use extended ASCII in the generated password.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Використовувати розширені ASCII для створення гасла.</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@ -3652,7 +3655,7 @@ Available commands:
|
||||
<name>SearchWidget</name>
|
||||
<message>
|
||||
<source>Search...</source>
|
||||
<translation>Шукати...</translation>
|
||||
<translation>Знайти...</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Search</source>
|
||||
@ -3715,11 +3718,11 @@ Please unlock the selected database or choose another one which is unlocked.</so
|
||||
</message>
|
||||
<message>
|
||||
<source>KeePassXC: Removed keys from database</source>
|
||||
<translation>KeePassXC: ключі видалені зі сховища</translation>
|
||||
<translation>KeePassXC: ключі видалено зі сховища</translation>
|
||||
</message>
|
||||
<message numerus="yes">
|
||||
<source>Successfully removed %n encryption-key(s) from KeePassX/Http Settings.</source>
|
||||
<translation><numerusform>Успішно видалив %n шифрувальний ключ з HTTP налаштувань KeePassX.</numerusform><numerusform>Успішно видалив %n шифрувальних ключа з HTTP налаштувань KeePassX.</numerusform><numerusform>Успішно видалив %n шифрувальних ключів з HTTP налаштувань KeePassX.</numerusform></translation>
|
||||
<translation><numerusform>Успішно видалено %n шифрувальний ключ з HTTP налаштувань KeePassX.</numerusform><numerusform>Успішно видалено %n шифрувальних ключа з HTTP налаштувань KeePassX.</numerusform><numerusform>Успішно видалено %n шифрувальних ключів з HTTP налаштувань KeePassX.</numerusform></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>KeePassXC: No keys found</source>
|
||||
@ -3747,11 +3750,11 @@ Please unlock the selected database or choose another one which is unlocked.</so
|
||||
</message>
|
||||
<message>
|
||||
<source>KeePassXC: Removed permissions</source>
|
||||
<translation>KeePassXC: привілеї видалені</translation>
|
||||
<translation>KeePassXC: привілеї видалено</translation>
|
||||
</message>
|
||||
<message numerus="yes">
|
||||
<source>Successfully removed permissions from %n entries.</source>
|
||||
<translation><numerusform>Успішно видалив привілеї для %n запису.</numerusform><numerusform>Успішно видалив привілеї для %n записів.</numerusform><numerusform>Успішно видалив привілеї для %n записів.</numerusform></translation>
|
||||
<translation><numerusform>Успішно видалено привілеї для %n запису.</numerusform><numerusform>Успішно видалено привілеї для %n записів.</numerusform><numerusform>Успішно видалено привілеї для %n записів.</numerusform></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>KeePassXC: No entry with permissions found!</source>
|
||||
@ -3817,7 +3820,7 @@ Please unlock the selected database or choose another one which is unlocked.</so
|
||||
</message>
|
||||
<message>
|
||||
<source>Minimize when copying to clipboard</source>
|
||||
<translation>Згортати при копіюванні до буфера обміну</translation>
|
||||
<translation>Згортати при копіюванні до кишені</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Minimize window at application startup</source>
|
||||
@ -3917,7 +3920,7 @@ Please unlock the selected database or choose another one which is unlocked.</so
|
||||
</message>
|
||||
<message>
|
||||
<source>Clear clipboard after</source>
|
||||
<translation>Очищати буфер обміну через</translation>
|
||||
<translation>Очищати кишеню через</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source> sec</source>
|
||||
@ -3966,7 +3969,7 @@ Please unlock the selected database or choose another one which is unlocked.</so
|
||||
</message>
|
||||
<message>
|
||||
<source>Re-lock previously locked database after performing Auto-Type</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Заблоковувати попередньо заблоковане сховище після завершення автозаповнення</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
|
@ -73,7 +73,7 @@ CPU 架构:%2
|
||||
</message>
|
||||
<message>
|
||||
<source>Special thanks from the KeePassXC team go to debfx for creating the original KeePassX.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>KeePassXC 团队特别感谢 debfx 开发了最初版 KeePassX</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Build Type: %1
|
||||
@ -255,7 +255,7 @@ Please select whether you want to allow access.</source>
|
||||
<message>
|
||||
<source>Show a &notification when credentials are requested</source>
|
||||
<extracomment>Credentials mean login data requested via browser extension</extracomment>
|
||||
<translation type="unfinished"/>
|
||||
<translation>当请求凭据时显示通知 (&N)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Re&quest to unlock the database if it is locked</source>
|
||||
@ -280,12 +280,12 @@ Please select whether you want to allow access.</source>
|
||||
<message>
|
||||
<source>Sort &matching credentials by title</source>
|
||||
<extracomment>Credentials mean login data requested via browser extension</extracomment>
|
||||
<translation type="unfinished"/>
|
||||
<translation>按照名称排列匹配的凭据</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Sort matching credentials by &username</source>
|
||||
<extracomment>Credentials mean login data requested via browser extension</extracomment>
|
||||
<translation type="unfinished"/>
|
||||
<translation>根据用户名排列匹配的凭据</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>&Disconnect all browsers</source>
|
||||
@ -293,7 +293,7 @@ Please select whether you want to allow access.</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>Forget all remembered &permissions</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>取消所有记住的权限</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Advanced</source>
|
||||
@ -302,7 +302,7 @@ Please select whether you want to allow access.</source>
|
||||
<message>
|
||||
<source>Never &ask before accessing credentials</source>
|
||||
<extracomment>Credentials mean login data requested via browser extension</extracomment>
|
||||
<translation type="unfinished"/>
|
||||
<translation>读取凭据时不再询问</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Never ask before &updating credentials</source>
|
||||
@ -316,7 +316,7 @@ Please select whether you want to allow access.</source>
|
||||
<message>
|
||||
<source>Searc&h in all opened databases for matching credentials</source>
|
||||
<extracomment>Credentials mean login data requested via browser extension</extracomment>
|
||||
<translation type="unfinished"/>
|
||||
<translation>在所有打开的的数据库中搜索相符的凭据</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Automatically creating or updating string fields is not supported.</source>
|
||||
@ -328,11 +328,11 @@ Please select whether you want to allow access.</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>Updates KeePassXC or keepassxc-proxy binary path automatically to native messaging scripts on startup.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>启动时自动将 KeePassXC 或 keepassxc-proxy 执行文件路径更新为 native messaging 脚本。</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Update &native messaging manifest files at startup</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>启动时更新 native messaging 的 manifest 文件</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Support a proxy application between KeePassXC and browser extension.</source>
|
||||
@ -340,16 +340,16 @@ Please select whether you want to allow access.</source>
|
||||
</message>
|
||||
<message>
|
||||
<source>Use a &proxy application between KeePassXC and browser extension</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>在 KeePassXC 与浏览器扩展之间使用代理程序</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Use a custom proxy location if you installed a proxy manually.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>如果手工安装了代理,请设置自定义代理的路径。</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Use a &custom proxy location</source>
|
||||
<comment>Meant is the proxy for KeePassXC-Browser</comment>
|
||||
<translation type="unfinished"/>
|
||||
<translation>设置自定义代理路径</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Browse...</source>
|
||||
|
@ -1,5 +1,5 @@
|
||||
name: keepassxc
|
||||
version: 2.3.1
|
||||
version: 2.3.2
|
||||
grade: stable
|
||||
summary: Community-driven port of the Windows application “KeePass Password Safe”
|
||||
description: |
|
||||
|
@ -202,9 +202,6 @@ add_feature_info(SSHAgent WITH_XC_SSHAGENT "SSH agent integration compatible wit
|
||||
add_feature_info(YubiKey WITH_XC_YUBIKEY "YubiKey HMAC-SHA1 challenge-response")
|
||||
|
||||
add_subdirectory(http)
|
||||
if(WITH_XC_NETWORKING)
|
||||
find_package(CURL REQUIRED)
|
||||
endif()
|
||||
|
||||
set(BROWSER_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/browser)
|
||||
add_subdirectory(browser)
|
||||
@ -279,7 +276,7 @@ if (UNIX AND NOT APPLE)
|
||||
target_link_libraries(keepassx_core Qt5::DBus)
|
||||
endif()
|
||||
if(MINGW)
|
||||
target_link_libraries(keepassx_core Wtsapi32.lib)
|
||||
target_link_libraries(keepassx_core Wtsapi32.lib Ws2_32.lib)
|
||||
endif()
|
||||
|
||||
if(MINGW)
|
||||
|
@ -140,13 +140,6 @@ QStringList AutoType::windowTitles()
|
||||
return m_plugin->windowTitles();
|
||||
}
|
||||
|
||||
void AutoType::resetInAutoType()
|
||||
{
|
||||
m_inAutoType.unlock();
|
||||
|
||||
emit autotypeRejected();
|
||||
}
|
||||
|
||||
void AutoType::raiseWindow()
|
||||
{
|
||||
#if defined(Q_OS_MAC)
|
||||
@ -199,9 +192,14 @@ int AutoType::callEventFilter(void* event)
|
||||
*/
|
||||
void AutoType::executeAutoTypeActions(const Entry* entry, QWidget* hideWindow, const QString& sequence, WId window)
|
||||
{
|
||||
if (!m_inAutoType.tryLock()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// no edit to the sequence beyond this point
|
||||
if (!verifyAutoTypeSyntax(sequence)) {
|
||||
emit autotypeRejected();
|
||||
m_inAutoType.unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -210,6 +208,7 @@ void AutoType::executeAutoTypeActions(const Entry* entry, QWidget* hideWindow, c
|
||||
|
||||
if (!parseActions(sequence, entry, actions)) {
|
||||
emit autotypeRejected();
|
||||
m_inAutoType.unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -221,7 +220,7 @@ void AutoType::executeAutoTypeActions(const Entry* entry, QWidget* hideWindow, c
|
||||
#endif
|
||||
}
|
||||
|
||||
Tools::wait(m_plugin->initialTimeout());
|
||||
Tools::wait(qMax(100, config()->get("AutoTypeStartDelay", 500).toInt()));
|
||||
|
||||
if (!window) {
|
||||
window = m_plugin->activeWindow();
|
||||
@ -233,6 +232,7 @@ void AutoType::executeAutoTypeActions(const Entry* entry, QWidget* hideWindow, c
|
||||
if (m_plugin->activeWindow() != window) {
|
||||
qWarning("Active window changed, interrupting auto-type.");
|
||||
emit autotypeRejected();
|
||||
m_inAutoType.unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -242,6 +242,8 @@ void AutoType::executeAutoTypeActions(const Entry* entry, QWidget* hideWindow, c
|
||||
|
||||
// emit signal only if autotype performed correctly
|
||||
emit autotypePerformed();
|
||||
|
||||
m_inAutoType.unlock();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -259,13 +261,7 @@ void AutoType::performAutoType(const Entry* entry, QWidget* hideWindow)
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m_inAutoType.tryLock()) {
|
||||
return;
|
||||
}
|
||||
|
||||
executeAutoTypeActions(entry, hideWindow, sequences.first());
|
||||
|
||||
m_inAutoType.unlock();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -278,13 +274,14 @@ void AutoType::performGlobalAutoType(const QList<Database*>& dbList)
|
||||
return;
|
||||
}
|
||||
|
||||
QString windowTitle = m_plugin->activeWindowTitle();
|
||||
|
||||
if (windowTitle.isEmpty()) {
|
||||
if (!m_inGlobalAutoTypeDialog.tryLock()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m_inAutoType.tryLock()) {
|
||||
QString windowTitle = m_plugin->activeWindowTitle();
|
||||
|
||||
if (windowTitle.isEmpty()) {
|
||||
m_inGlobalAutoTypeDialog.unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -303,8 +300,6 @@ void AutoType::performGlobalAutoType(const QList<Database*>& dbList)
|
||||
}
|
||||
|
||||
if (matchList.isEmpty()) {
|
||||
m_inAutoType.unlock();
|
||||
|
||||
if (qobject_cast<QApplication*>(QCoreApplication::instance())) {
|
||||
auto* msgBox = new QMessageBox();
|
||||
msgBox->setAttribute(Qt::WA_DeleteOnClose);
|
||||
@ -318,16 +313,20 @@ void AutoType::performGlobalAutoType(const QList<Database*>& dbList)
|
||||
msgBox->activateWindow();
|
||||
}
|
||||
|
||||
m_inGlobalAutoTypeDialog.unlock();
|
||||
emit autotypeRejected();
|
||||
} else if ((matchList.size() == 1) && !config()->get("security/autotypeask").toBool()) {
|
||||
executeAutoTypeActions(matchList.first().entry, nullptr, matchList.first().sequence);
|
||||
m_inAutoType.unlock();
|
||||
m_inGlobalAutoTypeDialog.unlock();
|
||||
} else {
|
||||
m_windowFromGlobal = m_plugin->activeWindow();
|
||||
auto* selectDialog = new AutoTypeSelectDialog();
|
||||
|
||||
// connect slots, both of which must unlock the m_inGlobalAutoTypeDialog mutex
|
||||
connect(selectDialog, SIGNAL(matchActivated(AutoTypeMatch)),
|
||||
SLOT(performAutoTypeFromGlobal(AutoTypeMatch)));
|
||||
connect(selectDialog, SIGNAL(rejected()), SLOT(resetInAutoType()));
|
||||
connect(selectDialog, SIGNAL(rejected()), SLOT(autoTypeRejectedFromGlobal()));
|
||||
|
||||
selectDialog->setMatchList(matchList);
|
||||
#if defined(Q_OS_MAC)
|
||||
m_plugin->raiseOwnWindow();
|
||||
@ -341,14 +340,22 @@ void AutoType::performGlobalAutoType(const QList<Database*>& dbList)
|
||||
|
||||
void AutoType::performAutoTypeFromGlobal(AutoTypeMatch match)
|
||||
{
|
||||
// We don't care about the result here, the mutex should already be locked. Now it's locked for sure
|
||||
m_inAutoType.tryLock();
|
||||
|
||||
m_plugin->raiseWindow(m_windowFromGlobal);
|
||||
|
||||
executeAutoTypeActions(match.entry, nullptr, match.sequence, m_windowFromGlobal);
|
||||
|
||||
m_inAutoType.unlock();
|
||||
// make sure the mutex is definitely locked before we unlock it
|
||||
Q_UNUSED(m_inGlobalAutoTypeDialog.tryLock());
|
||||
m_inGlobalAutoTypeDialog.unlock();
|
||||
}
|
||||
|
||||
void AutoType::autoTypeRejectedFromGlobal()
|
||||
{
|
||||
// this slot can be called twice when the selection dialog is deleted,
|
||||
// so make sure the mutex is locked before we try unlocking it
|
||||
Q_UNUSED(m_inGlobalAutoTypeDialog.tryLock());
|
||||
m_inGlobalAutoTypeDialog.unlock();
|
||||
|
||||
emit autotypeRejected();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -358,7 +365,7 @@ bool AutoType::parseActions(const QString& actionSequence, const Entry* entry, Q
|
||||
{
|
||||
QString tmpl;
|
||||
bool inTmpl = false;
|
||||
m_autoTypeDelay = config()->get("AutoTypeDelay").toInt();
|
||||
m_autoTypeDelay = qMax(config()->get("AutoTypeDelay").toInt(), 0);
|
||||
|
||||
QString sequence = actionSequence;
|
||||
sequence.replace("{{}", "{LEFTBRACE}");
|
||||
|
@ -69,7 +69,7 @@ signals:
|
||||
|
||||
private slots:
|
||||
void performAutoTypeFromGlobal(AutoTypeMatch match);
|
||||
void resetInAutoType();
|
||||
void autoTypeRejectedFromGlobal();
|
||||
void unloadPlugin();
|
||||
|
||||
private:
|
||||
@ -88,6 +88,7 @@ private:
|
||||
bool windowMatches(const QString& windowTitle, const QString& windowPattern);
|
||||
|
||||
QMutex m_inAutoType;
|
||||
QMutex m_inGlobalAutoTypeDialog;
|
||||
int m_autoTypeDelay;
|
||||
Qt::Key m_currentGlobalKey;
|
||||
Qt::KeyboardModifiers m_currentGlobalModifiers;
|
||||
|
@ -33,7 +33,6 @@ public:
|
||||
virtual bool registerGlobalShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers) = 0;
|
||||
virtual void unregisterGlobalShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers) = 0;
|
||||
virtual int platformEventFilter(void* event) = 0;
|
||||
virtual int initialTimeout() = 0;
|
||||
virtual bool raiseWindow(WId window) = 0;
|
||||
virtual void unload() {}
|
||||
|
||||
|
@ -154,11 +154,6 @@ AutoTypeExecutor* AutoTypePlatformMac::createExecutor()
|
||||
return new AutoTypeExecutorMac(this);
|
||||
}
|
||||
|
||||
int AutoTypePlatformMac::initialTimeout()
|
||||
{
|
||||
return 500;
|
||||
}
|
||||
|
||||
//
|
||||
// Activate window by process id
|
||||
//
|
||||
|
@ -42,7 +42,6 @@ public:
|
||||
bool registerGlobalShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers) override;
|
||||
void unregisterGlobalShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers) override;
|
||||
int platformEventFilter(void* event) override;
|
||||
int initialTimeout() override;
|
||||
bool raiseWindow(WId pid) override;
|
||||
AutoTypeExecutor* createExecutor() override;
|
||||
|
||||
|
@ -103,11 +103,6 @@ void AutoTypePlatformTest::addActionKey(AutoTypeKey* action)
|
||||
m_actionChars.append(keyToString(action->key));
|
||||
}
|
||||
|
||||
int AutoTypePlatformTest::initialTimeout()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool AutoTypePlatformTest::raiseWindow(WId window)
|
||||
{
|
||||
Q_UNUSED(window);
|
||||
|
@ -42,7 +42,6 @@ public:
|
||||
bool registerGlobalShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers) override;
|
||||
void unregisterGlobalShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers) override;
|
||||
int platformEventFilter(void* event) override;
|
||||
int initialTimeout() override;
|
||||
bool raiseWindow(WId window) override;
|
||||
AutoTypeExecutor* createExecutor() override;
|
||||
|
||||
|
@ -109,11 +109,6 @@ AutoTypeExecutor* AutoTypePlatformWin::createExecutor()
|
||||
return new AutoTypeExecutorWin(this);
|
||||
}
|
||||
|
||||
int AutoTypePlatformWin::initialTimeout()
|
||||
{
|
||||
return 500;
|
||||
}
|
||||
|
||||
//
|
||||
// Set foreground window
|
||||
//
|
||||
|
@ -39,7 +39,6 @@ public:
|
||||
bool registerGlobalShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers) override;
|
||||
void unregisterGlobalShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers) override;
|
||||
int platformEventFilter(void* event) override;
|
||||
int initialTimeout() override;
|
||||
bool raiseWindow(WId window) override;
|
||||
AutoTypeExecutor* createExecutor() override;
|
||||
|
||||
|
@ -844,12 +844,6 @@ void AutoTypeExecutorX11::execClearField(AutoTypeClearField* action = nullptr)
|
||||
nanosleep(&ts, nullptr);
|
||||
}
|
||||
|
||||
|
||||
int AutoTypePlatformX11::initialTimeout()
|
||||
{
|
||||
return 500;
|
||||
}
|
||||
|
||||
bool AutoTypePlatformX11::raiseWindow(WId window)
|
||||
{
|
||||
if (m_atomNetActiveWindow == None) {
|
||||
|
@ -51,7 +51,6 @@ public:
|
||||
bool registerGlobalShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers) override;
|
||||
void unregisterGlobalShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers) override;
|
||||
int platformEventFilter(void* event) override;
|
||||
int initialTimeout() override;
|
||||
bool raiseWindow(WId window) override;
|
||||
AutoTypeExecutor* createExecutor() override;
|
||||
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include <QJsonParseError>
|
||||
#include "BrowserAction.h"
|
||||
#include "BrowserSettings.h"
|
||||
#include "NativeMessagingBase.h"
|
||||
#include "sodium.h"
|
||||
#include "sodium/crypto_box.h"
|
||||
#include "sodium/randombytes.h"
|
||||
@ -239,9 +240,17 @@ QJsonObject BrowserAction::handleGetLogins(const QJsonObject& json, const QStrin
|
||||
return getErrorReply(action, ERROR_KEEPASS_NO_URL_PROVIDED);
|
||||
}
|
||||
|
||||
const QJsonArray keys = decrypted.value("keys").toArray();
|
||||
|
||||
StringPairList keyList;
|
||||
for (const QJsonValue val : keys) {
|
||||
const QJsonObject keyObject = val.toObject();
|
||||
keyList.push_back(qMakePair(keyObject.value("id").toString(), keyObject.value("key").toString()));
|
||||
}
|
||||
|
||||
const QString id = decrypted.value("id").toString();
|
||||
const QString submit = decrypted.value("submitUrl").toString();
|
||||
const QJsonArray users = m_browserService.findMatchingEntries(id, url, submit, "");
|
||||
const QJsonArray users = m_browserService.findMatchingEntries(id, url, submit, "", keyList);
|
||||
|
||||
if (users.isEmpty()) {
|
||||
return getErrorReply(action, ERROR_KEEPASS_NO_LOGINS_FOUND);
|
||||
@ -456,7 +465,7 @@ QString BrowserAction::encrypt(const QString plaintext, const QString nonce)
|
||||
std::vector<unsigned char> sk(sa.cbegin(), sa.cend());
|
||||
|
||||
std::vector<unsigned char> e;
|
||||
e.resize(max_length);
|
||||
e.resize(NATIVE_MSG_MAX_LENGTH);
|
||||
|
||||
if (m.empty() || n.empty() || ck.empty() || sk.empty()) {
|
||||
return QString();
|
||||
@ -484,7 +493,7 @@ QByteArray BrowserAction::decrypt(const QString encrypted, const QString nonce)
|
||||
std::vector<unsigned char> sk(sa.cbegin(), sa.cend());
|
||||
|
||||
std::vector<unsigned char> d;
|
||||
d.resize(max_length);
|
||||
d.resize(NATIVE_MSG_MAX_LENGTH);
|
||||
|
||||
if (m.empty() || n.empty() || ck.empty() || sk.empty()) {
|
||||
return QByteArray();
|
||||
|
@ -67,7 +67,6 @@ void BrowserOptionDialog::loadSettings()
|
||||
// hide unimplemented options
|
||||
// TODO: fix this
|
||||
m_ui->showNotification->hide();
|
||||
m_ui->bestMatchOnly->hide();
|
||||
|
||||
if (settings.sortByUsername()) {
|
||||
m_ui->sortByUsername->setChecked(true);
|
||||
|
@ -47,7 +47,8 @@ static int KEEPASSXCBROWSER_DEFAULT_ICON = 1;
|
||||
|
||||
BrowserService::BrowserService(DatabaseTabWidget* parent) :
|
||||
m_dbTabWidget(parent),
|
||||
m_dialogActive(false)
|
||||
m_dialogActive(false),
|
||||
m_bringToFrontRequested(false)
|
||||
{
|
||||
connect(m_dbTabWidget, SIGNAL(databaseLocked(DatabaseWidget*)), this, SLOT(databaseLocked(DatabaseWidget*)));
|
||||
connect(m_dbTabWidget, SIGNAL(databaseUnlocked(DatabaseWidget*)), this, SLOT(databaseUnlocked(DatabaseWidget*)));
|
||||
@ -82,6 +83,7 @@ bool BrowserService::openDatabase(bool triggerUnlock)
|
||||
|
||||
if (triggerUnlock) {
|
||||
KEEPASSXC_MAIN_WINDOW->bringToFront();
|
||||
m_bringToFrontRequested = true;
|
||||
}
|
||||
|
||||
return false;
|
||||
@ -224,7 +226,7 @@ QString BrowserService::getKey(const QString& id)
|
||||
return config->attributes()->value(QLatin1String(ASSOCIATE_KEY_PREFIX) + id);
|
||||
}
|
||||
|
||||
QJsonArray BrowserService::findMatchingEntries(const QString& id, const QString& url, const QString& submitUrl, const QString& realm)
|
||||
QJsonArray BrowserService::findMatchingEntries(const QString& id, const QString& url, const QString& submitUrl, const QString& realm, const StringPairList& keyList)
|
||||
{
|
||||
QJsonArray result;
|
||||
if (thread() != QThread::currentThread()) {
|
||||
@ -233,7 +235,8 @@ QJsonArray BrowserService::findMatchingEntries(const QString& id, const QString&
|
||||
Q_ARG(const QString&, id),
|
||||
Q_ARG(const QString&, url),
|
||||
Q_ARG(const QString&, submitUrl),
|
||||
Q_ARG(const QString&, realm));
|
||||
Q_ARG(const QString&, realm),
|
||||
Q_ARG(const StringPairList&, keyList));
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -244,7 +247,7 @@ QJsonArray BrowserService::findMatchingEntries(const QString& id, const QString&
|
||||
// Check entries for authorization
|
||||
QList<Entry*> pwEntriesToConfirm;
|
||||
QList<Entry*> pwEntries;
|
||||
for (Entry* entry : searchEntries(url)) {
|
||||
for (Entry* entry : searchEntries(url, keyList)) {
|
||||
switch (checkAccess(entry, host, submitHost, realm)) {
|
||||
case Denied:
|
||||
continue;
|
||||
@ -340,12 +343,18 @@ void BrowserService::updateEntry(const QString& id, const QString& uuid, const Q
|
||||
}
|
||||
|
||||
if (username.compare(login, Qt::CaseSensitive) != 0 || entry->password().compare(password, Qt::CaseSensitive) != 0) {
|
||||
QMessageBox::StandardButton dialogResult = QMessageBox::No;
|
||||
int dialogResult = QMessageBox::No;
|
||||
if (!BrowserSettings::alwaysAllowUpdate()) {
|
||||
dialogResult = QMessageBox::warning(0, tr("KeePassXC: Update Entry"),
|
||||
tr("Do you want to update the information in %1 - %2?")
|
||||
.arg(QUrl(url).host()).arg(username),
|
||||
QMessageBox::Yes|QMessageBox::No);
|
||||
QMessageBox msgBox;
|
||||
msgBox.setWindowTitle(tr("KeePassXC: Update Entry"));
|
||||
msgBox.setText(tr("Do you want to update the information in %1 - %2?").arg(QUrl(url).host()).arg(username));
|
||||
msgBox.setStandardButtons(QMessageBox::Yes);
|
||||
msgBox.addButton(QMessageBox::No);
|
||||
msgBox.setDefaultButton(QMessageBox::No);
|
||||
msgBox.setWindowFlags(Qt::WindowStaysOnTopHint);
|
||||
msgBox.activateWindow();
|
||||
msgBox.raise();
|
||||
dialogResult = msgBox.exec();
|
||||
}
|
||||
|
||||
if (BrowserSettings::alwaysAllowUpdate() || dialogResult == QMessageBox::Yes) {
|
||||
@ -381,7 +390,7 @@ QList<Entry*> BrowserService::searchEntries(Database* db, const QString& hostnam
|
||||
return entries;
|
||||
}
|
||||
|
||||
QList<Entry*> BrowserService::searchEntries(const QString& text)
|
||||
QList<Entry*> BrowserService::searchEntries(const QString& text, const StringPairList& keyList)
|
||||
{
|
||||
// Get the list of databases to search
|
||||
QList<Database*> databases;
|
||||
@ -390,10 +399,19 @@ QList<Entry*> BrowserService::searchEntries(const QString& text)
|
||||
for (int i = 0; i < count; ++i) {
|
||||
if (DatabaseWidget* dbWidget = qobject_cast<DatabaseWidget*>(m_dbTabWidget->widget(i))) {
|
||||
if (Database* db = dbWidget->database()) {
|
||||
// Check if database is connected with KeePassXC-Browser
|
||||
for (const StringPair keyPair : keyList) {
|
||||
Entry* entry = db->resolveEntry(KEEPASSXCBROWSER_UUID);
|
||||
if (entry) {
|
||||
QString key = entry->attributes()->value(QLatin1String(ASSOCIATE_KEY_PREFIX) + keyPair.first);
|
||||
if (!key.isEmpty() && keyPair.second == key) {
|
||||
databases << db;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (Database* db = getDatabase()) {
|
||||
databases << db;
|
||||
}
|
||||
@ -512,21 +530,30 @@ QList<Entry*> BrowserService::sortEntries(QList<Entry*>& pwEntries, const QStrin
|
||||
const QString submitUrl = url.toString(QUrl::StripTrailingSlash);
|
||||
const QString baseSubmitUrl = url.toString(QUrl::StripTrailingSlash | QUrl::RemovePath | QUrl::RemoveQuery | QUrl::RemoveFragment);
|
||||
|
||||
QMultiMap<int, const Entry*> priorities;
|
||||
for (const Entry* entry : pwEntries) {
|
||||
// Build map of prioritized entries
|
||||
QMultiMap<int, Entry*> priorities;
|
||||
for (Entry* entry : pwEntries) {
|
||||
priorities.insert(sortPriority(entry, host, submitUrl, baseSubmitUrl), entry);
|
||||
}
|
||||
|
||||
QList<Entry*> results;
|
||||
QString field = BrowserSettings::sortByTitle() ? "Title" : "UserName";
|
||||
std::sort(pwEntries.begin(), pwEntries.end(), [&priorities, &field](const Entry* left, const Entry* right) {
|
||||
int res = priorities.key(left) - priorities.key(right);
|
||||
if (res == 0) {
|
||||
for (int i = 100; i >= 0; i -= 5) {
|
||||
if (priorities.count(i) > 0) {
|
||||
// Sort same priority entries by Title or UserName
|
||||
auto entries = priorities.values(i);
|
||||
std::sort(entries.begin(), entries.end(), [&priorities, &field](Entry* left, Entry* right) {
|
||||
return QString::localeAwareCompare(left->attributes()->value(field), right->attributes()->value(field)) < 0;
|
||||
}
|
||||
return res < 0;
|
||||
});
|
||||
results << entries;
|
||||
if (BrowserSettings::bestMatchOnly() && !pwEntries.isEmpty()) {
|
||||
// Early out once we find the highest batch of matches
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return pwEntries;
|
||||
return results;
|
||||
}
|
||||
|
||||
bool BrowserService::confirmEntries(QList<Entry*>& pwEntriesToConfirm, const QString& url, const QString& host, const QString& submitHost, const QString& realm)
|
||||
@ -727,6 +754,10 @@ void BrowserService::databaseLocked(DatabaseWidget* dbWidget)
|
||||
void BrowserService::databaseUnlocked(DatabaseWidget* dbWidget)
|
||||
{
|
||||
if (dbWidget) {
|
||||
if (m_bringToFrontRequested) {
|
||||
KEEPASSXC_MAIN_WINDOW->lower();
|
||||
m_bringToFrontRequested = false;
|
||||
}
|
||||
emit databaseUnlocked();
|
||||
}
|
||||
}
|
||||
|
@ -25,7 +25,8 @@
|
||||
#include "gui/DatabaseTabWidget.h"
|
||||
#include "core/Entry.h"
|
||||
|
||||
enum { max_length = 16*1024 };
|
||||
typedef QPair<QString, QString> StringPair;
|
||||
typedef QList<StringPair> StringPairList;
|
||||
|
||||
class BrowserService : public QObject
|
||||
{
|
||||
@ -42,12 +43,12 @@ public:
|
||||
QString getKey(const QString& id);
|
||||
void addEntry(const QString& id, const QString& login, const QString& password, const QString& url, const QString& submitUrl, const QString& realm);
|
||||
QList<Entry*> searchEntries(Database* db, const QString& hostname);
|
||||
QList<Entry*> searchEntries(const QString& text);
|
||||
QList<Entry*> searchEntries(const QString& text, const StringPairList& keyList);
|
||||
void removeSharedEncryptionKeys();
|
||||
void removeStoredPermissions();
|
||||
|
||||
public slots:
|
||||
QJsonArray findMatchingEntries(const QString& id, const QString& url, const QString& submitUrl, const QString& realm);
|
||||
QJsonArray findMatchingEntries(const QString& id, const QString& url, const QString& submitUrl, const QString& realm, const StringPairList& keyList);
|
||||
QString storeKey(const QString& key);
|
||||
void updateEntry(const QString& id, const QString& uuid, const QString& login, const QString& password, const QString& url);
|
||||
void databaseLocked(DatabaseWidget* dbWidget);
|
||||
@ -77,6 +78,7 @@ private:
|
||||
private:
|
||||
DatabaseTabWidget* const m_dbTabWidget;
|
||||
bool m_dialogActive;
|
||||
bool m_bringToFrontRequested;
|
||||
};
|
||||
|
||||
#endif // BROWSERSERVICE_H
|
||||
|
@ -32,7 +32,7 @@ public:
|
||||
|
||||
static bool showNotification(); //TODO!!
|
||||
static void setShowNotification(bool showNotification);
|
||||
static bool bestMatchOnly(); //TODO!!
|
||||
static bool bestMatchOnly();
|
||||
static void setBestMatchOnly(bool bestMatchOnly);
|
||||
static bool unlockDatabase();
|
||||
static void setUnlockDatabase(bool unlockDatabase);
|
||||
@ -46,7 +46,7 @@ public:
|
||||
static void setAlwaysAllowAccess(bool alwaysAllowAccess);
|
||||
static bool alwaysAllowUpdate();
|
||||
static void setAlwaysAllowUpdate(bool alwaysAllowUpdate);
|
||||
static bool searchInAllDatabases();//TODO!!
|
||||
static bool searchInAllDatabases();
|
||||
static void setSearchInAllDatabases(bool searchInAllDatabases);
|
||||
static bool supportKphFields();
|
||||
static void setSupportKphFields(bool supportKphFields);
|
||||
|
@ -36,14 +36,17 @@
|
||||
#include <io.h>
|
||||
#endif
|
||||
|
||||
NativeMessagingBase::NativeMessagingBase()
|
||||
NativeMessagingBase::NativeMessagingBase(const bool enabled)
|
||||
{
|
||||
#ifdef Q_OS_WIN
|
||||
Q_UNUSED(enabled);
|
||||
_setmode(_fileno(stdin), _O_BINARY);
|
||||
_setmode(_fileno(stdout), _O_BINARY);
|
||||
#else
|
||||
if (enabled) {
|
||||
m_notifier.reset(new QSocketNotifier(fileno(stdin), QSocketNotifier::Read, this));
|
||||
connect(m_notifier.data(), SIGNAL(activated(int)), this, SLOT(newNativeMessage()));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -121,7 +124,8 @@ void NativeMessagingBase::sendReply(const QJsonObject& json)
|
||||
void NativeMessagingBase::sendReply(const QString& reply)
|
||||
{
|
||||
if (!reply.isEmpty()) {
|
||||
uint len = reply.length();
|
||||
QByteArray bytes = reply.toUtf8();
|
||||
uint len = bytes.size();
|
||||
std::cout << char(((len>>0) & 0xFF)) << char(((len>>8) & 0xFF)) << char(((len>>16) & 0xFF)) << char(((len>>24) & 0xFF));
|
||||
std::cout << reply.toStdString() << std::flush;
|
||||
}
|
||||
|
@ -32,12 +32,19 @@
|
||||
#include <iostream>
|
||||
#include <unistd.h>
|
||||
|
||||
#ifndef Q_OS_WIN
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#endif
|
||||
|
||||
static const int NATIVE_MSG_MAX_LENGTH = 1024*1024;
|
||||
|
||||
class NativeMessagingBase : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit NativeMessagingBase();
|
||||
explicit NativeMessagingBase(const bool enabled);
|
||||
~NativeMessagingBase() = default;
|
||||
|
||||
protected slots:
|
||||
|
@ -23,8 +23,12 @@
|
||||
#include "NativeMessagingHost.h"
|
||||
#include "BrowserSettings.h"
|
||||
|
||||
NativeMessagingHost::NativeMessagingHost(DatabaseTabWidget* parent) :
|
||||
NativeMessagingBase(),
|
||||
#ifdef Q_OS_WIN
|
||||
#include <Winsock2.h>
|
||||
#endif
|
||||
|
||||
NativeMessagingHost::NativeMessagingHost(DatabaseTabWidget* parent, const bool enabled) :
|
||||
NativeMessagingBase(enabled),
|
||||
m_mutex(QMutex::Recursive),
|
||||
m_browserClients(m_browserService),
|
||||
m_browserService(parent)
|
||||
@ -73,6 +77,11 @@ void NativeMessagingHost::run()
|
||||
QString serverPath = getLocalServerPath();
|
||||
QFile::remove(serverPath);
|
||||
|
||||
// Ensure that STDIN is not being listened when proxy is used
|
||||
if (m_notifier->isEnabled()) {
|
||||
m_notifier->setEnabled(false);
|
||||
}
|
||||
|
||||
if (m_localServer->isListening()) {
|
||||
m_localServer->close();
|
||||
}
|
||||
@ -142,11 +151,17 @@ void NativeMessagingHost::newLocalConnection()
|
||||
void NativeMessagingHost::newLocalMessage()
|
||||
{
|
||||
QLocalSocket* socket = qobject_cast<QLocalSocket*>(QObject::sender());
|
||||
|
||||
if (!socket || socket->bytesAvailable() <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
socket->setReadBufferSize(NATIVE_MSG_MAX_LENGTH);
|
||||
int socketDesc = socket->socketDescriptor();
|
||||
if (socketDesc) {
|
||||
int max = NATIVE_MSG_MAX_LENGTH;
|
||||
setsockopt(socketDesc, SOL_SOCKET, SO_SNDBUF, reinterpret_cast<char*>(&max), sizeof(max));
|
||||
}
|
||||
|
||||
QByteArray arr = socket->readAll();
|
||||
if (arr.isEmpty()) {
|
||||
return;
|
||||
|
@ -31,7 +31,7 @@ class NativeMessagingHost : public NativeMessagingBase
|
||||
typedef QList<QLocalSocket*> SocketList;
|
||||
|
||||
public:
|
||||
explicit NativeMessagingHost(DatabaseTabWidget* parent = 0);
|
||||
explicit NativeMessagingHost(DatabaseTabWidget* parent = 0, const bool enabled = false);
|
||||
~NativeMessagingHost();
|
||||
int init();
|
||||
void run();
|
||||
|
@ -135,6 +135,7 @@ void Config::init(const QString& fileName)
|
||||
m_defaults.insert("AutoTypeEntryTitleMatch", true);
|
||||
m_defaults.insert("AutoTypeEntryURLMatch", true);
|
||||
m_defaults.insert("AutoTypeDelay", 25);
|
||||
m_defaults.insert("AutoTypeStartDelay", 500);
|
||||
m_defaults.insert("UseGroupIconOnEntryCreation", true);
|
||||
m_defaults.insert("IgnoreGroupExpansion", true);
|
||||
m_defaults.insert("security/clearclipboard", true);
|
||||
|
@ -97,6 +97,15 @@ QString FilePath::wordlistPath(const QString& name)
|
||||
}
|
||||
|
||||
QIcon FilePath::applicationIcon()
|
||||
{
|
||||
#ifdef KEEPASSXC_DIST_SNAP
|
||||
return icon("apps", "keepassxc", false);
|
||||
#else
|
||||
return icon("apps", "keepassxc");
|
||||
#endif
|
||||
}
|
||||
|
||||
QIcon FilePath::trayIcon()
|
||||
{
|
||||
bool darkIcon = useDarkIcon();
|
||||
|
||||
@ -107,7 +116,6 @@ QIcon FilePath::applicationIcon()
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
QIcon FilePath::trayIconLocked()
|
||||
{
|
||||
#ifdef KEEPASSXC_DIST_SNAP
|
||||
|
@ -29,6 +29,7 @@ public:
|
||||
QString pluginPath(const QString& name);
|
||||
QString wordlistPath(const QString& name);
|
||||
QIcon applicationIcon();
|
||||
QIcon trayIcon();
|
||||
QIcon trayIconLocked();
|
||||
QIcon trayIconUnlocked();
|
||||
QIcon icon(const QString& category, const QString& name, bool fromTheme = true);
|
||||
|
@ -173,6 +173,9 @@ void DatabaseOpenWidget::openDatabase()
|
||||
return;
|
||||
}
|
||||
|
||||
m_ui->editPassword->setShowPassword(false);
|
||||
QCoreApplication::processEvents();
|
||||
|
||||
QFile file(m_filename);
|
||||
if (!file.open(QIODevice::ReadOnly)) {
|
||||
m_ui->messageWidget->showMessage(
|
||||
|
@ -198,7 +198,7 @@ void DetailsWidget::updateEntryNotesTab()
|
||||
Q_ASSERT(m_currentEntry);
|
||||
const QString notes = m_currentEntry->notes();
|
||||
setTabEnabled(m_ui->entryTabWidget, m_ui->entryNotesTab, !notes.isEmpty());
|
||||
m_ui->entryNotesEdit->setText(m_currentEntry->resolveMultiplePlaceholders(notes));
|
||||
m_ui->entryNotesEdit->setText(notes);
|
||||
}
|
||||
|
||||
void DetailsWidget::updateEntryAttributesTab()
|
||||
|
@ -21,7 +21,6 @@
|
||||
|
||||
#include <QFileDialog>
|
||||
#include <QMessageBox>
|
||||
#include <QFileDialog>
|
||||
|
||||
#include "core/Config.h"
|
||||
#include "core/Group.h"
|
||||
@ -31,9 +30,7 @@
|
||||
#include "gui/MessageBox.h"
|
||||
|
||||
#ifdef WITH_XC_NETWORKING
|
||||
#include <curl/curl.h>
|
||||
#include "core/AsyncTask.h"
|
||||
#undef MessageBox
|
||||
#include <QtNetwork>
|
||||
#endif
|
||||
|
||||
IconStruct::IconStruct()
|
||||
@ -42,10 +39,31 @@ IconStruct::IconStruct()
|
||||
{
|
||||
}
|
||||
|
||||
UrlFetchProgressDialog::UrlFetchProgressDialog(const QUrl &url, QWidget *parent)
|
||||
: QProgressDialog(parent)
|
||||
{
|
||||
setWindowTitle(tr("Download Progress"));
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
setLabelText(tr("Downloading %1.").arg(url.toDisplayString()));
|
||||
setMinimum(0);
|
||||
setValue(0);
|
||||
setMinimumDuration(0);
|
||||
setMinimumSize(QSize(400, 75));
|
||||
}
|
||||
|
||||
void UrlFetchProgressDialog::networkReplyProgress(qint64 bytesRead, qint64 totalBytes)
|
||||
{
|
||||
setMaximum(totalBytes);
|
||||
setValue(bytesRead);
|
||||
}
|
||||
|
||||
EditWidgetIcons::EditWidgetIcons(QWidget* parent)
|
||||
: QWidget(parent)
|
||||
, m_ui(new Ui::EditWidgetIcons())
|
||||
, m_database(nullptr)
|
||||
#ifdef WITH_XC_NETWORKING
|
||||
, m_reply(nullptr)
|
||||
#endif
|
||||
, m_defaultIconModel(new DefaultIconModel(this))
|
||||
, m_customIconModel(new CustomIconModel(this))
|
||||
{
|
||||
@ -136,7 +154,7 @@ void EditWidgetIcons::load(const Uuid& currentUuid, Database* database, const Ic
|
||||
void EditWidgetIcons::setUrl(const QString& url)
|
||||
{
|
||||
#ifdef WITH_XC_NETWORKING
|
||||
m_url = url;
|
||||
m_url = QUrl(url);
|
||||
m_ui->faviconButton->setVisible(!url.isEmpty());
|
||||
#else
|
||||
Q_UNUSED(url);
|
||||
@ -144,87 +162,152 @@ void EditWidgetIcons::setUrl(const QString& url)
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef WITH_XC_NETWORKING
|
||||
namespace {
|
||||
// Try to get the 2nd level domain of the host part of a QUrl. For example,
|
||||
// "foo.bar.example.com" would become "example.com", and "foo.bar.example.co.uk"
|
||||
// would become "example.co.uk".
|
||||
QString getSecondLevelDomain(QUrl url)
|
||||
{
|
||||
QString fqdn = url.host();
|
||||
fqdn.truncate(fqdn.length() - url.topLevelDomain().length());
|
||||
QStringList parts = fqdn.split('.');
|
||||
QString newdom = parts.takeLast() + url.topLevelDomain();
|
||||
return newdom;
|
||||
}
|
||||
|
||||
QUrl convertVariantToUrl(QVariant var)
|
||||
{
|
||||
QUrl url;
|
||||
if (var.canConvert<QUrl>())
|
||||
url = var.value<QUrl>();
|
||||
return url;
|
||||
}
|
||||
|
||||
QUrl getRedirectTarget(QNetworkReply *reply)
|
||||
{
|
||||
QVariant var = reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
|
||||
QUrl url = convertVariantToUrl(var);
|
||||
return url;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void EditWidgetIcons::downloadFavicon()
|
||||
{
|
||||
#ifdef WITH_XC_NETWORKING
|
||||
m_ui->faviconButton->setDisabled(true);
|
||||
|
||||
QUrl url = QUrl(m_url);
|
||||
url.setPath("/favicon.ico");
|
||||
m_redirects = 0;
|
||||
m_urlsToTry.clear();
|
||||
|
||||
QString fullyQualifiedDomain = m_url.host();
|
||||
QString secondLevelDomain = getSecondLevelDomain(m_url);
|
||||
|
||||
// Attempt to simply load the favicon.ico file
|
||||
QImage image = fetchFavicon(url);
|
||||
if (!image.isNull()) {
|
||||
addCustomIcon(image);
|
||||
} else if (config()->get("security/IconDownloadFallbackToGoogle", false).toBool()) {
|
||||
QUrl faviconUrl = QUrl("https://www.google.com/s2/favicons");
|
||||
faviconUrl.setQuery("domain=" + QUrl::toPercentEncoding(url.host()));
|
||||
// Attempt to load favicon from Google
|
||||
image = fetchFavicon(faviconUrl);
|
||||
if (!image.isNull()) {
|
||||
addCustomIcon(image);
|
||||
} else {
|
||||
emit messageEditEntry(tr("Unable to fetch favicon."), MessageWidget::Error);
|
||||
if (fullyQualifiedDomain != secondLevelDomain) {
|
||||
m_urlsToTry.append(QUrl(m_url.scheme() + "://" + fullyQualifiedDomain + "/favicon.ico"));
|
||||
}
|
||||
m_urlsToTry.append(QUrl(m_url.scheme() + "://" + secondLevelDomain + "/favicon.ico"));
|
||||
|
||||
// Try to use Google fallback, if enabled
|
||||
if (config()->get("security/IconDownloadFallbackToGoogle", false).toBool()) {
|
||||
QUrl urlGoogle = QUrl("https://www.google.com/s2/favicons");
|
||||
|
||||
urlGoogle.setQuery("domain=" + QUrl::toPercentEncoding(secondLevelDomain));
|
||||
m_urlsToTry.append(urlGoogle);
|
||||
}
|
||||
|
||||
startFetchFavicon(m_urlsToTry.takeFirst());
|
||||
#endif
|
||||
}
|
||||
|
||||
void EditWidgetIcons::fetchReadyRead()
|
||||
{
|
||||
#ifdef WITH_XC_NETWORKING
|
||||
m_bytesReceived += m_reply->readAll();
|
||||
#endif
|
||||
}
|
||||
|
||||
void EditWidgetIcons::fetchFinished()
|
||||
{
|
||||
#ifdef WITH_XC_NETWORKING
|
||||
QImage image;
|
||||
bool googleFallbackEnabled = config()->get("security/IconDownloadFallbackToGoogle", false).toBool();
|
||||
bool error = (m_reply->error() != QNetworkReply::NoError);
|
||||
QUrl redirectTarget = getRedirectTarget(m_reply);
|
||||
|
||||
m_reply->deleteLater();
|
||||
m_reply = nullptr;
|
||||
|
||||
if (!error) {
|
||||
if (redirectTarget.isValid()) {
|
||||
// Redirected, we need to follow it, or fall through if we have
|
||||
// done too many redirects already.
|
||||
if (m_redirects < 5) {
|
||||
m_redirects++;
|
||||
if (redirectTarget.isRelative())
|
||||
redirectTarget = m_fetchUrl.resolved(redirectTarget);
|
||||
startFetchFavicon(redirectTarget);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// No redirect, and we theoretically have some icon data now.
|
||||
image.loadFromData(m_bytesReceived);
|
||||
}
|
||||
}
|
||||
|
||||
if (!image.isNull()) {
|
||||
addCustomIcon(image);
|
||||
} else if (!m_urlsToTry.empty()) {
|
||||
m_redirects = 0;
|
||||
startFetchFavicon(m_urlsToTry.takeFirst());
|
||||
return;
|
||||
} else {
|
||||
if (!googleFallbackEnabled) {
|
||||
emit messageEditEntry(tr("Unable to fetch favicon.") + "\n" +
|
||||
tr("Hint: You can enable Google as a fallback under Tools>Settings>Security"),
|
||||
MessageWidget::Error);
|
||||
} else {
|
||||
emit messageEditEntry(tr("Unable to fetch favicon."), MessageWidget::Error);
|
||||
}
|
||||
}
|
||||
|
||||
m_ui->faviconButton->setDisabled(false);
|
||||
#endif
|
||||
}
|
||||
|
||||
void EditWidgetIcons::fetchCanceled()
|
||||
{
|
||||
#ifdef WITH_XC_NETWORKING
|
||||
namespace {
|
||||
std::size_t writeCurlResponse(char* ptr, std::size_t size, std::size_t nmemb, void* data)
|
||||
{
|
||||
QByteArray* response = static_cast<QByteArray*>(data);
|
||||
std::size_t realsize = size * nmemb;
|
||||
response->append(ptr, realsize);
|
||||
return realsize;
|
||||
}
|
||||
}
|
||||
|
||||
QImage EditWidgetIcons::fetchFavicon(const QUrl& url)
|
||||
{
|
||||
QImage image;
|
||||
CURL* curl = curl_easy_init();
|
||||
if (curl) {
|
||||
QByteArray imagedata;
|
||||
QByteArray baUrl = url.url().toLatin1();
|
||||
|
||||
curl_easy_setopt(curl, CURLOPT_URL, baUrl.data());
|
||||
curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 5L);
|
||||
curl_easy_setopt(curl, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
|
||||
curl_easy_setopt(curl, CURLOPT_USERAGENT, "curl");
|
||||
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
|
||||
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 10L);
|
||||
curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1L);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &imagedata);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &writeCurlResponse);
|
||||
#ifdef Q_OS_WIN
|
||||
const QDir appDir = QFileInfo(QCoreApplication::applicationFilePath()).absoluteDir();
|
||||
if (appDir.exists("ssl\\certs")) {
|
||||
curl_easy_setopt(curl, CURLOPT_CAINFO, (appDir.absolutePath() + "\\ssl\\certs\\ca-bundle.crt").toLatin1().data());
|
||||
}
|
||||
m_reply->abort();
|
||||
#endif
|
||||
|
||||
// Perform the request in another thread
|
||||
CURLcode result = AsyncTask::runAndWaitForFuture([curl]() {
|
||||
return curl_easy_perform(curl);
|
||||
});
|
||||
|
||||
if (result == CURLE_OK) {
|
||||
image.loadFromData(imagedata);
|
||||
}
|
||||
|
||||
curl_easy_cleanup(curl);
|
||||
}
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
void EditWidgetIcons::startFetchFavicon(const QUrl& url)
|
||||
{
|
||||
#ifdef WITH_XC_NETWORKING
|
||||
m_bytesReceived.clear();
|
||||
|
||||
m_fetchUrl = url;
|
||||
|
||||
QNetworkRequest request(url);
|
||||
|
||||
m_reply = m_netMgr.get(request);
|
||||
connect(m_reply, &QNetworkReply::finished, this, &EditWidgetIcons::fetchFinished);
|
||||
connect(m_reply, &QIODevice::readyRead, this, &EditWidgetIcons::fetchReadyRead);
|
||||
|
||||
UrlFetchProgressDialog *progress = new UrlFetchProgressDialog(url, this);
|
||||
progress->setAttribute(Qt::WA_DeleteOnClose);
|
||||
connect(m_reply, &QNetworkReply::finished, progress, &QProgressDialog::hide);
|
||||
connect(m_reply, &QNetworkReply::downloadProgress, progress, &UrlFetchProgressDialog::networkReplyProgress);
|
||||
connect(progress, &QProgressDialog::canceled, this, &EditWidgetIcons::fetchCanceled);
|
||||
|
||||
progress->show();
|
||||
#else
|
||||
Q_UNUSED(url);
|
||||
#endif
|
||||
}
|
||||
|
||||
void EditWidgetIcons::addCustomIconFromFile()
|
||||
{
|
||||
|
@ -21,7 +21,9 @@
|
||||
|
||||
#include <QWidget>
|
||||
#include <QSet>
|
||||
#include <QProgressDialog>
|
||||
#include <QUrl>
|
||||
#include <QNetworkAccessManager>
|
||||
|
||||
#include "config-keepassx.h"
|
||||
#include "core/Global.h"
|
||||
@ -31,6 +33,9 @@
|
||||
class Database;
|
||||
class DefaultIconModel;
|
||||
class CustomIconModel;
|
||||
#ifdef WITH_XC_NETWORKING
|
||||
class QNetworkReply;
|
||||
#endif
|
||||
|
||||
namespace Ui {
|
||||
class EditWidgetIcons;
|
||||
@ -44,6 +49,17 @@ struct IconStruct
|
||||
int number;
|
||||
};
|
||||
|
||||
class UrlFetchProgressDialog : public QProgressDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit UrlFetchProgressDialog(const QUrl &url, QWidget *parent = nullptr);
|
||||
|
||||
public slots:
|
||||
void networkReplyProgress(qint64 bytesRead, qint64 totalBytes);
|
||||
};
|
||||
|
||||
class EditWidgetIcons : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
@ -65,9 +81,10 @@ signals:
|
||||
|
||||
private slots:
|
||||
void downloadFavicon();
|
||||
#ifdef WITH_XC_NETWORKING
|
||||
QImage fetchFavicon(const QUrl& url);
|
||||
#endif
|
||||
void startFetchFavicon(const QUrl& url);
|
||||
void fetchFinished();
|
||||
void fetchReadyRead();
|
||||
void fetchCanceled();
|
||||
void addCustomIconFromFile();
|
||||
void addCustomIcon(const QImage& icon);
|
||||
void removeCustomIcon();
|
||||
@ -80,7 +97,15 @@ private:
|
||||
const QScopedPointer<Ui::EditWidgetIcons> m_ui;
|
||||
Database* m_database;
|
||||
Uuid m_currentUuid;
|
||||
QString m_url;
|
||||
#ifdef WITH_XC_NETWORKING
|
||||
QUrl m_url;
|
||||
QUrl m_fetchUrl;
|
||||
QList<QUrl> m_urlsToTry;
|
||||
QByteArray m_bytesReceived;
|
||||
QNetworkAccessManager m_netMgr;
|
||||
QNetworkReply *m_reply;
|
||||
int m_redirects;
|
||||
#endif
|
||||
DefaultIconModel* const m_defaultIconModel;
|
||||
CustomIconModel* const m_customIconModel;
|
||||
|
||||
|
@ -117,7 +117,7 @@ class BrowserPlugin: public ISettingsPage
|
||||
{
|
||||
public:
|
||||
BrowserPlugin(DatabaseTabWidget* tabWidget) {
|
||||
m_nativeMessagingHost = QSharedPointer<NativeMessagingHost>(new NativeMessagingHost(tabWidget));
|
||||
m_nativeMessagingHost = QSharedPointer<NativeMessagingHost>(new NativeMessagingHost(tabWidget, BrowserSettings::isEnabled()));
|
||||
}
|
||||
|
||||
~BrowserPlugin() {
|
||||
@ -797,7 +797,7 @@ void MainWindow::closeEvent(QCloseEvent* event)
|
||||
config()->get("GUI/MinimizeOnClose").toBool();
|
||||
if (minimizeOnClose && !m_appExitCalled)
|
||||
{
|
||||
event->ignore();
|
||||
event->accept();
|
||||
hideWindow();
|
||||
|
||||
if (config()->get("security/lockdatabaseminimize").toBool()) {
|
||||
@ -899,7 +899,7 @@ void MainWindow::updateTrayIcon()
|
||||
|
||||
m_trayIcon->setContextMenu(menu);
|
||||
|
||||
m_trayIcon->setIcon(filePath()->applicationIcon());
|
||||
m_trayIcon->setIcon(filePath()->trayIcon());
|
||||
m_trayIcon->show();
|
||||
}
|
||||
if (m_ui->tabWidget->hasLockableDatabases()) {
|
||||
@ -971,7 +971,11 @@ void MainWindow::trayIconTriggered(QSystemTrayIcon::ActivationReason reason)
|
||||
void MainWindow::hideWindow()
|
||||
{
|
||||
saveWindowInformation();
|
||||
#ifndef Q_OS_MAC
|
||||
#if !defined(Q_OS_LINUX) && !defined(Q_OS_MAC)
|
||||
// On some Linux systems, the window should NOT be minimized and hidden (i.e. not shown), at
|
||||
// the same time (which would happen if both minimize on startup and minimize to tray are set)
|
||||
// since otherwise it causes problems on restore as seen on issue #1595. Hiding it is enough.
|
||||
// TODO: Add an explanation for why this is also not done on Mac (or remove the check)
|
||||
setWindowState(windowState() | Qt::WindowMinimized);
|
||||
#endif
|
||||
QTimer::singleShot(0, this, SLOT(hide()));
|
||||
@ -983,7 +987,7 @@ void MainWindow::hideWindow()
|
||||
|
||||
void MainWindow::toggleWindow()
|
||||
{
|
||||
if ((QApplication::activeWindow() == this) && isVisible() && !isMinimized()) {
|
||||
if (isVisible() && !isMinimized()) {
|
||||
hideWindow();
|
||||
} else {
|
||||
bringToFront();
|
||||
|
@ -152,6 +152,7 @@ void SettingsWidget::loadSettings()
|
||||
}
|
||||
m_generalUi->autoTypeShortcutWidget->setAttribute(Qt::WA_MacShowFocusRect, true);
|
||||
m_generalUi->autoTypeDelaySpinBox->setValue(config()->get("AutoTypeDelay").toInt());
|
||||
m_generalUi->autoTypeStartDelaySpinBox->setValue(config()->get("AutoTypeStartDelay").toInt());
|
||||
}
|
||||
|
||||
|
||||
@ -227,6 +228,7 @@ void SettingsWidget::saveSettings()
|
||||
config()->set("GlobalAutoTypeModifiers",
|
||||
static_cast<int>(m_generalUi->autoTypeShortcutWidget->modifiers()));
|
||||
config()->set("AutoTypeDelay", m_generalUi->autoTypeDelaySpinBox->value());
|
||||
config()->set("AutoTypeStartDelay", m_generalUi->autoTypeStartDelaySpinBox->value());
|
||||
}
|
||||
config()->set("security/clearclipboard", m_secUi->clearClipboardCheckBox->isChecked());
|
||||
config()->set("security/clearclipboardtimeout", m_secUi->clearClipboardSpinBox->value());
|
||||
|
@ -26,7 +26,7 @@
|
||||
<item>
|
||||
<widget class="QTabWidget" name="generalSettingsTabWidget">
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
<number>1</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="tabGeneral">
|
||||
<attribute name="title">
|
||||
@ -404,7 +404,7 @@
|
||||
<number>10</number>
|
||||
</property>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="autoTypeShortcutLabel_2">
|
||||
<widget class="QLabel" name="autoTypeShortcutLabel">
|
||||
<property name="text">
|
||||
<string>Global Auto-Type shortcut</string>
|
||||
</property>
|
||||
@ -420,14 +420,14 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="autoTypeDelayLabel_2">
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="autoTypeDelayLabel">
|
||||
<property name="text">
|
||||
<string>Auto-Type delay</string>
|
||||
<string>Auto-Type typing delay</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<item row="3" column="1">
|
||||
<widget class="QSpinBox" name="autoTypeDelaySpinBox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
||||
@ -442,13 +442,48 @@
|
||||
<string/>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>999</number>
|
||||
<number>1000</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>25</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="autoTypeStartDelayLabel">
|
||||
<property name="text">
|
||||
<string>Auto-Type start delay</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QSpinBox" name="autoTypeStartDelaySpinBox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="suffix">
|
||||
<string comment="Milliseconds"> ms</string>
|
||||
</property>
|
||||
<property name="prefix">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>100</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>10000</number>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<number>100</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>500</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
|
@ -281,6 +281,8 @@ void EditEntryWidget::setupSSHAgent()
|
||||
connect(m_sshAgentUi->decryptButton, SIGNAL(clicked()), SLOT(decryptPrivateKey()));
|
||||
connect(m_sshAgentUi->copyToClipboardButton, SIGNAL(clicked()), SLOT(copyPublicKey()));
|
||||
|
||||
connect(m_advancedUi->attachmentsWidget->entryAttachments(), SIGNAL(modified()), SLOT(updateSSHAgentAttachments()));
|
||||
|
||||
addPage(tr("SSH Agent"), FilePath::instance()->icon("apps", "utilities-terminal"), m_sshAgentWidget);
|
||||
}
|
||||
|
||||
@ -299,6 +301,27 @@ void EditEntryWidget::updateSSHAgent()
|
||||
m_sshAgentUi->removeFromAgentButton->setEnabled(false);
|
||||
m_sshAgentUi->copyToClipboardButton->setEnabled(false);
|
||||
|
||||
m_sshAgentSettings = settings;
|
||||
updateSSHAgentAttachments();
|
||||
|
||||
if (settings.selectedType() == "attachment") {
|
||||
m_sshAgentUi->attachmentRadioButton->setChecked(true);
|
||||
} else {
|
||||
m_sshAgentUi->externalFileRadioButton->setChecked(true);
|
||||
}
|
||||
|
||||
updateSSHAgentKeyInfo();
|
||||
}
|
||||
|
||||
void EditEntryWidget::updateSSHAgentAttachment()
|
||||
{
|
||||
m_sshAgentUi->attachmentRadioButton->setChecked(true);
|
||||
updateSSHAgentKeyInfo();
|
||||
}
|
||||
|
||||
void EditEntryWidget::updateSSHAgentAttachments()
|
||||
{
|
||||
m_sshAgentUi->attachmentComboBox->clear();
|
||||
m_sshAgentUi->attachmentComboBox->addItem("");
|
||||
|
||||
auto attachments = m_advancedUi->attachmentsWidget->entryAttachments();
|
||||
@ -310,24 +333,8 @@ void EditEntryWidget::updateSSHAgent()
|
||||
m_sshAgentUi->attachmentComboBox->addItem(fileName);
|
||||
}
|
||||
|
||||
m_sshAgentUi->attachmentComboBox->setCurrentText(settings.attachmentName());
|
||||
m_sshAgentUi->externalFileEdit->setText(settings.fileName());
|
||||
|
||||
if (settings.selectedType() == "attachment") {
|
||||
m_sshAgentUi->attachmentRadioButton->setChecked(true);
|
||||
} else {
|
||||
m_sshAgentUi->externalFileRadioButton->setChecked(true);
|
||||
}
|
||||
|
||||
m_sshAgentSettings = settings;
|
||||
|
||||
updateSSHAgentKeyInfo();
|
||||
}
|
||||
|
||||
void EditEntryWidget::updateSSHAgentAttachment()
|
||||
{
|
||||
m_sshAgentUi->attachmentRadioButton->setChecked(true);
|
||||
updateSSHAgentKeyInfo();
|
||||
m_sshAgentUi->attachmentComboBox->setCurrentText(m_sshAgentSettings.attachmentName());
|
||||
m_sshAgentUi->externalFileEdit->setText(m_sshAgentSettings.fileName());
|
||||
}
|
||||
|
||||
void EditEntryWidget::updateSSHAgentKeyInfo()
|
||||
@ -420,12 +427,16 @@ void EditEntryWidget::browsePrivateKey()
|
||||
|
||||
bool EditEntryWidget::getOpenSSHKey(OpenSSHKey& key, bool decrypt)
|
||||
{
|
||||
QString fileName;
|
||||
QByteArray privateKeyData;
|
||||
|
||||
if (m_sshAgentUi->attachmentRadioButton->isChecked()) {
|
||||
privateKeyData = m_advancedUi->attachmentsWidget->getAttachment(m_sshAgentUi->attachmentComboBox->currentText());
|
||||
fileName = m_sshAgentUi->attachmentComboBox->currentText();
|
||||
privateKeyData = m_advancedUi->attachmentsWidget->getAttachment(fileName);
|
||||
} else {
|
||||
QFile localFile(m_sshAgentUi->externalFileEdit->text());
|
||||
QFileInfo localFileInfo(localFile);
|
||||
fileName = localFileInfo.fileName();
|
||||
|
||||
if (localFile.fileName().isEmpty()) {
|
||||
return false;
|
||||
@ -464,6 +475,10 @@ bool EditEntryWidget::getOpenSSHKey(OpenSSHKey& key, bool decrypt)
|
||||
key.setComment(m_entry->username());
|
||||
}
|
||||
|
||||
if (key.comment().isEmpty()) {
|
||||
key.setComment(fileName);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -103,6 +103,7 @@ private slots:
|
||||
#ifdef WITH_XC_SSHAGENT
|
||||
void updateSSHAgent();
|
||||
void updateSSHAgentAttachment();
|
||||
void updateSSHAgentAttachments();
|
||||
void updateSSHAgentKeyInfo();
|
||||
void browsePrivateKey();
|
||||
void addKeyToAgent();
|
||||
|
@ -185,7 +185,7 @@ QVariant EntryModel::data(const QModelIndex& index, int role) const
|
||||
return result;
|
||||
case Notes:
|
||||
// Display only first line of notes in simplified format
|
||||
result = entry->resolveMultiplePlaceholders(entry->notes().section("\n", 0, 0).simplified());
|
||||
result = entry->notes().section("\n", 0, 0).simplified();
|
||||
if (attr->isReference(EntryAttributes::NotesKey)) {
|
||||
result.prepend(tr("Ref: ", "Reference abbreviation"));
|
||||
}
|
||||
|
30
src/main.cpp
30
src/main.cpp
@ -29,6 +29,8 @@
|
||||
#include "gui/MainWindow.h"
|
||||
#include "gui/MessageBox.h"
|
||||
|
||||
#include "cli/Utils.h"
|
||||
|
||||
#if defined(WITH_ASAN) && defined(WITH_LSAN)
|
||||
#include <sanitizer/lsan_interface.h>
|
||||
#endif
|
||||
@ -43,6 +45,21 @@ Q_IMPORT_PLUGIN(QXcbIntegrationPlugin)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
static inline void earlyQNetworkAccessManagerWorkaround()
|
||||
{
|
||||
// When QNetworkAccessManager is instantiated it regularly starts polling
|
||||
// all network interfaces to see if anything changes and if so, what. This
|
||||
// creates a latency spike every 10 seconds on Mac OS 10.12+ and Windows 7 >=
|
||||
// when on a wifi connection.
|
||||
// So here we disable it for lack of better measure.
|
||||
// This will also cause this message: QObject::startTimer: Timers cannot
|
||||
// have negative intervals
|
||||
// For more info see:
|
||||
// - https://bugreports.qt.io/browse/QTBUG-40332
|
||||
// - https://bugreports.qt.io/browse/QTBUG-46015
|
||||
qputenv("QT_BEARER_POLL_TIMEOUT", QByteArray::number(-1));
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
#ifdef QT_NO_DEBUG
|
||||
@ -50,6 +67,8 @@ int main(int argc, char** argv)
|
||||
#endif
|
||||
Tools::setupSearchPaths();
|
||||
|
||||
earlyQNetworkAccessManagerWorkaround();
|
||||
|
||||
Application app(argc, argv);
|
||||
Application::setApplicationName("keepassxc");
|
||||
Application::setApplicationVersion(KEEPASSX_VERSION);
|
||||
@ -125,7 +144,14 @@ int main(int argc, char** argv)
|
||||
// start minimized if configured
|
||||
bool minimizeOnStartup = config()->get("GUI/MinimizeOnStartup").toBool();
|
||||
bool minimizeToTray = config()->get("GUI/MinimizeToTray").toBool();
|
||||
#ifndef Q_OS_LINUX
|
||||
if (minimizeOnStartup) {
|
||||
#else
|
||||
// On some Linux systems, the window should NOT be minimized and hidden (i.e. not shown), at
|
||||
// the same time (which would happen if both minimize on startup and minimize to tray are set)
|
||||
// since otherwise it causes problems on restore as seen on issue #1595. Hiding it is enough.
|
||||
if (minimizeOnStartup && !minimizeToTray) {
|
||||
#endif
|
||||
mainWindow.setWindowState(Qt::WindowMinimized);
|
||||
}
|
||||
if (!(minimizeOnStartup && minimizeToTray)) {
|
||||
@ -148,7 +174,9 @@ int main(int argc, char** argv)
|
||||
// we always need consume a line of STDIN if --pw-stdin is set to clear out the
|
||||
// buffer for native messaging, even if the specified file does not exist
|
||||
static QTextStream in(stdin, QIODevice::ReadOnly);
|
||||
password = in.readLine();
|
||||
static QTextStream out(stdout, QIODevice::WriteOnly);
|
||||
out << QCoreApplication::translate("Main", "Database password: ") << flush;
|
||||
password = Utils::getPassword();
|
||||
}
|
||||
|
||||
if (!filename.isEmpty() && QFile::exists(filename) && !filename.endsWith(".json", Qt::CaseInsensitive)) {
|
||||
|
@ -55,4 +55,7 @@ if(WITH_XC_BROWSER)
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/src
|
||||
COMMENT "Changing linking of keepassxc-proxy")
|
||||
endif()
|
||||
if(MINGW)
|
||||
target_link_libraries(keepassxc-proxy Wtsapi32.lib Ws2_32.lib)
|
||||
endif()
|
||||
endif()
|
||||
|
@ -18,10 +18,21 @@
|
||||
#include <QCoreApplication>
|
||||
#include "NativeMessagingHost.h"
|
||||
|
||||
NativeMessagingHost::NativeMessagingHost() : NativeMessagingBase()
|
||||
#ifdef Q_OS_WIN
|
||||
#include <Winsock2.h>
|
||||
#endif
|
||||
|
||||
NativeMessagingHost::NativeMessagingHost() : NativeMessagingBase(true)
|
||||
{
|
||||
m_localSocket = new QLocalSocket();
|
||||
m_localSocket->connectToServer(getLocalServerPath());
|
||||
m_localSocket->setReadBufferSize(NATIVE_MSG_MAX_LENGTH);
|
||||
|
||||
int socketDesc = m_localSocket->socketDescriptor();
|
||||
if (socketDesc) {
|
||||
int max = NATIVE_MSG_MAX_LENGTH;
|
||||
setsockopt(socketDesc, SOL_SOCKET, SO_SNDBUF, reinterpret_cast<char*>(&max), sizeof(max));
|
||||
}
|
||||
#ifdef Q_OS_WIN
|
||||
m_running.store(true);
|
||||
m_future = QtConcurrent::run(this, static_cast<void(NativeMessagingHost::*)()>(&NativeMessagingHost::readNativeMessages));
|
||||
|
@ -319,9 +319,9 @@ bool OpenSSHKey::openPrivateKey(const QString& passphrase)
|
||||
|
||||
if (m_cipherName.compare("aes-128-cbc", Qt::CaseInsensitive) == 0) {
|
||||
cipher.reset(new SymmetricCipher(SymmetricCipher::Aes128, SymmetricCipher::Cbc, SymmetricCipher::Decrypt));
|
||||
} else if (m_cipherName == "aes256-cbc") {
|
||||
} else if (m_cipherName == "aes256-cbc" || m_cipherName.compare("aes-256-cbc", Qt::CaseInsensitive) == 0) {
|
||||
cipher.reset(new SymmetricCipher(SymmetricCipher::Aes256, SymmetricCipher::Cbc, SymmetricCipher::Decrypt));
|
||||
} else if (m_cipherName == "aes256-ctr") {
|
||||
} else if (m_cipherName == "aes256-ctr" || m_cipherName.compare("aes-256-ctr", Qt::CaseInsensitive) == 0) {
|
||||
cipher.reset(new SymmetricCipher(SymmetricCipher::Aes256, SymmetricCipher::Ctr, SymmetricCipher::Decrypt));
|
||||
} else if (m_cipherName != "none") {
|
||||
m_error = tr("Unknown cipher: %1").arg(m_cipherName);
|
||||
@ -372,10 +372,22 @@ bool OpenSSHKey::openPrivateKey(const QString& passphrase)
|
||||
return false;
|
||||
}
|
||||
|
||||
QByteArray keyData;
|
||||
QByteArray mdBuf;
|
||||
do {
|
||||
QCryptographicHash hash(QCryptographicHash::Md5);
|
||||
hash.addData(mdBuf);
|
||||
hash.addData(passphrase.toUtf8());
|
||||
hash.addData(m_cipherIV.data(), 8);
|
||||
QByteArray keyData = hash.result();
|
||||
mdBuf = hash.result();
|
||||
keyData.append(mdBuf);
|
||||
} while(keyData.size() < cipher->keySize());
|
||||
|
||||
if (keyData.size() > cipher->keySize()) {
|
||||
// If our key size isn't a multiple of 16 (e.g. AES-192 or something),
|
||||
// then we will need to truncate it.
|
||||
keyData.resize(cipher->keySize());
|
||||
}
|
||||
|
||||
if (!cipher->init(keyData, m_cipherIV)) {
|
||||
m_error = cipher->errorString();
|
||||
|
@ -186,7 +186,17 @@ bool SSHAgent::addIdentity(OpenSSHKey& key, quint32 lifetime, bool confirm)
|
||||
}
|
||||
|
||||
if (responseData.length() < 1 || static_cast<quint8>(responseData[0]) != SSH_AGENT_SUCCESS) {
|
||||
m_error = tr("Agent refused this identity.");
|
||||
m_error = tr("Agent refused this identity. Possible reasons include:")
|
||||
+ "\n" + tr("The key has already been added.");
|
||||
|
||||
if (lifetime > 0) {
|
||||
m_error += "\n" + tr("Restricted lifetime is not supported by the agent (check options).");
|
||||
}
|
||||
|
||||
if (confirm) {
|
||||
m_error += "\n" + tr("A confirmation request is not supported by the agent (check options).");
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -268,10 +278,15 @@ void SSHAgent::databaseModeChanged(DatabaseWidget::Mode mode)
|
||||
}
|
||||
|
||||
QByteArray keyData;
|
||||
QString fileName;
|
||||
if (settings.selectedType() == "attachment") {
|
||||
keyData = e->attachments()->value(settings.attachmentName());
|
||||
fileName = settings.attachmentName();
|
||||
keyData = e->attachments()->value(fileName);
|
||||
} else if (!settings.fileName().isEmpty()) {
|
||||
QFile file(settings.fileName());
|
||||
QFileInfo fileInfo(file);
|
||||
|
||||
fileName = fileInfo.fileName();
|
||||
|
||||
if (file.size() > 1024 * 1024) {
|
||||
continue;
|
||||
@ -302,6 +317,10 @@ void SSHAgent::databaseModeChanged(DatabaseWidget::Mode mode)
|
||||
key.setComment(e->username());
|
||||
}
|
||||
|
||||
if (key.comment().isEmpty()) {
|
||||
key.setComment(fileName);
|
||||
}
|
||||
|
||||
if (settings.removeAtDatabaseClose()) {
|
||||
removeIdentityAtLock(key, uuid);
|
||||
}
|
||||
|
@ -34,17 +34,7 @@
|
||||
#ifndef _BLF_H_
|
||||
#define _BLF_H_
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
typedef uint32_t u_int32_t;
|
||||
typedef uint16_t u_int16_t;
|
||||
typedef uint8_t u_int8_t;
|
||||
|
||||
#define bzero(p,s) memset(p, 0, s)
|
||||
|
||||
#endif
|
||||
#include "includes.h"
|
||||
|
||||
#if !defined(HAVE_BCRYPT_PBKDF) && !defined(HAVE_BLH_H)
|
||||
|
||||
|
@ -39,7 +39,7 @@
|
||||
* Bruce Schneier.
|
||||
*/
|
||||
|
||||
#define HAVE_BLF_H
|
||||
#include "includes.h"
|
||||
|
||||
#if !defined(HAVE_BCRYPT_PBKDF) && (!defined(HAVE_BLOWFISH_INITSTATE) || \
|
||||
!defined(HAVE_BLOWFISH_EXPAND0STATE) || !defined(HAVE_BLF_ENC))
|
||||
@ -51,7 +51,7 @@
|
||||
|
||||
#include <sys/types.h>
|
||||
#ifdef HAVE_BLF_H
|
||||
#include "blf.h"
|
||||
#include <blf.h>
|
||||
#endif
|
||||
|
||||
#undef inline
|
||||
|
20
src/sshagent/includes.h
Normal file
20
src/sshagent/includes.h
Normal file
@ -0,0 +1,20 @@
|
||||
// mimic openSSH-portable's includes.h file to be able to use
|
||||
// its unmodified blowfish code
|
||||
|
||||
#define HAVE_BLF_H
|
||||
|
||||
#ifndef _GNU_SOURCE
|
||||
#define _GNU_SOURCE /* activate extra prototypes for glibc */
|
||||
#endif
|
||||
#include <sys/types.h>
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <stdint.h>
|
||||
|
||||
typedef uint32_t u_int32_t;
|
||||
typedef uint16_t u_int16_t;
|
||||
typedef uint8_t u_int8_t;
|
||||
|
||||
#define bzero(p,s) memset(p, 0, s)
|
||||
#endif
|
@ -90,7 +90,7 @@ void TestOpenSSHKey::testParseDSA()
|
||||
QCOMPARE(key.fingerprint(), QString("SHA256:tbbNuLN1hja8JNASDTlLOZQsbTlJDzJlz/oAGK3sX18"));
|
||||
}
|
||||
|
||||
void TestOpenSSHKey::testDecryptAES128CBC()
|
||||
void TestOpenSSHKey::testDecryptRSAAES128CBC()
|
||||
{
|
||||
const QString keyString = QString(
|
||||
"-----BEGIN RSA PRIVATE KEY-----\n"
|
||||
@ -181,7 +181,7 @@ void TestOpenSSHKey::testParseRSA()
|
||||
QCOMPARE(key.fingerprint(), QString("SHA256:DYdaZciYNxCejr+/8x+OKYxeTU1D5UsuIFUG4PWRFkk"));
|
||||
}
|
||||
|
||||
void TestOpenSSHKey::testDecryptAES256CBC()
|
||||
void TestOpenSSHKey::testDecryptOpenSSHAES256CBC()
|
||||
{
|
||||
const QString keyString = QString(
|
||||
"-----BEGIN OPENSSH PRIVATE KEY-----\n"
|
||||
@ -215,7 +215,55 @@ void TestOpenSSHKey::testDecryptAES256CBC()
|
||||
QVERIFY(privateKey.length() == 158);
|
||||
}
|
||||
|
||||
void TestOpenSSHKey::testDecryptAES256CTR()
|
||||
void TestOpenSSHKey::testDecryptRSAAES256CBC()
|
||||
{
|
||||
const QString keyString = QString(
|
||||
"-----BEGIN RSA PRIVATE KEY-----\n"
|
||||
"Proc-Type: 4,ENCRYPTED\n"
|
||||
"DEK-Info: AES-256-CBC,D51E3F558B621BD9384627762CBD16AC\n"
|
||||
"\n"
|
||||
"b6nr/06Gj8/Nw3ZFMePFyZeuBodExvZZtZPSH3t/2ArcxXOkoqUhLmlcY/JrvnBF\n"
|
||||
"JHc34wx/6Yng7mqtUMuk2iMemTzOj3JRx8zHUhwPLnjM/tmeOm0wBUb3WB4+rFZW\n"
|
||||
"s1PaIgeKywKgFK0UkcSRpMuSaxheWmHrtJkBsHTF7Tg3ogPL8Dc+nhQlbe/ZGaQb\n"
|
||||
"vMdSYcBMaXngS5ZiOafXeY8+l+IMMOZwy5vPTFQGqKHIzOxFhShs1hSExnwOXy69\n"
|
||||
"wxrA/QftjNEy5ixIeGT7iQfRB04tEVg0DjYphTPmI2ophzFlwJVBjhj2cmmnsMZg\n"
|
||||
"a2TdT/78KZsw2cA5ieMcU6d7Yz5p5nu5dyTbZonn6qWficdZRJwZnVb5ikPnJYbZ\n"
|
||||
"1YJRHHND+RWtpanxz7WhStscTCLeI9w9j2gqBJSjKDPgJaoMiA+tyEoakNlPYg+9\n"
|
||||
"DadJkBGP0g5E9zw0n4niqQ7eCxk7qolmW6Wtn2zL4UyeJKGi9NWFSGW9x/PmAIse\n"
|
||||
"E2KVodiJMRNa8/qUZcW58ZG2uRnFTsW4BXdmzOy/Zp53TVGWStBVLDcldSD03ItD\n"
|
||||
"JIWQWDgWp5xyVqPl+8mkW7xDY0GRVSJCyRkctQeGTGysy0BcNjgQQtiA3lPC0rY5\n"
|
||||
"m2VxrCYU1KuyHsAjs/V8THcW4a1UdPcVBg1QbCh29bMoM6u4MuXVt7rkwxAV9HJa\n"
|
||||
"VbwPsKy7V6G60KaAFIiOs0wdOzBZBoPGd9vBQOEzATh2FYJruDo2OfzEnhv25RxE\n"
|
||||
"1q+C/Jds9cWqaNY8kNtUG799XIKkjrC6KvnoV6UA4BkGs2DAcO9rnwtl/hToEoBe\n"
|
||||
"ZVj72dlTuS6l9rHqKaz2GI0k0SEt/ZoakPHeDRgPNcDvEZWitV8MuD6Mwb47Y88u\n"
|
||||
"sjBmS5k4sJOtB4bLg/UShcqYfkv2OTsK90qGQtba9vMk04Xh1FuxB4fHa5VoKrsX\n"
|
||||
"Th/LB34xoYugd16NPmLuawhSo70o4bT70GYpxnb4brGfjWiuthRdegAG9ESSX+M6\n"
|
||||
"rNKQPnn2GSroIpkoA4k0PaflcE5tpzeIiJdv0h65N3vw6MFnCaWy8sRSy9fMyRim\n"
|
||||
"U8QZB2jcp+YjUU/eny3scuh0Vqt6g1tfFbI84pCC5bArBirf63MeMtwDU/IVImax\n"
|
||||
"xzKOzl7k8ropA+rhAJ4Z9X35EmUncBXhf8g39w6nFuSlqjE6rMxCrsrehljQ1Iuz\n"
|
||||
"bujaJ2PKpf98OejHDKnMDOfBBq0DdeERCYWlCcqWSgrEgHh4vB5dEQAPP5bAkdZj\n"
|
||||
"m0Dq+gF99yadioxf3/MUZVTa1dHklBJJkXTYVPeyH07Th5j7bGCcVb9Zd2Ao/Dia\n"
|
||||
"MPWf6xViCC6d0njCLQY2R8mOR5OMVsdlFrsKZMQ/lqjS/WSM6URDkuGb0Cq94TQd\n"
|
||||
"7DoblcA50FTwYrVXMygWygbjzJxhcoJDHztzwoqLT/ghh+6zRg6R/fY222tHHbhz\n"
|
||||
"nePf421NILzfxnuW+GOwRCM5+IHE3OBS/PYDGijjRFHU4ky0rRRDE64m9CeFzeBh\n"
|
||||
"CnFvW6Yx3Hrh5tXBP7kRZ6KjyrPP7tI4ciVSJceSBLRzFmoBr10kRMm+VsUh2xZH\n"
|
||||
"-----END RSA PRIVATE KEY-----\n"
|
||||
);
|
||||
|
||||
const QByteArray keyData = keyString.toLatin1();
|
||||
|
||||
OpenSSHKey key;
|
||||
QVERIFY(key.parse(keyData));
|
||||
QVERIFY(key.encrypted());
|
||||
QCOMPARE(key.cipherName(), QString("AES-256-CBC"));
|
||||
QVERIFY(!key.openPrivateKey("incorrectpassphrase"));
|
||||
QVERIFY(key.openPrivateKey("correctpassphrase"));
|
||||
QCOMPARE(key.type(), QString("ssh-rsa"));
|
||||
QCOMPARE(key.comment(), QString(""));
|
||||
QCOMPARE(key.fingerprint(), QString("SHA256:1Hsebt2WWnmc72FERsUOgvaajIGHkrMONxXylcmk87U"));
|
||||
}
|
||||
|
||||
void TestOpenSSHKey::testDecryptOpenSSHAES256CTR()
|
||||
{
|
||||
const QString keyString = QString(
|
||||
"-----BEGIN OPENSSH PRIVATE KEY-----\n"
|
||||
@ -248,3 +296,51 @@ void TestOpenSSHKey::testDecryptAES256CTR()
|
||||
QVERIFY(publicKey.length() == 51);
|
||||
QVERIFY(privateKey.length() == 158);
|
||||
}
|
||||
|
||||
void TestOpenSSHKey::testDecryptRSAAES256CTR()
|
||||
{
|
||||
const QString keyString = QString(
|
||||
"-----BEGIN RSA PRIVATE KEY-----\n"
|
||||
"Proc-Type: 4,ENCRYPTED\n"
|
||||
"DEK-Info: AES-256-CTR,192421854316290DFA8F469A1E8CB9BB\n"
|
||||
"\n"
|
||||
"3h7gUWua+jcvhYj1vUusbMdOG9j8SmNWFV5Hfersi8nF4ddsWEQDnMrRuhtIn4tU\n"
|
||||
"GcLY+SXguim5XXwF8gG1tmvpvFMhudTfX+0cIAX7eAVmqLy2RTA18DWqDwWokVP0\n"
|
||||
"RJPgRJJSorjMtu2F0YGVVjElW7pHIal7luNk3BDgYUMlgSg0BGOWb+9BkXcEnfc8\n"
|
||||
"KEwsJw2onoR2eXo6rYnczGaqPhAPu+I+PfUn0J8PFiffWc1KebRntLdRWeNsBS4p\n"
|
||||
"oxtqByzMYIu/WPSJJ5iFoNdKaWQPiZJB+juwI1wNLEtpzKkhpc7/6mOy87h+0eGV\n"
|
||||
"fF7javrbHv37eE+k2iZXrcLfvRpiBqt5+uhhCaM8TivBeUho5J38ru/wt/dk+OvY\n"
|
||||
"tzXboWA4zVnaYmqta1CkXYKOmb5a8TWEwtxmAuE6kCz/n3pPa6gwkwsyGI65IEyX\n"
|
||||
"ycJsbwUilAzXTcz5bIruGx38Sa9fndAN9llOQMS/hdyNs5W5dO1XZ5gU+ARPce+j\n"
|
||||
"+A2R8oCUv+2ciEu8z3F++U9aTRmTlD3xeIM0IWUFXKt8Y9fSRC5XoPCbZYNxnV6/\n"
|
||||
"hn9NPKCb890Faxies3MABOB5IZ0aTPWkx9ntxFhMaXyfkX2YthNO0GzAENPP9Knt\n"
|
||||
"DYhQePlKQ7sNi8+wzsHNeDxNuL/+Rib2MN3ankDtHIsqFz/Em+rD0+3ya8bLy3pP\n"
|
||||
"eeUiNpezL+uxI5llq/pikzK4sOgvH1r5YEkMxt9I09grkBwxR7DMBo0vgRE2MLiL\n"
|
||||
"nlri8TDwArC1+0gE8NspkkClsBOHXuVlGZo5lup2tn5MzERQcLvuFnAby/GnaVXQ\n"
|
||||
"Hm76teb1wMdL58FrdZsKR6e80E+F6JpTsz0a3XJqptgAwGsoxqizkUNJG5hRP8bi\n"
|
||||
"NBCFQZPeYi/GxgN5O2UkxhgRkKAcrHg+G87nhLk1ipsc214rb6iOspNizP6fGDuv\n"
|
||||
"/bsNTpYRgMNxCLh5Nv0HSUqckoNKOcIVe/9nF5/LLFGfhz95agjKTbBygThFK28N\n"
|
||||
"bnHq5fO9yKCMrGCRBQ6No1wwexyS4IAq17LcQP3k4w4n+Wt2GjO5HIldGOEyGqCE\n"
|
||||
"zeHYrPpGXF/yf3XTm00XghdQtVtRJptdddXVGZN3EN2w7/ghOSIIlsJO9C4IRgU3\n"
|
||||
"WkhX7oOpSE4wmXd5Ada+D1U46snW5nWANWko2NmQNVDeJcvuymL6t2ccNYeFWiA+\n"
|
||||
"Hlv0avBnqng7ZWPxYacqZI3+vU0rN9usN1pzwY/4NsBa34o3M7u6KvzEkyewbyUT\n"
|
||||
"VfXLJ8XRzb2u4NqQv0WiTBIRxvVB1sRPcrwB4HWKHwRFT8T7f1fefteROrKV7aKm\n"
|
||||
"Q48pckidDM0ORh1yIET8u24Mreo5eeWXjVJ9MHoM0486VySYxMwk8yp4tnaHx5kd\n"
|
||||
"fGlBbbwFOifhzjAk0u3KJRAG85t2GZhfTMo1IHS2kdu4Xs1N00ZmK0hjeGG+DCwy\n"
|
||||
"06ZKHOF5BHWU3SpQEjCsPDwfIpOINWGAJJnp6NIVf7FkHwViV50GWWGSZal4NqZy\n"
|
||||
"kR19buHiOb7KnGoPCw8MUmIym8v30FePhM4YQ7ASmRtsXlAhQNRX\n"
|
||||
"-----END RSA PRIVATE KEY-----\n"
|
||||
);
|
||||
|
||||
const QByteArray keyData = keyString.toLatin1();
|
||||
|
||||
OpenSSHKey key;
|
||||
QVERIFY(key.parse(keyData));
|
||||
QVERIFY(key.encrypted());
|
||||
QCOMPARE(key.cipherName(), QString("AES-256-CTR"));
|
||||
QVERIFY(!key.openPrivateKey("incorrectpassphrase"));
|
||||
QVERIFY(key.openPrivateKey("correctpassphrase"));
|
||||
QCOMPARE(key.type(), QString("ssh-rsa"));
|
||||
QCOMPARE(key.comment(), QString(""));
|
||||
QCOMPARE(key.fingerprint(), QString("SHA256:1Hsebt2WWnmc72FERsUOgvaajIGHkrMONxXylcmk87U"));
|
||||
}
|
||||
|
@ -31,9 +31,11 @@ private slots:
|
||||
void testParse();
|
||||
void testParseDSA();
|
||||
void testParseRSA();
|
||||
void testDecryptAES128CBC();
|
||||
void testDecryptAES256CBC();
|
||||
void testDecryptAES256CTR();
|
||||
void testDecryptRSAAES128CBC();
|
||||
void testDecryptOpenSSHAES256CBC();
|
||||
void testDecryptRSAAES256CBC();
|
||||
void testDecryptOpenSSHAES256CTR();
|
||||
void testDecryptRSAAES256CTR();
|
||||
};
|
||||
|
||||
#endif // TESTOPENSSHKEY_H
|
||||
|
@ -71,6 +71,8 @@ void TestGui::initTestCase()
|
||||
Config::createTempFileInstance();
|
||||
// Disable autosave so we can test the modified file indicator
|
||||
Config::instance()->set("AutoSaveAfterEveryChange", false);
|
||||
// Enable the tray icon so we can test hiding/restoring the window
|
||||
Config::instance()->set("GUI/ShowTrayIcon", true);
|
||||
|
||||
m_mainWindow = new MainWindow();
|
||||
m_tabWidget = m_mainWindow->findChild<DatabaseTabWidget*>("tabWidget");
|
||||
@ -1091,6 +1093,32 @@ void TestGui::testDragAndDropKdbxFiles()
|
||||
QCOMPARE(m_tabWidget->count(), openedDatabasesCount);
|
||||
}
|
||||
|
||||
void TestGui::testTrayRestoreHide()
|
||||
{
|
||||
if (!QSystemTrayIcon::isSystemTrayAvailable()) {
|
||||
QSKIP("QSystemTrayIcon::isSystemTrayAvailable() = false, skipping tray restore/hide test...");
|
||||
}
|
||||
|
||||
QSystemTrayIcon* trayIcon = m_mainWindow->findChild<QSystemTrayIcon*>();
|
||||
QVERIFY(m_mainWindow->isVisible());
|
||||
|
||||
trayIcon->activated(QSystemTrayIcon::Trigger);
|
||||
Tools::wait(100);
|
||||
QVERIFY(!m_mainWindow->isVisible());
|
||||
|
||||
trayIcon->activated(QSystemTrayIcon::Trigger);
|
||||
Tools::wait(100);
|
||||
QVERIFY(m_mainWindow->isVisible());
|
||||
|
||||
trayIcon->activated(QSystemTrayIcon::Trigger);
|
||||
Tools::wait(100);
|
||||
QVERIFY(!m_mainWindow->isVisible());
|
||||
|
||||
trayIcon->activated(QSystemTrayIcon::Trigger);
|
||||
Tools::wait(100);
|
||||
QVERIFY(m_mainWindow->isVisible());
|
||||
}
|
||||
|
||||
void TestGui::cleanupTestCase()
|
||||
{
|
||||
delete m_mainWindow;
|
||||
|
@ -63,6 +63,7 @@ private slots:
|
||||
void testKeePass1Import();
|
||||
void testDatabaseLocking();
|
||||
void testDragAndDropKdbxFiles();
|
||||
void testTrayRestoreHide();
|
||||
|
||||
private:
|
||||
int addCannedEntries();
|
||||
|
Loading…
Reference in New Issue
Block a user