Files
keepassxc/src/gui/entry/EntryURLModel.cpp
Sami Vänttinen 9ba6ada266 Add support for URL wildcards and exact URL (#9835)
* Add support for URL wildcards with Additional URL feature

* Only check TLD if wildcard is used

* Avoid using network function in no-feature build

---------

Co-authored-by: varjolintu <sami.vanttinen@ahmala.org>
Co-authored-by: Jonathan White <support@dmapps.us>
2025-02-09 20:03:15 -05:00

163 lines
4.9 KiB
C++

/*
* Copyright (C) 2025 KeePassXC Team <team@keepassxc.org>
* Copyright (C) 2012 Felix Geyer <debfx@fobos.de>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 or (at your option)
* version 3 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "EntryURLModel.h"
#include "browser/BrowserService.h"
#include "core/EntryAttributes.h"
#include "gui/Icons.h"
#include "gui/UrlTools.h"
#include "gui/styles/StateColorPalette.h"
EntryURLModel::EntryURLModel(QObject* parent)
: QStandardItemModel(parent)
, m_entryAttributes(nullptr)
, m_errorIcon(icons()->icon("dialog-error"))
{
}
void EntryURLModel::setEntryAttributes(EntryAttributes* entryAttributes)
{
beginResetModel();
if (m_entryAttributes) {
m_entryAttributes->disconnect(this);
}
m_entryAttributes = entryAttributes;
if (m_entryAttributes) {
updateAttributes();
// clang-format off
connect(m_entryAttributes, SIGNAL(added(QString)), SLOT(updateAttributes()));
connect(m_entryAttributes, SIGNAL(customKeyModified(QString)), SLOT(updateAttributes()));
connect(m_entryAttributes, SIGNAL(removed(QString)), SLOT(updateAttributes()));
connect(m_entryAttributes, SIGNAL(renamed(QString,QString)), SLOT(updateAttributes()));
connect(m_entryAttributes, SIGNAL(reset()), SLOT(updateAttributes()));
// clang-format on
}
endResetModel();
}
QVariant EntryURLModel::data(const QModelIndex& index, int role) const
{
if (!index.isValid()) {
return {};
}
const auto key = keyByIndex(index);
if (key.isEmpty()) {
return {};
}
const auto value = m_entryAttributes->value(key);
const auto urlValid = urlTools()->isUrlValid(value, true);
// Check for duplicate URLs in the attribute list. Excludes the current key/value from the comparison.
auto customAttributeKeys = m_entryAttributes->customKeys().filter(EntryAttributes::AdditionalUrlAttribute);
customAttributeKeys.removeOne(key);
const auto duplicateUrl =
m_entryAttributes->values(customAttributeKeys).contains(value) || urlTools()->isUrlIdentical(value, m_entryUrl);
if (role == Qt::BackgroundRole && (!urlValid || duplicateUrl)) {
StateColorPalette statePalette;
return statePalette.color(StateColorPalette::ColorRole::Error);
} else if (role == Qt::DecorationRole && (!urlValid || duplicateUrl)) {
return m_errorIcon;
} else if (role == Qt::DisplayRole || role == Qt::EditRole) {
return value;
} else if (role == Qt::ToolTipRole && duplicateUrl) {
return tr("Duplicate URL");
} else if (role == Qt::ToolTipRole && !urlValid) {
return tr("Invalid URL");
}
return {};
}
void EntryURLModel::setEntryUrl(const QString& entryUrl)
{
m_entryUrl = entryUrl;
}
bool EntryURLModel::setData(const QModelIndex& index, const QVariant& value, int role)
{
if (!index.isValid() || role != Qt::EditRole || value.type() != QVariant::String || value.toString().isEmpty()) {
return false;
}
const auto row = index.row();
const auto key = m_urls.at(row).first;
if (EntryAttributes::isDefaultAttribute(key)) {
return false;
}
m_entryAttributes->set(key, value.toString());
emit dataChanged(this->index(row, 0), this->index(row, columnCount() - 1));
return true;
}
QModelIndex EntryURLModel::indexByKey(const QString& key) const
{
int row = -1;
for (int i = 0; i < m_urls.size(); ++i) {
if (m_urls.at(i).first == key) {
row = i;
break;
}
}
if (row == -1) {
return {};
} else {
return index(row, 0);
}
}
QString EntryURLModel::keyByIndex(const QModelIndex& index) const
{
if (!index.isValid()) {
return {};
} else {
return m_urls.at(index.row()).first;
}
}
void EntryURLModel::updateAttributes()
{
clear();
m_urls.clear();
const auto attributesKeyList = m_entryAttributes->keys();
for (const auto& key : attributesKeyList) {
if (!EntryAttributes::isDefaultAttribute(key) && key.contains(EntryAttributes::AdditionalUrlAttribute)) {
const auto value = m_entryAttributes->value(key);
m_urls.append(qMakePair(key, value));
auto* item = new QStandardItem(value);
if (m_entryAttributes->isProtected(key)) {
item->setFlags(item->flags() & ~Qt::ItemIsEnabled);
}
appendRow(item);
}
}
}