mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2024-12-28 08:49:42 -05:00
Merge branch 'release/2.6.1' into develop
This commit is contained in:
commit
71b05dbcf4
@ -24,18 +24,20 @@ set(DOC_DIR ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
set(OUT_DIR ${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
# Build html documentation on all platforms
|
||||
file(GLOB html_depends ${DOC_DIR}/topics/* ${DOC_DIR}/styles/* ${DOC_DIR}/images/*)
|
||||
add_custom_command(OUTPUT KeePassXC_GettingStarted.html
|
||||
COMMAND ${ASCIIDOCTOR_EXE} -D ${OUT_DIR} -o KeePassXC_GettingStarted.html ${DOC_DIR}/GettingStarted.adoc
|
||||
DEPENDS ${DOC_DIR}/topics/* ${DOC_DIR}/styles/* ${DOC_DIR}/images/* ${DOC_DIR}/GettingStarted.adoc
|
||||
DEPENDS ${html_depends} ${DOC_DIR}/GettingStarted.adoc
|
||||
VERBATIM)
|
||||
add_custom_command(OUTPUT KeePassXC_UserGuide.html
|
||||
COMMAND ${ASCIIDOCTOR_EXE} -D ${OUT_DIR} -o KeePassXC_UserGuide.html ${DOC_DIR}/UserGuide.adoc
|
||||
DEPENDS ${DOC_DIR}/topics/* ${DOC_DIR}/styles/* ${DOC_DIR}/images/* ${DOC_DIR}/UserGuide.adoc
|
||||
DEPENDS ${html_depends} ${DOC_DIR}/UserGuide.adoc
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
VERBATIM)
|
||||
file(GLOB styles_depends ${DOC_DIR}/styles/*)
|
||||
add_custom_command(OUTPUT KeePassXC_KeyboardShortcuts.html
|
||||
COMMAND ${ASCIIDOCTOR_EXE} -D ${OUT_DIR} -o KeePassXC_KeyboardShortcuts.html ${DOC_DIR}/topics/KeyboardShortcuts.adoc
|
||||
DEPENDS ${DOC_DIR}/topics/KeyboardShortcuts.adoc ${DOC_DIR}/styles/*
|
||||
DEPENDS ${DOC_DIR}/topics/KeyboardShortcuts.adoc ${styles_depends}
|
||||
VERBATIM)
|
||||
|
||||
add_custom_target(docs ALL DEPENDS KeePassXC_GettingStarted.html KeePassXC_UserGuide.html KeePassXC_KeyboardShortcuts.html)
|
||||
@ -50,11 +52,11 @@ install(FILES
|
||||
if(APPLE OR UNIX)
|
||||
add_custom_command(OUTPUT keepassxc.1
|
||||
COMMAND ${ASCIIDOCTOR_EXE} -D ${OUT_DIR} -b manpage ${DOC_DIR}/man/keepassxc.1.adoc
|
||||
DEPENDS ${DOC_DIR}/man/*
|
||||
DEPENDS ${DOC_DIR}/man/keepassxc.1.adoc
|
||||
VERBATIM)
|
||||
add_custom_command(OUTPUT keepassxc-cli.1
|
||||
COMMAND ${ASCIIDOCTOR_EXE} -D ${OUT_DIR} -b manpage ${DOC_DIR}/man/keepassxc-cli.1.adoc
|
||||
DEPENDS ${DOC_DIR}/man/*
|
||||
DEPENDS ${DOC_DIR}/man/keepassxc-cli.1.adoc
|
||||
VERBATIM)
|
||||
add_custom_target(manpages ALL DEPENDS keepassxc.1 keepassxc-cli.1)
|
||||
|
||||
|
@ -1,10 +1,28 @@
|
||||
// Copyright (C) 2017 Manolis Agkopian <m.agkopian@gmail.com>
|
||||
// Copyright (C) 2020 KeePassXC Team <team@keepassxc.org>
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 2 or (at your option)
|
||||
// version 3 of the License.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
= keepassxc-cli(1)
|
||||
:docdate: 2020-07-05
|
||||
:docdate: 2020-07-10
|
||||
:doctype: manpage
|
||||
:revnumber: 2.6.0
|
||||
:mansource: KeePassXC {revnumber}
|
||||
:manmanual: General Commands Manual
|
||||
|
||||
== NAME
|
||||
keepassxc-cli - command line interface for the KeePassXC password manager.
|
||||
keepassxc-cli - command line interface for the KeePassXC password manager
|
||||
|
||||
== SYNOPSIS
|
||||
*keepassxc-cli* _command_ [_options_]
|
||||
@ -16,21 +34,21 @@ It provides the ability to query and modify the entries of a KeePass database, d
|
||||
== COMMANDS
|
||||
*add* [_options_] <__database__> <__entry__>::
|
||||
Adds a new entry to a database.
|
||||
A password can be generated (_-g_ option), or a prompt can be displayed to input the password (_-p_ option).
|
||||
The same password generation options as documented for the generate command can be used when the _-g_ option is set.
|
||||
A password can be generated (*-g* option), or a prompt can be displayed to input the password (*-p* option).
|
||||
The same password generation options as documented for the generate command can be used when the *-g* option is set.
|
||||
|
||||
*analyze* [_options_] <__database__>::
|
||||
Analyzes passwords in a database for weaknesses.
|
||||
|
||||
*clip* [_options_] <__database__> <__entry__> [_timeout_]::
|
||||
Copies an attribute or the current TOTP (if the _-t_ option is specified) of a database entry to the clipboard.
|
||||
If no attribute name is specified using the _-a_ option, the password is copied.
|
||||
Copies an attribute or the current TOTP (if the *-t* option is specified) of a database entry to the clipboard.
|
||||
If no attribute name is specified using the *-a* option, the password is copied.
|
||||
If multiple entries with the same name exist in different groups, only the attribute for the first one is copied.
|
||||
For copying the attribute of an entry in a specific group, the group path to the entry should be specified as well, instead of just the name.
|
||||
Optionally, a timeout in seconds can be specified to automatically clear the clipboard.
|
||||
|
||||
*close*::
|
||||
In interactive mode, closes the currently opened database (see _open_).
|
||||
In interactive mode, closes the currently opened database (see *open*).
|
||||
|
||||
*db-create* [_options_] <__database__>::
|
||||
Creates a new database with a password and/or a key file.
|
||||
@ -45,8 +63,8 @@ It provides the ability to query and modify the entries of a KeePass database, d
|
||||
|
||||
*edit* [_options_] <__database__> <__entry__>::
|
||||
Edits a database entry.
|
||||
A password can be generated (_-g_ option), or a prompt can be displayed to input the password (_-p_ option).
|
||||
The same password generation options as documented for the generate command can be used when the _-g_ option is set.
|
||||
A password can be generated (*-g* option), or a prompt can be displayed to input the password (*-p* option).
|
||||
The same password generation options as documented for the generate command can be used when the *-g* option is set.
|
||||
|
||||
*estimate* [_options_] [_password_]::
|
||||
Estimates the entropy of a password.
|
||||
@ -54,7 +72,7 @@ It provides the ability to query and modify the entries of a KeePass database, d
|
||||
|
||||
*exit*::
|
||||
Exits interactive mode.
|
||||
Synonymous with _quit_.
|
||||
Synonymous with *quit*.
|
||||
|
||||
*export* [_options_] <__database__>::
|
||||
Exports the content of a database to standard output in the specified format (defaults to XML).
|
||||
@ -78,7 +96,7 @@ It provides the ability to query and modify the entries of a KeePass database, d
|
||||
*merge* [_options_] <__database1__> <__database2__>::
|
||||
Merges two databases together.
|
||||
The first database file is going to be replaced by the result of the merge, for that reason it is advisable to keep a backup of the two database files before attempting a merge.
|
||||
In the case that both databases make use of the same credentials, the _--same-credentials_ or _-s_ option can be used.
|
||||
In the case that both databases make use of the same credentials, the *--same-credentials* or *-s* option can be used.
|
||||
|
||||
*mkdir* [_options_] <__database__> <__group__>::
|
||||
Adds a new group to a database.
|
||||
@ -88,11 +106,11 @@ It provides the ability to query and modify the entries of a KeePass database, d
|
||||
|
||||
*open* [_options_] <__database__>::
|
||||
Opens the given database in a shell-style interactive mode.
|
||||
This is useful for performing multiple operations on a single database (e.g. _ls_ followed by _show_).
|
||||
This is useful for performing multiple operations on a single database (e.g. *ls* followed by *show*).
|
||||
|
||||
*quit*::
|
||||
Exits interactive mode.
|
||||
Synonymous with _exit_.
|
||||
Synonymous with *exit*.
|
||||
|
||||
*rm* [_options_] <__database__> <__entry__>::
|
||||
Removes an entry from a database.
|
||||
@ -107,7 +125,7 @@ It provides the ability to query and modify the entries of a KeePass database, d
|
||||
*show* [_options_] <__database__> <__entry__>::
|
||||
Shows the title, username, password, URL and notes of a database entry.
|
||||
Can also show the current TOTP.
|
||||
Regarding the occurrence of multiple entries with the same name in different groups, everything stated in the _clip_ command section also applies here.
|
||||
Regarding the occurrence of multiple entries with the same name in different groups, everything stated in the *clip* command section also applies here.
|
||||
|
||||
== OPTIONS
|
||||
=== General options
|
||||
@ -151,7 +169,7 @@ It provides the ability to query and modify the entries of a KeePass database, d
|
||||
Uses the same credentials for unlocking both databases.
|
||||
|
||||
=== Add and edit options
|
||||
The same password generation options as documented for the generate command can be used with those 2 commands when the -g option is set.
|
||||
The same password generation options as documented for the generate command can be used with those 2 commands when the *-g* option is set.
|
||||
|
||||
*-u*, *--username* <__username__>::
|
||||
Specifies the username of the entry.
|
||||
@ -183,7 +201,7 @@ The same password generation options as documented for the generate command can
|
||||
*-a*, *--attribute*::
|
||||
Copies the specified attribute to the clipboard.
|
||||
If no attribute is specified, the password attribute is the default.
|
||||
For example, "_-a_ username" would copy the username to the clipboard.
|
||||
For example, "*-a* *username*" would copy the username to the clipboard.
|
||||
[Default: password]
|
||||
|
||||
*-t*, *--totp*::
|
||||
@ -204,7 +222,7 @@ The same password generation options as documented for the generate command can
|
||||
*-a*, *--attributes* <__attribute__>...::
|
||||
Shows the named attributes.
|
||||
This option can be specified more than once, with each attribute shown one-per-line in the given order.
|
||||
If no attributes are specified and _-t_ is not specified, a summary of the default attributes is given.
|
||||
If no attributes are specified and *-t* is not specified, a summary of the default attributes is given.
|
||||
Protected attributes will be displayed in clear text if specified explicitly by this option.
|
||||
|
||||
*-s*, *--show-protected*::
|
||||
@ -274,9 +292,11 @@ The same password generation options as documented for the generate command can
|
||||
Include characters from every selected group.
|
||||
[Default: Disabled]
|
||||
|
||||
== REPORTING BUGS
|
||||
Bugs and feature requests can be reported on GitHub at https://github.com/keepassxreboot/keepassxc/issues.
|
||||
include::section-notes.adoc[]
|
||||
|
||||
== AUTHOR
|
||||
This manual page was originally written by Manolis Agkopian <m.agkopian@gmail.com>,
|
||||
and is maintained by the KeePassXC Team <team@keepassxc.org>.
|
||||
This manual page was originally written by Manolis Agkopian <m.agkopian@gmail.com>.
|
||||
|
||||
include::section-reporting-bugs.adoc[]
|
||||
|
||||
include::section-copyright.adoc[]
|
||||
|
@ -1,10 +1,28 @@
|
||||
// Copyright (C) 2019 Janek Bevendorff <janek@jbev.net>
|
||||
// Copyright (C) 2020 KeePassXC Team <team@keepassxc.org>
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 2 or (at your option)
|
||||
// version 3 of the License.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
= keepassxc(1)
|
||||
:docdate: 2020-07-05
|
||||
:docdate: 2020-07-10
|
||||
:doctype: manpage
|
||||
:revnumber: 2.6.0
|
||||
:mansource: KeePassXC {revnumber}
|
||||
:manmanual: General Commands Manual
|
||||
|
||||
== NAME
|
||||
keepassxc - password manager
|
||||
keepassxc - a modern open-source password manager
|
||||
|
||||
== SYNOPSIS
|
||||
*keepassxc* [_options_] [_filename(s)_]
|
||||
@ -23,19 +41,25 @@ Your wallet works offline and requires no Internet connection.
|
||||
Displays version information.
|
||||
|
||||
*--config* <__config__>::
|
||||
Path to a custom config file
|
||||
Path to a custom config file.
|
||||
|
||||
*--keyfile* <__keyfile__>::
|
||||
Key file of the database
|
||||
Key file of the database.
|
||||
|
||||
*--pw-stdin*::
|
||||
Read password of the database from stdin
|
||||
Read password of the database from stdin.
|
||||
|
||||
*--pw*, *--parent-window* <__handle__>::
|
||||
Parent window handle
|
||||
Parent window handle.
|
||||
|
||||
*--debug-info*::
|
||||
Displays debugging information.
|
||||
|
||||
include::section-notes.adoc[]
|
||||
|
||||
== AUTHOR
|
||||
This manual page is maintained by the KeePassXC Team <team@keepassxc.org>.
|
||||
This manual page was originally written by Janek Bevendorff <janek@jbev.net>.
|
||||
|
||||
include::section-reporting-bugs.adoc[]
|
||||
|
||||
include::section-copyright.adoc[]
|
||||
|
19
docs/man/section-copyright.adoc
Normal file
19
docs/man/section-copyright.adoc
Normal file
@ -0,0 +1,19 @@
|
||||
// Copyright (C) 2020 KeePassXC Team <team@keepassxc.org>
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 2 or (at your option)
|
||||
// version 3 of the License.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
== COPYRIGHT
|
||||
Copyright \(C) 2016-2020 KeePassXC Team <team@keepassxc.org>
|
||||
|
||||
*KeePassXC* code is licensed under GPL-2 or GPL-3.
|
27
docs/man/section-notes.adoc
Normal file
27
docs/man/section-notes.adoc
Normal file
@ -0,0 +1,27 @@
|
||||
// Copyright (C) 2020 KeePassXC Team <team@keepassxc.org>
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 2 or (at your option)
|
||||
// version 3 of the License.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
== NOTES
|
||||
*Project homepage*::
|
||||
https://keepassxc.org
|
||||
|
||||
*QuickStart Guide*::
|
||||
https://keepassxc.org/docs/KeePassXC_GettingStarted.html
|
||||
|
||||
*User Guide*::
|
||||
https://keepassxc.org/docs/KeePassXC_UserGuide.html
|
||||
|
||||
*Git repository*::
|
||||
https://github.com/keepassxreboot/keepassxc.git
|
17
docs/man/section-reporting-bugs.adoc
Normal file
17
docs/man/section-reporting-bugs.adoc
Normal file
@ -0,0 +1,17 @@
|
||||
// Copyright (C) 2020 KeePassXC Team <team@keepassxc.org>
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 2 or (at your option)
|
||||
// version 3 of the License.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
== REPORTING BUGS
|
||||
Bugs and feature requests can be reported on GitHub at https://github.com/keepassxreboot/keepassxc/issues.
|
@ -28,23 +28,23 @@
|
||||
|
||||
<screenshots>
|
||||
<screenshot type="default">
|
||||
<image>https://keepassxc.org/images/screenshots/linux/screen_001.png</image>
|
||||
<image>https://keepassxc.org/images/screenshots/thumbs/welcome_screen.png</image>
|
||||
<caption>Create, Import or Open Databases</caption>
|
||||
</screenshot>
|
||||
<screenshot>
|
||||
<image>https://keepassxc.org/images/screenshots/linux/screen_002.png</image>
|
||||
<image>https://keepassxc.org/images/screenshots/thumbs/database_view.png</image>
|
||||
<caption>Organize with Groups and Entries</caption>
|
||||
</screenshot>
|
||||
<screenshot>
|
||||
<image>https://keepassxc.org/images/screenshots/linux/screen_003.png</image>
|
||||
<image>https://keepassxc.org/images/screenshots/thumbs/edit_entry.png</image>
|
||||
<caption>Database Entry</caption>
|
||||
</screenshot>
|
||||
<screenshot>
|
||||
<image>https://keepassxc.org/images/screenshots/linux/screen_004.png</image>
|
||||
<image>https://keepassxc.org/images/screenshots/thumbs/edit_entry_icons.png</image>
|
||||
<caption>Icon Selection for Entry</caption>
|
||||
</screenshot>
|
||||
<screenshot>
|
||||
<image>https://keepassxc.org/images/screenshots/linux/screen_006.png</image>
|
||||
<image>https://keepassxc.org/images/screenshots/thumbs/password_generator_advanced.png</image>
|
||||
<caption>Password Generator</caption>
|
||||
</screenshot>
|
||||
</screenshots>
|
||||
@ -614,4 +614,5 @@
|
||||
</description>
|
||||
</release>
|
||||
</releases>
|
||||
<content_rating type="oars-1.0" />
|
||||
</component>
|
||||
|
@ -1,13 +1,39 @@
|
||||
[Desktop Entry]
|
||||
Name=KeePassXC
|
||||
GenericName=Password Manager
|
||||
GenericName[ar]=مدير كلمات المرور
|
||||
GenericName[bg]=Мениджър на пароли
|
||||
GenericName[ca]=Gestor de contrasenyes
|
||||
GenericName[cs]=Aplikace pro správu hesel
|
||||
GenericName[da]=Adgangskodehåndtering
|
||||
GenericName[de]=Passwortverwaltung
|
||||
GenericName[es]=Gestor de contraseñas
|
||||
GenericName[et]=Paroolihaldur
|
||||
GenericName[fi]=Salasanamanageri
|
||||
GenericName[fr]=Gestionnaire de mot de passe
|
||||
GenericName[hu]=Jelszókezelő
|
||||
GenericName[id]=Pengelola Sandi
|
||||
GenericName[it]=Gestione password
|
||||
GenericName[ja]=パスワードマネージャー
|
||||
GenericName[ko]=암호 관리자
|
||||
GenericName[lt]=Slaptažodžių tvarkytuvė
|
||||
GenericName[nb]=Passordhåndterer
|
||||
GenericName[nl]=Wachtwoordbeheer
|
||||
GenericName[pl]=Menedżer haseł
|
||||
GenericName[pt_BR]=Gerenciador de Senhas
|
||||
GenericName[pt]=Gestor de palavras-passe
|
||||
GenericName[ro]=Manager de parole
|
||||
GenericName[ru]=менеджер паролей
|
||||
GenericName[sk]=Správca hesiel
|
||||
GenericName[sv]=Lösenordshanterare
|
||||
GenericName[th]=แอพจัดการรหัสผ่าน
|
||||
GenericName[tr]=Parola yöneticisi
|
||||
GenericName[uk]=Розпорядник паролів
|
||||
GenericName[zh_CN]=密码管理器
|
||||
GenericName[zh_TW]=密碼管理員
|
||||
Comment=Community-driven port of the Windows application “KeePass Password Safe”
|
||||
Comment[da]=Fællesskabsdrevet port af Windows-programmet “KeePass Password Safe”
|
||||
Comment[et]=Kogukonna arendatav port Windowsi programmist KeePass Password Safe
|
||||
Exec=keepassxc %f
|
||||
TryExec=keepassxc
|
||||
Icon=keepassxc
|
||||
|
@ -269,7 +269,7 @@ void AutoType::executeAutoTypeActions(const Entry* entry, QWidget* hideWindow, c
|
||||
|
||||
/**
|
||||
* Single Autotype entry-point function
|
||||
* Perfom autotype sequence in the active window
|
||||
* Look up the Auto-Type sequence for the given entry then perfom Auto-Type in the active window
|
||||
*/
|
||||
void AutoType::performAutoType(const Entry* entry, QWidget* hideWindow)
|
||||
{
|
||||
@ -285,6 +285,19 @@ void AutoType::performAutoType(const Entry* entry, QWidget* hideWindow)
|
||||
executeAutoTypeActions(entry, hideWindow, sequences.first());
|
||||
}
|
||||
|
||||
/**
|
||||
* Extra Autotype entry-point function
|
||||
* Perfom Auto-Type of the directly specified sequence in the active window
|
||||
*/
|
||||
void AutoType::performAutoTypeWithSequence(const Entry* entry, const QString& sequence, QWidget* hideWindow)
|
||||
{
|
||||
if (!m_plugin) {
|
||||
return;
|
||||
}
|
||||
|
||||
executeAutoTypeActions(entry, hideWindow, sequence);
|
||||
}
|
||||
|
||||
void AutoType::startGlobalAutoType()
|
||||
{
|
||||
m_windowForGlobal = m_plugin->activeWindow();
|
||||
|
@ -48,6 +48,7 @@ public:
|
||||
static bool checkHighDelay(const QString& string);
|
||||
static bool verifyAutoTypeSyntax(const QString& sequence);
|
||||
void performAutoType(const Entry* entry, QWidget* hideWindow = nullptr);
|
||||
void performAutoTypeWithSequence(const Entry* entry, const QString& sequence, QWidget* hideWindow = nullptr);
|
||||
|
||||
inline bool isAvailable()
|
||||
{
|
||||
|
@ -413,7 +413,7 @@ QJsonArray BrowserService::findMatchingEntries(const QString& dbid,
|
||||
}
|
||||
|
||||
// Sort results
|
||||
pwEntries = sortEntries(pwEntries, host, submitUrl);
|
||||
pwEntries = sortEntries(pwEntries, host, submitUrl, url);
|
||||
|
||||
// Fill the list
|
||||
QJsonArray result;
|
||||
@ -698,7 +698,10 @@ void BrowserService::convertAttributesToCustomData(QSharedPointer<Database> db)
|
||||
}
|
||||
}
|
||||
|
||||
QList<Entry*> BrowserService::sortEntries(QList<Entry*>& pwEntries, const QString& host, const QString& entryUrl)
|
||||
QList<Entry*> BrowserService::sortEntries(QList<Entry*>& pwEntries,
|
||||
const QString& host,
|
||||
const QString& entryUrl,
|
||||
const QString& fullUrl)
|
||||
{
|
||||
QUrl url(entryUrl);
|
||||
if (url.scheme().isEmpty()) {
|
||||
@ -712,7 +715,7 @@ QList<Entry*> BrowserService::sortEntries(QList<Entry*>& pwEntries, const QStrin
|
||||
// Build map of prioritized entries
|
||||
QMultiMap<int, Entry*> priorities;
|
||||
for (auto* entry : pwEntries) {
|
||||
priorities.insert(sortPriority(entry, host, submitUrl, baseSubmitUrl), entry);
|
||||
priorities.insert(sortPriority(entry, host, submitUrl, baseSubmitUrl, fullUrl), entry);
|
||||
}
|
||||
|
||||
QList<Entry*> results;
|
||||
@ -895,7 +898,8 @@ Group* BrowserService::getDefaultEntryGroup(const QSharedPointer<Database>& sele
|
||||
int BrowserService::sortPriority(const Entry* entry,
|
||||
const QString& host,
|
||||
const QString& submitUrl,
|
||||
const QString& baseSubmitUrl) const
|
||||
const QString& baseSubmitUrl,
|
||||
const QString& fullUrl) const
|
||||
{
|
||||
QUrl url(entry->url());
|
||||
if (url.scheme().isEmpty()) {
|
||||
@ -914,9 +918,12 @@ int BrowserService::sortPriority(const Entry* entry,
|
||||
if (!url.host().contains(".") && url.host() != "localhost") {
|
||||
return 0;
|
||||
}
|
||||
if (submitUrl == entryURL) {
|
||||
if (fullUrl == entryURL) {
|
||||
return 100;
|
||||
}
|
||||
if (submitUrl == entryURL) {
|
||||
return 95;
|
||||
}
|
||||
if (submitUrl.startsWith(entryURL) && entryURL != host && baseSubmitUrl != entryURL) {
|
||||
return 90;
|
||||
}
|
||||
@ -1025,7 +1032,17 @@ bool BrowserService::handleURL(const QString& entryUrl, const QString& url, cons
|
||||
|
||||
// Match the subdomains with the limited wildcard
|
||||
if (siteQUrl.host().endsWith(entryQUrl.host())) {
|
||||
return true;
|
||||
if (!browserSettings()->bestMatchOnly()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Match the exact subdomain and path, or start of the path when entry's path is longer than plain "/"
|
||||
if (siteQUrl.host() == entryQUrl.host()) {
|
||||
if (siteQUrl.path() == entryQUrl.path()
|
||||
|| (entryQUrl.path().size() > 1 && siteQUrl.path().startsWith(entryQUrl.path()))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
|
@ -119,7 +119,8 @@ private:
|
||||
|
||||
QList<Entry*> searchEntries(const QSharedPointer<Database>& db, const QString& url, const QString& submitUrl);
|
||||
QList<Entry*> searchEntries(const QString& url, const QString& submitUrl, const StringPairList& keyList);
|
||||
QList<Entry*> sortEntries(QList<Entry*>& pwEntries, const QString& host, const QString& submitUrl);
|
||||
QList<Entry*>
|
||||
sortEntries(QList<Entry*>& pwEntries, const QString& host, const QString& submitUrl, const QString& fullUrl);
|
||||
QList<Entry*> confirmEntries(QList<Entry*>& pwEntriesToConfirm,
|
||||
const QString& url,
|
||||
const QString& host,
|
||||
@ -130,8 +131,11 @@ private:
|
||||
QJsonArray getChildrenFromGroup(Group* group);
|
||||
Access checkAccess(const Entry* entry, const QString& host, const QString& submitHost, const QString& realm);
|
||||
Group* getDefaultEntryGroup(const QSharedPointer<Database>& selectedDb = {});
|
||||
int
|
||||
sortPriority(const Entry* entry, const QString& host, const QString& submitUrl, const QString& baseSubmitUrl) const;
|
||||
int sortPriority(const Entry* entry,
|
||||
const QString& host,
|
||||
const QString& submitUrl,
|
||||
const QString& baseSubmitUrl,
|
||||
const QString& fullUrl) const;
|
||||
bool schemeFound(const QString& url);
|
||||
bool removeFirstDomain(QString& hostname);
|
||||
bool handleURL(const QString& entryUrl, const QString& url, const QString& submitUrl);
|
||||
|
@ -27,7 +27,7 @@
|
||||
|
||||
Info::Info()
|
||||
{
|
||||
name = QString("db-show");
|
||||
name = QString("db-info");
|
||||
description = QObject::tr("Show a database's information.");
|
||||
}
|
||||
|
||||
|
@ -481,6 +481,8 @@ void Entry::updateTotp()
|
||||
m_attributes->value(Totp::ATTRIBUTE_SEED));
|
||||
} else if (m_attributes->contains(Totp::ATTRIBUTE_OTP)) {
|
||||
m_data.totpSettings = Totp::parseSettings(m_attributes->value(Totp::ATTRIBUTE_OTP));
|
||||
} else {
|
||||
m_data.totpSettings.reset();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -51,6 +51,13 @@ ScreenLockListenerDBus::ScreenLockListenerDBus(QWidget* parent)
|
||||
this, // receiver
|
||||
SLOT(gnomeSessionStatusChanged(uint)));
|
||||
|
||||
sessionBus.connect("org.xfce.ScreenSaver", // service
|
||||
"/org/xfce/ScreenSaver", // path
|
||||
"org.xfce.ScreenSaver", // interface
|
||||
"ActiveChanged", // signal name
|
||||
this, // receiver
|
||||
SLOT(freedesktopScreenSaver(bool)));
|
||||
|
||||
systemBus.connect("org.freedesktop.login1", // service
|
||||
"/org/freedesktop/login1", // path
|
||||
"org.freedesktop.login1.Manager", // interface
|
||||
|
@ -331,11 +331,15 @@ namespace Tools
|
||||
|
||||
#if defined(Q_OS_WIN)
|
||||
QRegularExpression varRe("\\%([A-Za-z][A-Za-z0-9_]*)\\%");
|
||||
QString homeEnv = "USERPROFILE";
|
||||
#else
|
||||
QRegularExpression varRe("\\$([A-Za-z][A-Za-z0-9_]*)");
|
||||
subbed.replace("~", environment.value("HOME"));
|
||||
QString homeEnv = "HOME";
|
||||
#endif
|
||||
|
||||
if (subbed.startsWith("~/") || subbed.startsWith("~\\"))
|
||||
subbed.replace(0, 1, environment.value(homeEnv));
|
||||
|
||||
QRegularExpressionMatch match;
|
||||
|
||||
do {
|
||||
|
@ -71,6 +71,8 @@ bool Translator::installTranslator(const QStringList& languages, const QString&
|
||||
QScopedPointer<QTranslator> translator(new QTranslator(qApp));
|
||||
if (translator->load(locale, "keepassx_", "", path)) {
|
||||
return QCoreApplication::installTranslator(translator.take());
|
||||
} else if (translator->load(locale, "keepassx_", "", QLibraryInfo::location(QLibraryInfo::TranslationsPath))) {
|
||||
return QCoreApplication::installTranslator(translator.take());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -27,6 +27,7 @@
|
||||
#include "core/Global.h"
|
||||
#include "core/Resources.h"
|
||||
#include "core/Translator.h"
|
||||
#include "gui/MainWindow.h"
|
||||
#include "gui/osutils/OSUtils.h"
|
||||
|
||||
#include "MessageBox.h"
|
||||
@ -324,7 +325,15 @@ void ApplicationSettingsWidget::saveSettings()
|
||||
config()->set(Config::AutoTypeEntryURLMatch, m_generalUi->autoTypeEntryURLMatchCheckBox->isChecked());
|
||||
config()->set(Config::FaviconDownloadTimeout, m_generalUi->faviconTimeoutSpinBox->value());
|
||||
|
||||
config()->set(Config::GUI_Language, m_generalUi->languageComboBox->currentData().toString());
|
||||
auto language = m_generalUi->languageComboBox->currentData().toString();
|
||||
if (config()->get(Config::GUI_Language) != language) {
|
||||
QTimer::singleShot(200, [] {
|
||||
getMainWindow()->restartApp(
|
||||
tr("You must restart the application to set the new language. Would you like to restart now?"));
|
||||
});
|
||||
}
|
||||
config()->set(Config::GUI_Language, language);
|
||||
|
||||
config()->set(Config::GUI_MovableToolbar, m_generalUi->toolbarMovableCheckBox->isChecked());
|
||||
config()->set(Config::GUI_MonospaceNotes, m_generalUi->monospaceNotesCheckBox->isChecked());
|
||||
|
||||
|
@ -799,6 +799,38 @@ void DatabaseWidget::performAutoType()
|
||||
}
|
||||
}
|
||||
|
||||
void DatabaseWidget::performAutoTypeUsername()
|
||||
{
|
||||
auto currentEntry = currentSelectedEntry();
|
||||
if (currentEntry) {
|
||||
autoType()->performAutoTypeWithSequence(currentEntry, QStringLiteral("{USERNAME}"), window());
|
||||
}
|
||||
}
|
||||
|
||||
void DatabaseWidget::performAutoTypeUsernameEnter()
|
||||
{
|
||||
auto currentEntry = currentSelectedEntry();
|
||||
if (currentEntry) {
|
||||
autoType()->performAutoTypeWithSequence(currentEntry, QStringLiteral("{USERNAME}{ENTER}"), window());
|
||||
}
|
||||
}
|
||||
|
||||
void DatabaseWidget::performAutoTypePassword()
|
||||
{
|
||||
auto currentEntry = currentSelectedEntry();
|
||||
if (currentEntry) {
|
||||
autoType()->performAutoTypeWithSequence(currentEntry, QStringLiteral("{PASSWORD}"), window());
|
||||
}
|
||||
}
|
||||
|
||||
void DatabaseWidget::performAutoTypePasswordEnter()
|
||||
{
|
||||
auto currentEntry = currentSelectedEntry();
|
||||
if (currentEntry) {
|
||||
autoType()->performAutoTypeWithSequence(currentEntry, QStringLiteral("{PASSWORD}{ENTER}"), window());
|
||||
}
|
||||
}
|
||||
|
||||
void DatabaseWidget::openUrl()
|
||||
{
|
||||
auto currentEntry = currentSelectedEntry();
|
||||
@ -1813,7 +1845,7 @@ bool DatabaseWidget::save()
|
||||
m_blockAutoSave = true;
|
||||
++m_saveAttempts;
|
||||
|
||||
auto focusWidget = qApp->focusWidget();
|
||||
QPointer<QWidget> focusWidget(qApp->focusWidget());
|
||||
|
||||
// TODO: Make this async
|
||||
// Lock out interactions
|
||||
@ -1887,7 +1919,7 @@ bool DatabaseWidget::saveAs()
|
||||
|
||||
bool ok = false;
|
||||
if (!newFilePath.isEmpty()) {
|
||||
auto focusWidget = qApp->focusWidget();
|
||||
QPointer<QWidget> focusWidget(qApp->focusWidget());
|
||||
|
||||
// Lock out interactions
|
||||
m_entryView->setDisabled(true);
|
||||
|
@ -186,6 +186,10 @@ public slots:
|
||||
void removeFromAgent();
|
||||
#endif
|
||||
void performAutoType();
|
||||
void performAutoTypeUsername();
|
||||
void performAutoTypeUsernameEnter();
|
||||
void performAutoTypePassword();
|
||||
void performAutoTypePasswordEnter();
|
||||
void openUrl();
|
||||
void downloadSelectedFavicons();
|
||||
void downloadAllFavicons();
|
||||
|
@ -126,6 +126,7 @@ MainWindow::MainWindow()
|
||||
m_entryContextMenu->addAction(m_ui->menuEntryTotp->menuAction());
|
||||
m_entryContextMenu->addSeparator();
|
||||
m_entryContextMenu->addAction(m_ui->actionEntryAutoType);
|
||||
m_entryContextMenu->addAction(m_ui->menuEntryAutoTypeWithSequence->menuAction());
|
||||
m_entryContextMenu->addSeparator();
|
||||
m_entryContextMenu->addAction(m_ui->actionEntryEdit);
|
||||
m_entryContextMenu->addAction(m_ui->actionEntryClone);
|
||||
@ -220,7 +221,12 @@ MainWindow::MainWindow()
|
||||
m_ui->toolbarSeparator->setVisible(false);
|
||||
m_showToolbarSeparator = config()->get(Config::GUI_ApplicationTheme).toString() != "classic";
|
||||
|
||||
m_ui->actionEntryAutoType->setVisible(autoType()->isAvailable());
|
||||
bool isAutoTypeAvailable = autoType()->isAvailable();
|
||||
m_ui->actionEntryAutoType->setVisible(isAutoTypeAvailable);
|
||||
m_ui->actionEntryAutoTypeUsername->setVisible(isAutoTypeAvailable);
|
||||
m_ui->actionEntryAutoTypeUsernameEnter->setVisible(isAutoTypeAvailable);
|
||||
m_ui->actionEntryAutoTypePassword->setVisible(isAutoTypeAvailable);
|
||||
m_ui->actionEntryAutoTypePasswordEnter->setVisible(isAutoTypeAvailable);
|
||||
|
||||
m_inactivityTimer = new InactivityTimer(this);
|
||||
connect(m_inactivityTimer, SIGNAL(inactivityDetected()), this, SLOT(lockDatabasesAfterInactivity()));
|
||||
@ -352,6 +358,11 @@ MainWindow::MainWindow()
|
||||
m_ui->actionEntryEdit->setIcon(resources()->icon("entry-edit"));
|
||||
m_ui->actionEntryDelete->setIcon(resources()->icon("entry-delete"));
|
||||
m_ui->actionEntryAutoType->setIcon(resources()->icon("auto-type"));
|
||||
m_ui->menuEntryAutoTypeWithSequence->setIcon(resources()->icon("auto-type"));
|
||||
m_ui->actionEntryAutoTypeUsername->setIcon(resources()->icon("auto-type"));
|
||||
m_ui->actionEntryAutoTypeUsernameEnter->setIcon(resources()->icon("auto-type"));
|
||||
m_ui->actionEntryAutoTypePassword->setIcon(resources()->icon("auto-type"));
|
||||
m_ui->actionEntryAutoTypePasswordEnter->setIcon(resources()->icon("auto-type"));
|
||||
m_ui->actionEntryMoveUp->setIcon(resources()->icon("move-up"));
|
||||
m_ui->actionEntryMoveDown->setIcon(resources()->icon("move-down"));
|
||||
m_ui->actionEntryCopyUsername->setIcon(resources()->icon("username-copy"));
|
||||
@ -446,6 +457,14 @@ MainWindow::MainWindow()
|
||||
m_actionMultiplexer.connect(m_ui->actionEntryCopyURL, SIGNAL(triggered()), SLOT(copyURL()));
|
||||
m_actionMultiplexer.connect(m_ui->actionEntryCopyNotes, SIGNAL(triggered()), SLOT(copyNotes()));
|
||||
m_actionMultiplexer.connect(m_ui->actionEntryAutoType, SIGNAL(triggered()), SLOT(performAutoType()));
|
||||
m_actionMultiplexer.connect(
|
||||
m_ui->actionEntryAutoTypeUsername, SIGNAL(triggered()), SLOT(performAutoTypeUsername()));
|
||||
m_actionMultiplexer.connect(
|
||||
m_ui->actionEntryAutoTypeUsernameEnter, SIGNAL(triggered()), SLOT(performAutoTypeUsernameEnter()));
|
||||
m_actionMultiplexer.connect(
|
||||
m_ui->actionEntryAutoTypePassword, SIGNAL(triggered()), SLOT(performAutoTypePassword()));
|
||||
m_actionMultiplexer.connect(
|
||||
m_ui->actionEntryAutoTypePasswordEnter, SIGNAL(triggered()), SLOT(performAutoTypePasswordEnter()));
|
||||
m_actionMultiplexer.connect(m_ui->actionEntryOpenUrl, SIGNAL(triggered()), SLOT(openUrl()));
|
||||
m_actionMultiplexer.connect(m_ui->actionEntryDownloadIcon, SIGNAL(triggered()), SLOT(downloadSelectedFavicons()));
|
||||
#ifdef WITH_XC_SSHAGENT
|
||||
@ -711,6 +730,13 @@ void MainWindow::setMenuActionState(DatabaseWidget::Mode mode)
|
||||
m_ui->menuEntryCopyAttribute->setEnabled(singleEntrySelected);
|
||||
m_ui->menuEntryTotp->setEnabled(singleEntrySelected);
|
||||
m_ui->actionEntryAutoType->setEnabled(singleEntrySelected);
|
||||
m_ui->menuEntryAutoTypeWithSequence->setEnabled(singleEntrySelected);
|
||||
m_ui->actionEntryAutoTypeUsername->setEnabled(singleEntrySelected && dbWidget->currentEntryHasUsername());
|
||||
m_ui->actionEntryAutoTypeUsernameEnter->setEnabled(singleEntrySelected
|
||||
&& dbWidget->currentEntryHasUsername());
|
||||
m_ui->actionEntryAutoTypePassword->setEnabled(singleEntrySelected && dbWidget->currentEntryHasPassword());
|
||||
m_ui->actionEntryAutoTypePasswordEnter->setEnabled(singleEntrySelected
|
||||
&& dbWidget->currentEntryHasPassword());
|
||||
m_ui->actionEntryOpenUrl->setEnabled(singleEntrySelected && dbWidget->currentEntryHasUrl());
|
||||
m_ui->actionEntryTotp->setEnabled(singleEntrySelected && dbWidget->currentEntryHasTotp());
|
||||
m_ui->actionEntryCopyTotp->setEnabled(singleEntrySelected && dbWidget->currentEntryHasTotp());
|
||||
@ -761,6 +787,7 @@ void MainWindow::setMenuActionState(DatabaseWidget::Mode mode)
|
||||
m_ui->actionEntryCopyURL,
|
||||
m_ui->actionEntryOpenUrl,
|
||||
m_ui->actionEntryAutoType,
|
||||
m_ui->menuEntryAutoTypeWithSequence->menuAction(),
|
||||
m_ui->actionEntryDownloadIcon,
|
||||
m_ui->actionEntryCopyNotes,
|
||||
m_ui->actionEntryCopyTitle,
|
||||
@ -1679,17 +1706,20 @@ void MainWindow::initViewMenu()
|
||||
}
|
||||
}
|
||||
|
||||
connect(themeActions, &QActionGroup::triggered, this, [this](QAction* action) {
|
||||
if (action->data() != config()->get(Config::GUI_ApplicationTheme)) {
|
||||
config()->set(Config::GUI_ApplicationTheme, action->data());
|
||||
connect(themeActions, &QActionGroup::triggered, this, [this, theme](QAction* action) {
|
||||
config()->set(Config::GUI_ApplicationTheme, action->data());
|
||||
if (action->data() != theme) {
|
||||
restartApp(tr("You must restart the application to apply this setting. Would you like to restart now?"));
|
||||
}
|
||||
});
|
||||
|
||||
m_ui->actionCompactMode->setChecked(config()->get(Config::GUI_CompactMode).toBool());
|
||||
connect(m_ui->actionCompactMode, &QAction::toggled, this, [this](bool checked) {
|
||||
bool compact = config()->get(Config::GUI_CompactMode).toBool();
|
||||
m_ui->actionCompactMode->setChecked(compact);
|
||||
connect(m_ui->actionCompactMode, &QAction::toggled, this, [this, compact](bool checked) {
|
||||
config()->set(Config::GUI_CompactMode, checked);
|
||||
restartApp(tr("You must restart the application to apply this setting. Would you like to restart now?"));
|
||||
if (checked != compact) {
|
||||
restartApp(tr("You must restart the application to apply this setting. Would you like to restart now?"));
|
||||
}
|
||||
});
|
||||
|
||||
m_ui->actionShowToolbar->setChecked(!config()->get(Config::GUI_HideToolbar).toBool());
|
||||
|
@ -310,6 +310,18 @@
|
||||
<addaction name="actionEntryTotpQRCode"/>
|
||||
<addaction name="actionEntrySetupTotp"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuEntryAutoTypeWithSequence">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Perform Auto-Type Sequence</string>
|
||||
</property>
|
||||
<addaction name="actionEntryAutoTypeUsername"/>
|
||||
<addaction name="actionEntryAutoTypeUsernameEnter"/>
|
||||
<addaction name="actionEntryAutoTypePassword"/>
|
||||
<addaction name="actionEntryAutoTypePasswordEnter"/>
|
||||
</widget>
|
||||
<addaction name="actionEntryNew"/>
|
||||
<addaction name="actionEntryEdit"/>
|
||||
<addaction name="actionEntryClone"/>
|
||||
@ -324,6 +336,7 @@
|
||||
<addaction name="menuEntryTotp"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionEntryAutoType"/>
|
||||
<addaction name="menuEntryAutoTypeWithSequence"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionEntryOpenUrl"/>
|
||||
<addaction name="actionEntryDownloadIcon"/>
|
||||
@ -680,6 +693,38 @@
|
||||
<string>Perform &Auto-Type</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionEntryAutoTypeUsername">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>{USERNAME}</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionEntryAutoTypeUsernameEnter">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>{USERNAME}{ENTER}</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionEntryAutoTypePassword">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>{PASSWORD}</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionEntryAutoTypePasswordEnter">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>{PASSWORD}{ENTER}</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionEntryDownloadIcon">
|
||||
<property name="text">
|
||||
<string>Download &Favicon</string>
|
||||
|
@ -50,21 +50,28 @@ PasswordEdit::PasswordEdit(QWidget* parent)
|
||||
passwordFont.setLetterSpacing(QFont::PercentageSpacing, 110);
|
||||
setFont(passwordFont);
|
||||
|
||||
// Prevent conflicts with global Mac shortcuts (force Control on all platforms)
|
||||
#ifdef Q_OS_MAC
|
||||
auto modifier = Qt::META;
|
||||
#else
|
||||
auto modifier = Qt::CTRL;
|
||||
#endif
|
||||
|
||||
m_toggleVisibleAction = new QAction(
|
||||
resources()->icon("password-show-off"),
|
||||
tr("Toggle Password (%1)").arg(QKeySequence(Qt::CTRL + Qt::Key_H).toString(QKeySequence::NativeText)),
|
||||
tr("Toggle Password (%1)").arg(QKeySequence(modifier + Qt::Key_H).toString(QKeySequence::NativeText)),
|
||||
nullptr);
|
||||
m_toggleVisibleAction->setCheckable(true);
|
||||
m_toggleVisibleAction->setShortcut(Qt::CTRL + Qt::Key_H);
|
||||
m_toggleVisibleAction->setShortcut(modifier + Qt::Key_H);
|
||||
m_toggleVisibleAction->setShortcutContext(Qt::WidgetShortcut);
|
||||
addAction(m_toggleVisibleAction, QLineEdit::TrailingPosition);
|
||||
connect(m_toggleVisibleAction, &QAction::triggered, this, &PasswordEdit::setShowPassword);
|
||||
|
||||
m_passwordGeneratorAction = new QAction(
|
||||
resources()->icon("password-generator"),
|
||||
tr("Generate Password (%1)").arg(QKeySequence(Qt::CTRL + Qt::Key_G).toString(QKeySequence::NativeText)),
|
||||
tr("Generate Password (%1)").arg(QKeySequence(modifier + Qt::Key_G).toString(QKeySequence::NativeText)),
|
||||
nullptr);
|
||||
m_passwordGeneratorAction->setShortcut(Qt::CTRL + Qt::Key_G);
|
||||
m_passwordGeneratorAction->setShortcut(modifier + Qt::Key_G);
|
||||
m_passwordGeneratorAction->setShortcutContext(Qt::WidgetShortcut);
|
||||
addAction(m_passwordGeneratorAction, QLineEdit::TrailingPosition);
|
||||
m_passwordGeneratorAction->setVisible(false);
|
||||
|
@ -447,12 +447,8 @@ void SSHAgent::databaseLocked()
|
||||
if (!removeIdentity(key)) {
|
||||
emit error(m_error);
|
||||
}
|
||||
it = m_addedKeys.erase(it);
|
||||
} else {
|
||||
// don't remove it yet
|
||||
m_addedKeys[key].second = false;
|
||||
++it;
|
||||
}
|
||||
it = m_addedKeys.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -113,7 +113,7 @@ QSharedPointer<Totp::Settings> Totp::parseSettings(const QString& rawSettings, c
|
||||
}
|
||||
|
||||
// Bound digits and step
|
||||
settings->digits = qMax(1u, settings->digits);
|
||||
settings->digits = qBound(1u, settings->digits, 10u);
|
||||
settings->step = qBound(1u, settings->step, 60u);
|
||||
|
||||
// Detect custom settings, used by setup GUI
|
||||
|
@ -38,6 +38,7 @@ void TestBrowser::initTestCase()
|
||||
{
|
||||
QVERIFY(Crypto::init());
|
||||
m_browserService = browserService();
|
||||
browserSettings()->setBestMatchOnly(false);
|
||||
}
|
||||
|
||||
void TestBrowser::init()
|
||||
@ -130,6 +131,7 @@ void TestBrowser::testSortPriority()
|
||||
QString host = "github.com";
|
||||
QString submitUrl = "https://github.com/session";
|
||||
QString baseSubmitUrl = "https://github.com";
|
||||
QString fullUrl = "https://github.com/login";
|
||||
|
||||
QScopedPointer<Entry> entry1(new Entry());
|
||||
QScopedPointer<Entry> entry2(new Entry());
|
||||
@ -141,6 +143,7 @@ void TestBrowser::testSortPriority()
|
||||
QScopedPointer<Entry> entry8(new Entry());
|
||||
QScopedPointer<Entry> entry9(new Entry());
|
||||
QScopedPointer<Entry> entry10(new Entry());
|
||||
QScopedPointer<Entry> entry11(new Entry());
|
||||
|
||||
entry1->setUrl("https://github.com/login");
|
||||
entry2->setUrl("https://github.com/login");
|
||||
@ -152,18 +155,20 @@ void TestBrowser::testSortPriority()
|
||||
entry8->setUrl("github.com/login");
|
||||
entry9->setUrl("https://github"); // Invalid URL
|
||||
entry10->setUrl("github.com");
|
||||
entry11->setUrl("https://github.com/login"); // Exact match
|
||||
|
||||
// The extension uses the submitUrl as default for comparison
|
||||
auto res1 = m_browserService->sortPriority(entry1.data(), host, "https://github.com/login", baseSubmitUrl);
|
||||
auto res2 = m_browserService->sortPriority(entry2.data(), host, submitUrl, baseSubmitUrl);
|
||||
auto res3 = m_browserService->sortPriority(entry3.data(), host, submitUrl, baseSubmitUrl);
|
||||
auto res4 = m_browserService->sortPriority(entry4.data(), host, submitUrl, baseSubmitUrl);
|
||||
auto res5 = m_browserService->sortPriority(entry5.data(), host, submitUrl, baseSubmitUrl);
|
||||
auto res6 = m_browserService->sortPriority(entry6.data(), host, submitUrl, baseSubmitUrl);
|
||||
auto res7 = m_browserService->sortPriority(entry7.data(), host, submitUrl, baseSubmitUrl);
|
||||
auto res8 = m_browserService->sortPriority(entry8.data(), host, submitUrl, baseSubmitUrl);
|
||||
auto res9 = m_browserService->sortPriority(entry9.data(), host, submitUrl, baseSubmitUrl);
|
||||
auto res10 = m_browserService->sortPriority(entry10.data(), host, submitUrl, baseSubmitUrl);
|
||||
auto res1 = m_browserService->sortPriority(entry1.data(), host, "https://github.com/login", baseSubmitUrl, fullUrl);
|
||||
auto res2 = m_browserService->sortPriority(entry2.data(), host, submitUrl, baseSubmitUrl, baseSubmitUrl);
|
||||
auto res3 = m_browserService->sortPriority(entry3.data(), host, submitUrl, baseSubmitUrl, fullUrl);
|
||||
auto res4 = m_browserService->sortPriority(entry4.data(), host, submitUrl, baseSubmitUrl, fullUrl);
|
||||
auto res5 = m_browserService->sortPriority(entry5.data(), host, submitUrl, baseSubmitUrl, fullUrl);
|
||||
auto res6 = m_browserService->sortPriority(entry6.data(), host, submitUrl, baseSubmitUrl, fullUrl);
|
||||
auto res7 = m_browserService->sortPriority(entry7.data(), host, submitUrl, baseSubmitUrl, fullUrl);
|
||||
auto res8 = m_browserService->sortPriority(entry8.data(), host, submitUrl, baseSubmitUrl, fullUrl);
|
||||
auto res9 = m_browserService->sortPriority(entry9.data(), host, submitUrl, baseSubmitUrl, fullUrl);
|
||||
auto res10 = m_browserService->sortPriority(entry10.data(), host, submitUrl, baseSubmitUrl, fullUrl);
|
||||
auto res11 = m_browserService->sortPriority(entry11.data(), host, submitUrl, baseSubmitUrl, fullUrl);
|
||||
|
||||
QCOMPARE(res1, 100);
|
||||
QCOMPARE(res2, 40);
|
||||
@ -175,6 +180,7 @@ void TestBrowser::testSortPriority()
|
||||
QCOMPARE(res8, 0);
|
||||
QCOMPARE(res9, 0);
|
||||
QCOMPARE(res10, 0);
|
||||
QCOMPARE(res11, 100);
|
||||
}
|
||||
|
||||
void TestBrowser::testSearchEntries()
|
||||
@ -382,8 +388,8 @@ void TestBrowser::testSortEntries()
|
||||
auto entries = createEntries(urls, root);
|
||||
|
||||
browserSettings()->setBestMatchOnly(false);
|
||||
auto result =
|
||||
m_browserService->sortEntries(entries, "github.com", "https://github.com/session"); // entries, host, submitUrl
|
||||
auto result = m_browserService->sortEntries(
|
||||
entries, "github.com", "https://github.com/session", "https://github.com"); // entries, host, submitUrl
|
||||
QCOMPARE(result.size(), 10);
|
||||
QCOMPARE(result[0]->username(), QString("User 2"));
|
||||
QCOMPARE(result[0]->url(), QString("https://github.com/"));
|
||||
@ -393,6 +399,15 @@ void TestBrowser::testSortEntries()
|
||||
QCOMPARE(result[2]->url(), QString("https://github.com/login"));
|
||||
QCOMPARE(result[3]->username(), QString("User 3"));
|
||||
QCOMPARE(result[3]->url(), QString("github.com/login"));
|
||||
|
||||
// Test with a perfect match. That should be first in the list.
|
||||
result = m_browserService->sortEntries(
|
||||
entries, "github.com", "https://github.com/session", "https://github.com/login_page");
|
||||
QCOMPARE(result.size(), 10);
|
||||
QCOMPARE(result[0]->username(), QString("User 0"));
|
||||
QCOMPARE(result[0]->url(), QString("https://github.com/login_page"));
|
||||
QCOMPARE(result[1]->username(), QString("User 2"));
|
||||
QCOMPARE(result[1]->url(), QString("https://github.com/"));
|
||||
}
|
||||
|
||||
QList<Entry*> TestBrowser::createEntries(QStringList& urls, Group* root) const
|
||||
@ -429,3 +444,58 @@ void TestBrowser::testValidURLs()
|
||||
QCOMPARE(Tools::checkUrlValid(i.key()), i.value());
|
||||
}
|
||||
}
|
||||
|
||||
void TestBrowser::testBestMatchingCredentials()
|
||||
{
|
||||
auto db = QSharedPointer<Database>::create();
|
||||
auto* root = db->rootGroup();
|
||||
|
||||
// Test with simple URL entries
|
||||
QStringList urls = {"https://github.com/loginpage", "https://github.com/justsomepage", "https://github.com/"};
|
||||
|
||||
auto entries = createEntries(urls, root);
|
||||
|
||||
browserSettings()->setBestMatchOnly(true);
|
||||
|
||||
auto result = m_browserService->searchEntries(db, "https://github.com/loginpage", "https://github.com/loginpage");
|
||||
QCOMPARE(result.size(), 1);
|
||||
QCOMPARE(result[0]->url(), QString("https://github.com/loginpage"));
|
||||
|
||||
result = m_browserService->searchEntries(db, "https://github.com/justsomepage", "https://github.com/justsomepage");
|
||||
QCOMPARE(result.size(), 1);
|
||||
QCOMPARE(result[0]->url(), QString("https://github.com/justsomepage"));
|
||||
|
||||
result = m_browserService->searchEntries(db, "https://github.com/", "https://github.com/");
|
||||
m_browserService->sortEntries(entries, "github.com", "https://github.com/", "https://github.com/");
|
||||
QCOMPARE(result.size(), 1);
|
||||
QCOMPARE(result[0]->url(), QString("https://github.com/"));
|
||||
|
||||
browserSettings()->setBestMatchOnly(false);
|
||||
result = m_browserService->searchEntries(db, "https://github.com/loginpage", "https://github.com/loginpage");
|
||||
QCOMPARE(result.size(), 3);
|
||||
QCOMPARE(result[0]->url(), QString("https://github.com/loginpage"));
|
||||
|
||||
// Test with subdomains
|
||||
QStringList subdomainsUrls = {"https://sub.github.com/loginpage",
|
||||
"https://sub.github.com/justsomepage",
|
||||
"https://bus.github.com/justsomepage"};
|
||||
|
||||
entries = createEntries(subdomainsUrls, root);
|
||||
|
||||
browserSettings()->setBestMatchOnly(true);
|
||||
|
||||
result = m_browserService->searchEntries(
|
||||
db, "https://sub.github.com/justsomepage", "https://sub.github.com/justsomepage");
|
||||
QCOMPARE(result.size(), 1);
|
||||
QCOMPARE(result[0]->url(), QString("https://sub.github.com/justsomepage"));
|
||||
|
||||
result = m_browserService->searchEntries(db, "https://github.com/justsomepage", "https://github.com/justsomepage");
|
||||
QCOMPARE(result.size(), 1);
|
||||
QCOMPARE(result[0]->url(), QString("https://github.com/justsomepage"));
|
||||
|
||||
result = m_browserService->searchEntries(db,
|
||||
"https://sub.github.com/justsomepage?wehavesomeextra=here",
|
||||
"https://sub.github.com/justsomepage?wehavesomeextra=here");
|
||||
QCOMPARE(result.size(), 1);
|
||||
QCOMPARE(result[0]->url(), QString("https://sub.github.com/justsomepage"));
|
||||
}
|
||||
|
@ -47,6 +47,7 @@ private slots:
|
||||
void testSubdomainsAndPaths();
|
||||
void testSortEntries();
|
||||
void testValidURLs();
|
||||
void testBestMatchingCredentials();
|
||||
|
||||
private:
|
||||
QList<Entry*> createEntries(QStringList& urls, Group* root) const;
|
||||
|
@ -72,10 +72,14 @@ void TestTools::testEnvSubstitute()
|
||||
#if defined(Q_OS_WIN)
|
||||
environment.insert("HOMEDRIVE", "C:");
|
||||
environment.insert("HOMEPATH", "\\Users\\User");
|
||||
environment.insert("USERPROFILE", "C:\\Users\\User");
|
||||
|
||||
QCOMPARE(Tools::envSubstitute("%HOMEDRIVE%%HOMEPATH%\\.ssh\\id_rsa", environment),
|
||||
QString("C:\\Users\\User\\.ssh\\id_rsa"));
|
||||
QCOMPARE(Tools::envSubstitute("start%EMPTY%%EMPTY%%%HOMEDRIVE%%end", environment), QString("start%C:%end"));
|
||||
QCOMPARE(Tools::envSubstitute("%USERPROFILE%\\.ssh\\id_rsa", environment),
|
||||
QString("C:\\Users\\User\\.ssh\\id_rsa"));
|
||||
QCOMPARE(Tools::envSubstitute("~\\.ssh\\id_rsa", environment), QString("C:\\Users\\User\\.ssh\\id_rsa"));
|
||||
#else
|
||||
environment.insert("HOME", QString("/home/user"));
|
||||
environment.insert("USER", QString("user"));
|
||||
|
Loading…
Reference in New Issue
Block a user