Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,14 @@ option(KPXC_MINIMAL "Build KeePassXC with the minimal feature set required for b
option(KPXC_FEATURE_BROWSER "Browser integration and passkeys support" ON)
option(KPXC_FEATURE_SSHAGENT "SSH Agent integration" ON)
option(KPXC_FEATURE_FDOSECRETS "freedesktop.org Secret Service integration; replace system keyring" ON)
option(KPXC_FEATURE_URLOVERRIDE "Global URL scheme override support" ON)

if(KPXC_MINIMAL)
# Disable advanced features in minimal mode
set(KPXC_FEATURE_BROWSER OFF)
set(KPXC_FEATURE_SSHAGENT OFF)
set(KPXC_FEATURE_FDOSECRETS OFF)
set(KPXC_FEATURE_URLOVERRIDE OFF)
endif()

# Minor Feature Flags
Expand All @@ -82,6 +84,7 @@ endif()
# Define feature summaries
add_feature_info("Browser" KPXC_FEATURE_BROWSER "Browser integration and passkeys support")
add_feature_info("SSH Agent" KPXC_FEATURE_SSHAGENT "SSH Agent integration")
add_feature_info("URL Override" KPXC_FEATURE_URLOVERRIDE "Global URL scheme override support")
if(UNIX AND NOT APPLE)
add_feature_info("Secret Service" KPXC_FEATURE_FDOSECRETS "Replace system keyring with freedesktop.org Secret Service integration")
endif()
Expand Down
1 change: 1 addition & 0 deletions INSTALL.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ KeePassXC comes with a variety of build options that can turn on/off features. E
-DKPXC_FEATURE_BROWSER=[ON|OFF] Browser integration and passkeys support (default: ON)
-DKPXC_FEATURE_SSHAGENT=[ON|OFF] SSH Agent integration (default: ON)
-DKPXC_FEATURE_FDOSECRETS=[ON|OFF] (Linux Only) freedesktop.org Secret Service integration; replace system keyring (default:ON)
-DKPXC_FEATURE_URLOVERRIDE=[ON|OFF] Global URL scheme override support (default: ON)

-DKPXC_FEATURE_NETWORK=[ON|OFF] Include code that reaches out to external networks (e.g. downloading icons) (default: ON)
-DKPXC_FEATURE_UPDATES=[ON|OFF] Include automatic update checks; disable for managed distributions (requires networking) (default: ON)
Expand Down
8 changes: 7 additions & 1 deletion src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,11 @@ if(KPXC_FEATURE_FDOSECRETS)
set(fdosecrets_LIB fdosecrets)
endif()

add_subdirectory(urloverride)
if(KPXC_FEATURE_URLOVERRIDE)
set(urloverride_LIB urloverride)
endif()

configure_file(config-keepassx.h.in ${CMAKE_CURRENT_BINARY_DIR}/config-keepassx.h)
configure_file(git-info.h.in ${CMAKE_CURRENT_BINARY_DIR}/git-info.h)

Expand Down Expand Up @@ -374,7 +379,8 @@ target_link_libraries(keepassxc_gui
${browser_LIB}
${fdosecrets_LIB}
${keeshare_LIB}
${sshagent_LIB})
${sshagent_LIB}
${urloverride_LIB})

if(APPLE)
target_link_libraries(keepassxc_gui "-framework Foundation -framework AppKit -framework Carbon -framework Security -framework LocalAuthentication -framework ScreenCaptureKit")
Expand Down
1 change: 1 addition & 0 deletions src/config-keepassx.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#cmakedefine KPXC_FEATURE_BROWSER
#cmakedefine KPXC_FEATURE_SSHAGENT
#cmakedefine KPXC_FEATURE_FDOSECRETS
#cmakedefine KPXC_FEATURE_URLOVERRIDE

/* Minor Features */
#cmakedefine KPXC_FEATURE_NETWORK
Expand Down
4 changes: 3 additions & 1 deletion src/core/Config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,9 @@ static const QHash<Config::ConfigKey, ConfigDirective> configStrings = {

// Messages
{Config::Messages_NoLegacyKeyFileWarning, {QS("Messages/NoLegacyKeyFileWarning"), Roaming, false}},
{Config::Messages_HidePreReleaseWarning, {QS("Messages/HidePreReleaseWarning"), Local, {}}}};
{Config::Messages_HidePreReleaseWarning, {QS("Messages/HidePreReleaseWarning"), Local, {}}},

{Config::UrlOverride_Rules, {QS("UrlOverride/Rules"), Roaming, {}}}};

// clang-format on

Expand Down
2 changes: 2 additions & 0 deletions src/core/Config.h
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,8 @@ class Config : public QObject
Messages_NoLegacyKeyFileWarning,
Messages_HidePreReleaseWarning,

UrlOverride_Rules,

// Special internal value
Deleted
};
Expand Down
22 changes: 19 additions & 3 deletions src/gui/DatabaseWidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@
#include "remote/RemoteHandler.h"
#include "remote/RemoteSettings.h"

#ifdef KPXC_FEATURE_URLOVERRIDE
#include "urloverride/UrlOverride.h"
#endif

#ifdef KPXC_FEATURE_NETWORK
#include "gui/IconDownloaderDialog.h"
#endif
Expand Down Expand Up @@ -1001,14 +1005,22 @@ void DatabaseWidget::openUrlForEntry(Entry* entry)
return;
}

QString cmdString = entry->resolveMultiplePlaceholders(entry->url());
QString rawTemplate = entry->url();
#ifdef KPXC_FEATURE_URLOVERRIDE
const auto overrideCommand = UrlOverride::findCommand(rawTemplate);
if (!overrideCommand.isEmpty()) {
rawTemplate = overrideCommand;
}
#endif
QString cmdString = entry->resolveMultiplePlaceholders(rawTemplate);

if (cmdString.startsWith("cmd://")) {
// check if decision to execute command was stored
bool launch = (entry->attributes()->value(EntryAttributes::RememberCmdExecAttr) == "1");

// otherwise ask user
if (!launch && cmdString.length() > 6) {
QString cmdTruncated = entry->resolveMultiplePlaceholders(entry->maskPasswordPlaceholders(entry->url()));
QString cmdTruncated = entry->resolveMultiplePlaceholders(entry->maskPasswordPlaceholders(rawTemplate));
cmdTruncated = cmdTruncated.mid(6);
if (cmdTruncated.length() > 400) {
cmdTruncated = cmdTruncated.left(400) + " […]";
Expand Down Expand Up @@ -1043,7 +1055,11 @@ void DatabaseWidget::openUrlForEntry(Entry* entry)
QStringList cmdList = QProcess::splitCommand(cmd);
if (!cmdList.isEmpty()) {
const QString program = cmdList.takeFirst();
#ifdef KPXC_FEATURE_URLOVERRIDE
UrlOverride::executeCommand(program, cmdList);
#else
QProcess::startDetached(program, cmdList);
#endif
}

if (config()->get(Config::MinimizeOnOpenUrl).toBool()) {
Expand All @@ -1053,7 +1069,7 @@ void DatabaseWidget::openUrlForEntry(Entry* entry)
} else if (cmdString.startsWith("kdbx://")) {
openDatabaseFromEntry(entry, false);
} else {
QUrl url = QUrl::fromUserInput(entry->resolveMultiplePlaceholders(entry->url()));
QUrl url = QUrl::fromUserInput(cmdString);
if (!url.isEmpty()) {
#ifdef KEEPASSXC_DIST_APPIMAGE
QProcess::execute("xdg-open", {url.toString(QUrl::FullyEncoded)});
Expand Down
7 changes: 7 additions & 0 deletions src/gui/MainWindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,10 @@
#include "browser/BrowserService.h"
#endif

#ifdef KPXC_FEATURE_URLOVERRIDE
#include "urloverride/UrlOverrideSettingsPage.h"
#endif

#if defined(Q_OS_UNIX) && !defined(Q_OS_MACOS) && !defined(QT_NO_DBUS)
#include "mainwindowadaptor.h"
#endif
Expand Down Expand Up @@ -203,6 +207,9 @@ MainWindow::MainWindow()
initActionCollection();

m_ui->settingsWidget->addSettingsPage(new ShortcutSettingsPage());
#ifdef KPXC_FEATURE_URLOVERRIDE
m_ui->settingsWidget->addSettingsPage(new UrlOverrideSettingsPage());
#endif

#ifdef KPXC_FEATURE_BROWSER
connect(
Expand Down
11 changes: 11 additions & 0 deletions src/gui/UrlTools.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -210,3 +210,14 @@ bool UrlTools::domainHasIllegalCharacters(const QString& domain)
static const QRegularExpression re(R"([\s\^#|/:<>\?@\[\]\\])");
return re.match(domain).hasMatch();
}

// Extracts the URI scheme (ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ), per RFC 3986) from a
// possibly messy string, e.g. one entered by a user who isn't thinking about exact syntax.
// Anything before or after the matched scheme is simply not part of it and is discarded, rather
// than guessing which surrounding characters (like a trailing "://") to strip.
QString UrlTools::normalizeScheme(const QString& scheme)
{
static const QRegularExpression schemePattern("[A-Za-z][A-Za-z0-9+.-]*");
const auto match = schemePattern.match(scheme);
return match.hasMatch() ? match.captured(0) : QString();
}
1 change: 1 addition & 0 deletions src/gui/UrlTools.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ namespace UrlTools
bool isUrlIdentical(QString first, QString second);
bool isUrlValid(const QString& urlField, bool looseComparison = false);
bool domainHasIllegalCharacters(const QString& domain);
QString normalizeScheme(const QString& scheme);

extern const QString URL_WILDCARD;
} // namespace UrlTools
Expand Down
26 changes: 26 additions & 0 deletions src/urloverride/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Copyright (C) 2026 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/>.

if(KPXC_FEATURE_URLOVERRIDE)
include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR})

set(urloverride_SOURCES
UrlOverride.cpp
UrlOverrideSettingsPage.cpp
)

add_library(urloverride STATIC ${urloverride_SOURCES})
target_link_libraries(urloverride Qt6::Core Qt6::Widgets)
endif()
Loading
Loading