2010-08-07 09:10:44 -04:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2010 Felix Geyer <debfx@fobos.de>
|
2017-06-09 17:40:36 -04:00
|
|
|
* Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
|
2010-08-07 09:10:44 -04:00
|
|
|
*
|
|
|
|
* 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 "Entry.h"
|
|
|
|
|
2017-07-01 08:51:20 -04:00
|
|
|
#include "config-keepassx.h"
|
|
|
|
|
2011-07-08 08:51:14 -04:00
|
|
|
#include "core/Database.h"
|
|
|
|
#include "core/DatabaseIcons.h"
|
|
|
|
#include "core/Group.h"
|
|
|
|
#include "core/Metadata.h"
|
2017-04-13 06:05:36 -04:00
|
|
|
#include "totp/totp.h"
|
2010-08-12 15:38:59 -04:00
|
|
|
|
2017-11-20 13:01:22 -05:00
|
|
|
#include <QRegularExpression>
|
|
|
|
|
2012-07-01 15:58:45 -04:00
|
|
|
const int Entry::DefaultIconNumber = 0;
|
2017-10-16 03:35:40 -04:00
|
|
|
const int Entry::ResolveMaximumDepth = 10;
|
|
|
|
|
2012-07-01 15:58:45 -04:00
|
|
|
|
2010-08-13 12:08:06 -04:00
|
|
|
Entry::Entry()
|
2012-07-16 03:54:04 -04:00
|
|
|
: m_attributes(new EntryAttributes(this))
|
|
|
|
, m_attachments(new EntryAttachments(this))
|
|
|
|
, m_autoTypeAssociations(new AutoTypeAssociations(this))
|
2015-07-24 12:28:12 -04:00
|
|
|
, m_tmpHistoryItem(nullptr)
|
2014-03-22 07:07:06 -04:00
|
|
|
, m_modifiedSinceBegin(false)
|
2012-07-19 07:57:55 -04:00
|
|
|
, m_updateTimeinfo(true)
|
2010-08-12 15:38:59 -04:00
|
|
|
{
|
2012-05-15 10:47:46 -04:00
|
|
|
m_data.iconNumber = DefaultIconNumber;
|
2012-04-23 15:06:04 -04:00
|
|
|
m_data.autoTypeEnabled = true;
|
|
|
|
m_data.autoTypeObfuscation = 0;
|
2017-11-20 19:25:09 -05:00
|
|
|
m_data.totpStep = Totp::defaultStep;
|
|
|
|
m_data.totpDigits = Totp::defaultDigits;
|
2011-07-07 06:42:08 -04:00
|
|
|
|
2012-04-14 09:38:20 -04:00
|
|
|
connect(m_attributes, SIGNAL(modified()), this, SIGNAL(modified()));
|
|
|
|
connect(m_attributes, SIGNAL(defaultKeyModified()), SLOT(emitDataChanged()));
|
|
|
|
connect(m_attachments, SIGNAL(modified()), this, SIGNAL(modified()));
|
2012-07-16 03:54:04 -04:00
|
|
|
connect(m_autoTypeAssociations, SIGNAL(modified()), SIGNAL(modified()));
|
2012-04-23 15:06:04 -04:00
|
|
|
|
2012-07-16 03:54:04 -04:00
|
|
|
connect(this, SIGNAL(modified()), SLOT(updateTimeinfo()));
|
2012-04-23 15:06:04 -04:00
|
|
|
connect(this, SIGNAL(modified()), SLOT(updateModifiedSinceBegin()));
|
2010-08-12 15:38:59 -04:00
|
|
|
}
|
|
|
|
|
2010-08-18 10:22:48 -04:00
|
|
|
Entry::~Entry()
|
|
|
|
{
|
2011-07-09 15:54:01 -04:00
|
|
|
if (m_group) {
|
|
|
|
m_group->removeEntry(this);
|
2012-04-21 13:06:28 -04:00
|
|
|
|
2012-04-21 18:29:39 -04:00
|
|
|
if (m_group->database()) {
|
|
|
|
m_group->database()->addDeletedObject(m_uuid);
|
|
|
|
}
|
2011-07-09 15:54:01 -04:00
|
|
|
}
|
|
|
|
|
2010-08-25 07:52:59 -04:00
|
|
|
qDeleteAll(m_history);
|
2010-08-18 10:22:48 -04:00
|
|
|
}
|
|
|
|
|
2012-08-01 04:40:38 -04:00
|
|
|
template <class T> inline bool Entry::set(T& property, const T& value)
|
2012-04-16 19:02:02 -04:00
|
|
|
{
|
2012-04-11 13:51:54 -04:00
|
|
|
if (property != value) {
|
|
|
|
property = value;
|
2017-03-10 09:58:42 -05:00
|
|
|
emit modified();
|
2012-04-11 13:51:54 -04:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-04-16 19:02:02 -04:00
|
|
|
void Entry::updateTimeinfo()
|
|
|
|
{
|
|
|
|
if (m_updateTimeinfo) {
|
2015-07-22 17:48:08 -04:00
|
|
|
m_data.timeInfo.setLastModificationTime(QDateTime::currentDateTimeUtc());
|
|
|
|
m_data.timeInfo.setLastAccessTime(QDateTime::currentDateTimeUtc());
|
2012-04-16 19:02:02 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Entry::setUpdateTimeinfo(bool value)
|
|
|
|
{
|
2012-04-11 13:51:54 -04:00
|
|
|
m_updateTimeinfo = value;
|
|
|
|
}
|
|
|
|
|
2010-08-12 15:38:59 -04:00
|
|
|
Uuid Entry::uuid() const
|
|
|
|
{
|
|
|
|
return m_uuid;
|
|
|
|
}
|
|
|
|
|
2012-01-01 15:52:54 -05:00
|
|
|
QImage Entry::icon() const
|
2010-08-12 15:38:59 -04:00
|
|
|
{
|
2012-04-23 15:06:04 -04:00
|
|
|
if (m_data.customIcon.isNull()) {
|
|
|
|
return databaseIcons()->icon(m_data.iconNumber);
|
2010-08-24 17:12:01 -04:00
|
|
|
}
|
|
|
|
else {
|
2013-07-04 07:31:38 -04:00
|
|
|
Q_ASSERT(database());
|
|
|
|
|
|
|
|
if (database()) {
|
|
|
|
return database()->metadata()->customIcon(m_data.customIcon);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return QImage();
|
|
|
|
}
|
2010-08-24 17:12:01 -04:00
|
|
|
}
|
2010-08-12 15:38:59 -04:00
|
|
|
}
|
|
|
|
|
2012-01-01 15:52:54 -05:00
|
|
|
QPixmap Entry::iconPixmap() const
|
|
|
|
{
|
2012-04-23 15:06:04 -04:00
|
|
|
if (m_data.customIcon.isNull()) {
|
|
|
|
return databaseIcons()->iconPixmap(m_data.iconNumber);
|
2012-01-01 15:52:54 -05:00
|
|
|
}
|
|
|
|
else {
|
2013-07-04 07:31:38 -04:00
|
|
|
Q_ASSERT(database());
|
|
|
|
|
2016-01-24 11:56:35 -05:00
|
|
|
if (database()) {
|
|
|
|
return database()->metadata()->customIconPixmap(m_data.customIcon);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return QPixmap();
|
2012-01-01 15:52:54 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-01-24 12:45:10 -05:00
|
|
|
QPixmap Entry::iconScaledPixmap() const
|
|
|
|
{
|
|
|
|
if (m_data.customIcon.isNull()) {
|
|
|
|
// built-in icons are 16x16 so don't need to be scaled
|
|
|
|
return databaseIcons()->iconPixmap(m_data.iconNumber);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
Q_ASSERT(database());
|
2012-01-01 15:52:54 -05:00
|
|
|
|
2016-01-24 12:45:10 -05:00
|
|
|
return database()->metadata()->customIconScaledPixmap(m_data.customIcon);
|
2012-01-01 15:52:54 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-08-18 16:57:26 -04:00
|
|
|
int Entry::iconNumber() const
|
|
|
|
{
|
2012-04-23 15:06:04 -04:00
|
|
|
return m_data.iconNumber;
|
2010-08-18 16:57:26 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
Uuid Entry::iconUuid() const
|
|
|
|
{
|
2012-04-23 15:06:04 -04:00
|
|
|
return m_data.customIcon;
|
2010-08-18 16:57:26 -04:00
|
|
|
}
|
|
|
|
|
2010-08-12 15:38:59 -04:00
|
|
|
QColor Entry::foregroundColor() const
|
|
|
|
{
|
2012-04-23 15:06:04 -04:00
|
|
|
return m_data.foregroundColor;
|
2010-08-12 15:38:59 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
QColor Entry::backgroundColor() const
|
|
|
|
{
|
2012-04-23 15:06:04 -04:00
|
|
|
return m_data.backgroundColor;
|
2010-08-12 15:38:59 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
QString Entry::overrideUrl() const
|
|
|
|
{
|
2012-04-23 15:06:04 -04:00
|
|
|
return m_data.overrideUrl;
|
2010-08-12 15:38:59 -04:00
|
|
|
}
|
|
|
|
|
2010-09-25 06:41:00 -04:00
|
|
|
QString Entry::tags() const
|
|
|
|
{
|
2012-04-23 15:06:04 -04:00
|
|
|
return m_data.tags;
|
2010-09-25 06:41:00 -04:00
|
|
|
}
|
|
|
|
|
2010-08-12 15:38:59 -04:00
|
|
|
TimeInfo Entry::timeInfo() const
|
|
|
|
{
|
2012-04-23 15:06:04 -04:00
|
|
|
return m_data.timeInfo;
|
2010-08-12 15:38:59 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
bool Entry::autoTypeEnabled() const
|
|
|
|
{
|
2012-04-23 15:06:04 -04:00
|
|
|
return m_data.autoTypeEnabled;
|
2010-08-12 15:38:59 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
int Entry::autoTypeObfuscation() const
|
|
|
|
{
|
2012-04-23 15:06:04 -04:00
|
|
|
return m_data.autoTypeObfuscation;
|
2010-08-12 15:38:59 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
QString Entry::defaultAutoTypeSequence() const
|
|
|
|
{
|
2012-04-23 15:06:04 -04:00
|
|
|
return m_data.defaultAutoTypeSequence;
|
2010-08-12 15:38:59 -04:00
|
|
|
}
|
|
|
|
|
2016-11-11 16:26:07 -05:00
|
|
|
QString Entry::effectiveAutoTypeSequence() const
|
|
|
|
{
|
|
|
|
if (!m_data.defaultAutoTypeSequence.isEmpty()) {
|
|
|
|
return m_data.defaultAutoTypeSequence;
|
|
|
|
}
|
|
|
|
QString sequence;
|
|
|
|
|
|
|
|
const Group* grp = group();
|
|
|
|
if(grp) {
|
|
|
|
sequence = grp->effectiveAutoTypeSequence();
|
|
|
|
} else {
|
|
|
|
return QString();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sequence.isEmpty() && (!username().isEmpty() || !password().isEmpty())) {
|
|
|
|
if (username().isEmpty()) {
|
|
|
|
sequence = "{PASSWORD}{ENTER}";
|
|
|
|
}
|
|
|
|
else if (password().isEmpty()) {
|
|
|
|
sequence = "{USERNAME}{ENTER}";
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
sequence = "{USERNAME}{TAB}{PASSWORD}{ENTER}";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return sequence;
|
|
|
|
}
|
|
|
|
|
2012-07-16 03:54:04 -04:00
|
|
|
AutoTypeAssociations* Entry::autoTypeAssociations()
|
|
|
|
{
|
|
|
|
return m_autoTypeAssociations;
|
|
|
|
}
|
|
|
|
|
|
|
|
const AutoTypeAssociations* Entry::autoTypeAssociations() const
|
2010-08-12 15:38:59 -04:00
|
|
|
{
|
2012-07-16 03:54:04 -04:00
|
|
|
return m_autoTypeAssociations;
|
2010-08-12 15:38:59 -04:00
|
|
|
}
|
|
|
|
|
2012-04-14 09:38:20 -04:00
|
|
|
QString Entry::title() const
|
2012-04-06 13:33:29 -04:00
|
|
|
{
|
2013-12-01 03:43:41 -05:00
|
|
|
return m_attributes->value(EntryAttributes::TitleKey);
|
2012-04-06 13:33:29 -04:00
|
|
|
}
|
|
|
|
|
2012-04-14 09:38:20 -04:00
|
|
|
QString Entry::url() const
|
2012-04-06 13:33:29 -04:00
|
|
|
{
|
2013-12-01 18:10:47 -05:00
|
|
|
return m_attributes->value(EntryAttributes::URLKey);
|
2010-08-12 15:38:59 -04:00
|
|
|
}
|
|
|
|
|
2017-06-29 13:54:49 -04:00
|
|
|
QString Entry::webUrl() const
|
|
|
|
{
|
2017-10-25 00:01:29 -04:00
|
|
|
QString url = resolveMultiplePlaceholders(m_attributes->value(EntryAttributes::URLKey));
|
|
|
|
return resolveUrl(url);
|
2017-06-29 13:54:49 -04:00
|
|
|
}
|
|
|
|
|
2017-10-25 23:29:01 -04:00
|
|
|
QString Entry::displayUrl() const
|
|
|
|
{
|
|
|
|
QString url = maskPasswordPlaceholders(m_attributes->value(EntryAttributes::URLKey));
|
|
|
|
url = resolveMultiplePlaceholders(url);
|
|
|
|
return resolveUrl(url);
|
|
|
|
}
|
|
|
|
|
2012-04-14 09:38:20 -04:00
|
|
|
QString Entry::username() const
|
2011-01-13 16:31:17 -05:00
|
|
|
{
|
2013-12-01 03:43:41 -05:00
|
|
|
return m_attributes->value(EntryAttributes::UserNameKey);
|
2011-01-13 16:31:17 -05:00
|
|
|
}
|
|
|
|
|
2012-04-14 09:38:20 -04:00
|
|
|
QString Entry::password() const
|
2011-01-13 16:31:17 -05:00
|
|
|
{
|
2013-12-01 03:43:41 -05:00
|
|
|
return m_attributes->value(EntryAttributes::PasswordKey);
|
2011-01-13 16:31:17 -05:00
|
|
|
}
|
|
|
|
|
2012-04-14 09:38:20 -04:00
|
|
|
QString Entry::notes() const
|
2010-08-18 09:08:17 -04:00
|
|
|
{
|
2013-12-01 03:43:41 -05:00
|
|
|
return m_attributes->value(EntryAttributes::NotesKey);
|
2010-08-18 09:08:17 -04:00
|
|
|
}
|
|
|
|
|
2012-05-15 13:58:10 -04:00
|
|
|
bool Entry::isExpired() const
|
|
|
|
{
|
2015-07-22 17:48:08 -04:00
|
|
|
return m_data.timeInfo.expires() && m_data.timeInfo.expiryTime() < QDateTime::currentDateTimeUtc();
|
2012-05-15 13:58:10 -04:00
|
|
|
}
|
|
|
|
|
2017-03-05 17:47:08 -05:00
|
|
|
bool Entry::hasReferences() const
|
|
|
|
{
|
|
|
|
const QList<QString> keyList = EntryAttributes::DefaultAttributes;
|
|
|
|
for (const QString& key : keyList) {
|
|
|
|
if (m_attributes->isReference(key)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2012-04-14 09:38:20 -04:00
|
|
|
EntryAttributes* Entry::attributes()
|
2010-08-18 09:08:17 -04:00
|
|
|
{
|
2012-04-14 09:38:20 -04:00
|
|
|
return m_attributes;
|
2010-08-18 09:08:17 -04:00
|
|
|
}
|
|
|
|
|
2012-04-14 09:38:20 -04:00
|
|
|
const EntryAttributes* Entry::attributes() const
|
2010-08-18 09:08:17 -04:00
|
|
|
{
|
2012-04-14 09:38:20 -04:00
|
|
|
return m_attributes;
|
2010-08-18 09:08:17 -04:00
|
|
|
}
|
|
|
|
|
2012-04-14 09:38:20 -04:00
|
|
|
EntryAttachments* Entry::attachments()
|
2010-08-18 09:08:17 -04:00
|
|
|
{
|
2012-04-14 09:38:20 -04:00
|
|
|
return m_attachments;
|
2010-08-18 09:08:17 -04:00
|
|
|
}
|
|
|
|
|
2012-04-14 09:38:20 -04:00
|
|
|
const EntryAttachments* Entry::attachments() const
|
2010-08-18 09:08:17 -04:00
|
|
|
{
|
2012-04-14 09:38:20 -04:00
|
|
|
return m_attachments;
|
2010-08-18 09:08:17 -04:00
|
|
|
}
|
|
|
|
|
2017-04-13 06:05:36 -04:00
|
|
|
bool Entry::hasTotp() const
|
|
|
|
{
|
|
|
|
return m_attributes->hasKey("TOTP Seed") || m_attributes->hasKey("otp");
|
|
|
|
}
|
|
|
|
|
|
|
|
QString Entry::totp() const
|
|
|
|
{
|
|
|
|
if (hasTotp()) {
|
|
|
|
QString seed = totpSeed();
|
|
|
|
quint64 time = QDateTime::currentDateTime().toTime_t();
|
2017-11-20 19:25:09 -05:00
|
|
|
QString output = Totp::generateTotp(seed.toLatin1(), time, m_data.totpDigits, m_data.totpStep);
|
2017-04-13 06:05:36 -04:00
|
|
|
|
|
|
|
return QString(output);
|
|
|
|
} else {
|
|
|
|
return QString("");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Entry::setTotp(const QString& seed, quint8& step, quint8& digits)
|
|
|
|
{
|
|
|
|
if (step == 0) {
|
2017-11-20 19:25:09 -05:00
|
|
|
step = Totp::defaultStep;
|
2017-04-13 06:05:36 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (digits == 0) {
|
2017-11-20 19:25:09 -05:00
|
|
|
digits = Totp::defaultDigits;
|
2017-04-13 06:05:36 -04:00
|
|
|
}
|
2017-11-20 13:01:22 -05:00
|
|
|
QString data;
|
|
|
|
|
2017-11-20 19:25:09 -05:00
|
|
|
const Totp::Encoder & enc = Totp::encoders.value(digits, Totp::defaultEncoder);
|
2017-04-13 06:05:36 -04:00
|
|
|
|
|
|
|
if (m_attributes->hasKey("otp")) {
|
2017-11-20 13:01:22 -05:00
|
|
|
data = QString("key=%1&step=%2&size=%3").arg(seed).arg(step).arg(enc.digits == 0 ? digits : enc.digits);
|
|
|
|
if (!enc.name.isEmpty()) {
|
|
|
|
data.append("&enocder=").append(enc.name);
|
|
|
|
}
|
|
|
|
m_attributes->set("otp", data, true);
|
2017-04-13 06:05:36 -04:00
|
|
|
} else {
|
|
|
|
m_attributes->set("TOTP Seed", seed, true);
|
2017-11-20 13:01:22 -05:00
|
|
|
if (!enc.shortName.isEmpty()) {
|
|
|
|
data = QString("%1;%2").arg(step).arg(enc.shortName);
|
|
|
|
} else {
|
|
|
|
data = QString("%1;%2").arg(step).arg(digits);
|
|
|
|
}
|
|
|
|
m_attributes->set("TOTP Settings", data);
|
2017-04-13 06:05:36 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
QString Entry::totpSeed() const
|
|
|
|
{
|
|
|
|
QString secret = "";
|
|
|
|
|
|
|
|
if (m_attributes->hasKey("otp")) {
|
|
|
|
secret = m_attributes->value("otp");
|
2017-05-03 19:54:19 -04:00
|
|
|
} else if (m_attributes->hasKey("TOTP Seed")) {
|
2017-04-13 06:05:36 -04:00
|
|
|
secret = m_attributes->value("TOTP Seed");
|
|
|
|
}
|
|
|
|
|
2017-11-20 19:25:09 -05:00
|
|
|
m_data.totpDigits = Totp::defaultDigits;
|
|
|
|
m_data.totpStep = Totp::defaultStep;
|
2017-04-13 06:05:36 -04:00
|
|
|
|
|
|
|
if (m_attributes->hasKey("TOTP Settings")) {
|
2017-11-20 19:25:09 -05:00
|
|
|
// this regex must be kept in sync with the set of allowed short names Totp::shortNameToEncoder
|
2017-11-20 13:01:22 -05:00
|
|
|
QRegularExpression rx(QString("(\\d+);((?:\\d+)|S)"));
|
|
|
|
QRegularExpressionMatch m = rx.match(m_attributes->value("TOTP Settings"));
|
|
|
|
if (m.hasMatch()) {
|
|
|
|
m_data.totpStep = m.captured(1).toUInt();
|
2017-11-20 19:25:09 -05:00
|
|
|
if (Totp::shortNameToEncoder.contains(m.captured(2))) {
|
|
|
|
m_data.totpDigits = Totp::shortNameToEncoder[m.captured(2)];
|
2017-11-20 13:01:22 -05:00
|
|
|
} else {
|
|
|
|
m_data.totpDigits = m.captured(2).toUInt();
|
|
|
|
}
|
2017-04-13 06:05:36 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-20 19:25:09 -05:00
|
|
|
return Totp::parseOtpString(secret, m_data.totpDigits, m_data.totpStep);
|
2017-04-13 06:05:36 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
quint8 Entry::totpStep() const
|
|
|
|
{
|
|
|
|
return m_data.totpStep;
|
|
|
|
}
|
|
|
|
|
|
|
|
quint8 Entry::totpDigits() const
|
|
|
|
{
|
|
|
|
return m_data.totpDigits;
|
|
|
|
}
|
|
|
|
|
2010-08-12 15:38:59 -04:00
|
|
|
void Entry::setUuid(const Uuid& uuid)
|
|
|
|
{
|
2010-08-13 12:08:06 -04:00
|
|
|
Q_ASSERT(!uuid.isNull());
|
2012-04-11 13:51:54 -04:00
|
|
|
set(m_uuid, uuid);
|
2010-08-12 15:38:59 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void Entry::setIcon(int iconNumber)
|
|
|
|
{
|
2010-08-14 06:24:35 -04:00
|
|
|
Q_ASSERT(iconNumber >= 0);
|
|
|
|
|
2012-04-23 15:06:04 -04:00
|
|
|
if (m_data.iconNumber != iconNumber || !m_data.customIcon.isNull()) {
|
|
|
|
m_data.iconNumber = iconNumber;
|
|
|
|
m_data.customIcon = Uuid();
|
2012-04-11 13:51:54 -04:00
|
|
|
|
2017-03-10 09:58:42 -05:00
|
|
|
emit modified();
|
2012-05-11 08:14:12 -04:00
|
|
|
emitDataChanged();
|
2012-04-11 13:51:54 -04:00
|
|
|
}
|
2010-08-12 15:38:59 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void Entry::setIcon(const Uuid& uuid)
|
|
|
|
{
|
2010-08-14 06:24:35 -04:00
|
|
|
Q_ASSERT(!uuid.isNull());
|
|
|
|
|
2012-04-23 15:06:04 -04:00
|
|
|
if (m_data.customIcon != uuid) {
|
|
|
|
m_data.customIcon = uuid;
|
|
|
|
m_data.iconNumber = 0;
|
2012-01-01 15:52:54 -05:00
|
|
|
|
2017-03-10 09:58:42 -05:00
|
|
|
emit modified();
|
2012-05-11 08:14:12 -04:00
|
|
|
emitDataChanged();
|
2012-04-11 13:51:54 -04:00
|
|
|
}
|
2010-08-12 15:38:59 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void Entry::setForegroundColor(const QColor& color)
|
|
|
|
{
|
2012-04-23 15:06:04 -04:00
|
|
|
set(m_data.foregroundColor, color);
|
2010-08-12 15:38:59 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void Entry::setBackgroundColor(const QColor& color)
|
|
|
|
{
|
2012-04-23 15:06:04 -04:00
|
|
|
set(m_data.backgroundColor, color);
|
2010-08-12 15:38:59 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void Entry::setOverrideUrl(const QString& url)
|
|
|
|
{
|
2012-04-23 15:06:04 -04:00
|
|
|
set(m_data.overrideUrl, url);
|
2010-08-12 15:38:59 -04:00
|
|
|
}
|
|
|
|
|
2010-09-25 06:41:00 -04:00
|
|
|
void Entry::setTags(const QString& tags)
|
|
|
|
{
|
2012-04-23 15:06:04 -04:00
|
|
|
set(m_data.tags, tags);
|
2010-09-25 06:41:00 -04:00
|
|
|
}
|
|
|
|
|
2010-08-12 15:38:59 -04:00
|
|
|
void Entry::setTimeInfo(const TimeInfo& timeInfo)
|
|
|
|
{
|
2012-04-23 15:06:04 -04:00
|
|
|
m_data.timeInfo = timeInfo;
|
2010-08-12 15:38:59 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void Entry::setAutoTypeEnabled(bool enable)
|
|
|
|
{
|
2012-04-23 15:06:04 -04:00
|
|
|
set(m_data.autoTypeEnabled, enable);
|
2010-08-12 15:38:59 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void Entry::setAutoTypeObfuscation(int obfuscation)
|
|
|
|
{
|
2012-04-23 15:06:04 -04:00
|
|
|
set(m_data.autoTypeObfuscation, obfuscation);
|
2010-08-12 15:38:59 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void Entry::setDefaultAutoTypeSequence(const QString& sequence)
|
|
|
|
{
|
2012-04-23 15:06:04 -04:00
|
|
|
set(m_data.defaultAutoTypeSequence, sequence);
|
2010-08-12 15:38:59 -04:00
|
|
|
}
|
|
|
|
|
2010-08-18 09:08:17 -04:00
|
|
|
void Entry::setTitle(const QString& title)
|
|
|
|
{
|
2013-12-01 03:43:41 -05:00
|
|
|
m_attributes->set(EntryAttributes::TitleKey, title, m_attributes->isProtected(EntryAttributes::TitleKey));
|
2010-08-18 09:08:17 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void Entry::setUrl(const QString& url)
|
|
|
|
{
|
2017-01-28 08:18:43 -05:00
|
|
|
bool remove = url != m_attributes->value(EntryAttributes::URLKey) &&
|
|
|
|
(m_attributes->value(EntryAttributes::RememberCmdExecAttr) == "1" ||
|
|
|
|
m_attributes->value(EntryAttributes::RememberCmdExecAttr) == "0");
|
|
|
|
if (remove) {
|
|
|
|
m_attributes->remove(EntryAttributes::RememberCmdExecAttr);
|
|
|
|
}
|
2013-12-01 18:10:47 -05:00
|
|
|
m_attributes->set(EntryAttributes::URLKey, url, m_attributes->isProtected(EntryAttributes::URLKey));
|
2010-08-18 09:08:17 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void Entry::setUsername(const QString& username)
|
|
|
|
{
|
2013-12-01 03:43:41 -05:00
|
|
|
m_attributes->set(EntryAttributes::UserNameKey, username, m_attributes->isProtected(EntryAttributes::UserNameKey));
|
2010-08-18 09:08:17 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void Entry::setPassword(const QString& password)
|
|
|
|
{
|
2013-12-01 03:43:41 -05:00
|
|
|
m_attributes->set(EntryAttributes::PasswordKey, password, m_attributes->isProtected(EntryAttributes::PasswordKey));
|
2010-08-18 09:08:17 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void Entry::setNotes(const QString& notes)
|
|
|
|
{
|
2013-12-01 03:43:41 -05:00
|
|
|
m_attributes->set(EntryAttributes::NotesKey, notes, m_attributes->isProtected(EntryAttributes::NotesKey));
|
2010-08-18 09:08:17 -04:00
|
|
|
}
|
|
|
|
|
2012-04-16 20:27:38 -04:00
|
|
|
void Entry::setExpires(const bool& value)
|
|
|
|
{
|
2012-04-23 15:06:04 -04:00
|
|
|
if (m_data.timeInfo.expires() != value) {
|
|
|
|
m_data.timeInfo.setExpires(value);
|
2017-03-10 09:58:42 -05:00
|
|
|
emit modified();
|
2012-04-16 20:27:38 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Entry::setExpiryTime(const QDateTime& dateTime)
|
|
|
|
{
|
2012-04-23 15:06:04 -04:00
|
|
|
if (m_data.timeInfo.expiryTime() != dateTime) {
|
|
|
|
m_data.timeInfo.setExpiryTime(dateTime);
|
2017-03-10 09:58:42 -05:00
|
|
|
emit modified();
|
2012-04-16 20:27:38 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-08-25 07:52:59 -04:00
|
|
|
QList<Entry*> Entry::historyItems()
|
|
|
|
{
|
|
|
|
return m_history;
|
|
|
|
}
|
|
|
|
|
|
|
|
const QList<Entry*>& Entry::historyItems() const
|
|
|
|
{
|
|
|
|
return m_history;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Entry::addHistoryItem(Entry* entry)
|
|
|
|
{
|
|
|
|
Q_ASSERT(!entry->parent());
|
|
|
|
|
|
|
|
m_history.append(entry);
|
2017-03-10 09:58:42 -05:00
|
|
|
emit modified();
|
2010-08-25 07:52:59 -04:00
|
|
|
}
|
|
|
|
|
2013-03-26 18:53:34 -04:00
|
|
|
void Entry::removeHistoryItems(const QList<Entry*>& historyEntries)
|
2012-05-18 13:22:22 -04:00
|
|
|
{
|
2013-03-26 18:53:34 -04:00
|
|
|
if (historyEntries.isEmpty()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-09-02 13:51:51 -04:00
|
|
|
for (Entry* entry : historyEntries) {
|
2012-05-18 13:22:22 -04:00
|
|
|
Q_ASSERT(!entry->parent());
|
|
|
|
Q_ASSERT(entry->uuid() == uuid());
|
2013-03-26 18:53:34 -04:00
|
|
|
Q_ASSERT(m_history.contains(entry));
|
|
|
|
|
|
|
|
m_history.removeOne(entry);
|
2012-05-18 13:22:22 -04:00
|
|
|
delete entry;
|
|
|
|
}
|
|
|
|
|
2017-03-10 09:58:42 -05:00
|
|
|
emit modified();
|
2012-05-18 13:22:22 -04:00
|
|
|
}
|
|
|
|
|
2012-06-29 18:22:07 -04:00
|
|
|
void Entry::truncateHistory()
|
|
|
|
{
|
2012-05-14 10:29:22 -04:00
|
|
|
const Database* db = database();
|
2012-05-14 10:27:59 -04:00
|
|
|
|
|
|
|
if (!db) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
int histMaxItems = db->metadata()->historyMaxItems();
|
|
|
|
if (histMaxItems > -1) {
|
|
|
|
int historyCount = 0;
|
|
|
|
QMutableListIterator<Entry*> i(m_history);
|
|
|
|
i.toBack();
|
|
|
|
while (i.hasPrevious()) {
|
|
|
|
historyCount++;
|
|
|
|
Entry* entry = i.previous();
|
|
|
|
if (historyCount > histMaxItems) {
|
|
|
|
delete entry;
|
|
|
|
i.remove();
|
2012-05-10 03:56:41 -04:00
|
|
|
}
|
2012-05-04 17:45:34 -04:00
|
|
|
}
|
2012-05-14 10:27:59 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
int histMaxSize = db->metadata()->historyMaxSize();
|
|
|
|
if (histMaxSize > -1) {
|
|
|
|
int size = 0;
|
2016-12-01 22:48:26 -05:00
|
|
|
QSet<QByteArray> foundAttachments = attachments()->values().toSet();
|
2012-05-14 10:27:59 -04:00
|
|
|
|
|
|
|
QMutableListIterator<Entry*> i(m_history);
|
|
|
|
i.toBack();
|
|
|
|
while (i.hasPrevious()) {
|
2012-07-19 18:45:34 -04:00
|
|
|
Entry* historyItem = i.previous();
|
2012-05-14 10:27:59 -04:00
|
|
|
|
|
|
|
// don't calculate size if it's already above the maximum
|
|
|
|
if (size <= histMaxSize) {
|
2012-07-19 18:45:34 -04:00
|
|
|
size += historyItem->attributes()->attributesSize();
|
|
|
|
|
2016-12-01 22:48:26 -05:00
|
|
|
const QSet<QByteArray> newAttachments = historyItem->attachments()->values().toSet() - foundAttachments;
|
2016-09-02 13:51:51 -04:00
|
|
|
for (const QByteArray& attachment : newAttachments) {
|
2012-07-19 18:45:34 -04:00
|
|
|
size += attachment.size();
|
|
|
|
}
|
2016-12-01 22:48:26 -05:00
|
|
|
foundAttachments += newAttachments;
|
2012-05-14 10:27:59 -04:00
|
|
|
}
|
2012-05-10 03:56:41 -04:00
|
|
|
|
2012-05-14 10:27:59 -04:00
|
|
|
if (size > histMaxSize) {
|
2012-07-19 18:45:34 -04:00
|
|
|
delete historyItem;
|
2012-05-14 10:27:59 -04:00
|
|
|
i.remove();
|
2012-05-10 03:56:41 -04:00
|
|
|
}
|
2012-05-04 17:45:34 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-11-22 07:27:49 -05:00
|
|
|
Entry* Entry::clone(CloneFlags flags) const
|
2012-05-15 15:10:39 -04:00
|
|
|
{
|
|
|
|
Entry* entry = new Entry();
|
|
|
|
entry->setUpdateTimeinfo(false);
|
2013-11-22 07:27:49 -05:00
|
|
|
if (flags & CloneNewUuid) {
|
|
|
|
entry->m_uuid = Uuid::random();
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
entry->m_uuid = m_uuid;
|
|
|
|
}
|
2012-05-15 15:10:39 -04:00
|
|
|
entry->m_data = m_data;
|
2012-07-20 06:13:26 -04:00
|
|
|
entry->m_attributes->copyDataFrom(m_attributes);
|
|
|
|
entry->m_attachments->copyDataFrom(m_attachments);
|
2017-02-27 20:25:56 -05:00
|
|
|
|
|
|
|
if (flags & CloneUserAsRef) {
|
2017-08-18 16:18:55 -04:00
|
|
|
// Build the username reference
|
2017-02-27 20:25:56 -05:00
|
|
|
QString username = "{REF:U@I:" + m_uuid.toHex() + "}";
|
|
|
|
entry->m_attributes->set(EntryAttributes::UserNameKey, username.toUpper(), m_attributes->isProtected(EntryAttributes::UserNameKey));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (flags & ClonePassAsRef) {
|
|
|
|
QString password = "{REF:P@I:" + m_uuid.toHex() + "}";
|
|
|
|
entry->m_attributes->set(EntryAttributes::PasswordKey, password.toUpper(), m_attributes->isProtected(EntryAttributes::PasswordKey));
|
|
|
|
}
|
|
|
|
|
2012-07-16 03:54:04 -04:00
|
|
|
entry->m_autoTypeAssociations->copyDataFrom(this->m_autoTypeAssociations);
|
2013-11-22 07:27:49 -05:00
|
|
|
if (flags & CloneIncludeHistory) {
|
2016-09-02 13:51:51 -04:00
|
|
|
for (Entry* historyItem : m_history) {
|
2013-11-22 07:27:49 -05:00
|
|
|
Entry* historyItemClone = historyItem->clone(flags & ~CloneIncludeHistory & ~CloneNewUuid);
|
|
|
|
historyItemClone->setUpdateTimeinfo(false);
|
|
|
|
historyItemClone->setUuid(entry->uuid());
|
|
|
|
historyItemClone->setUpdateTimeinfo(true);
|
|
|
|
entry->addHistoryItem(historyItemClone);
|
|
|
|
}
|
|
|
|
}
|
2012-05-15 15:10:39 -04:00
|
|
|
entry->setUpdateTimeinfo(true);
|
|
|
|
|
2013-11-22 07:27:49 -05:00
|
|
|
if (flags & CloneResetTimeInfo) {
|
2015-07-22 17:48:08 -04:00
|
|
|
QDateTime now = QDateTime::currentDateTimeUtc();
|
2013-11-22 07:27:49 -05:00
|
|
|
entry->m_data.timeInfo.setCreationTime(now);
|
|
|
|
entry->m_data.timeInfo.setLastModificationTime(now);
|
|
|
|
entry->m_data.timeInfo.setLastAccessTime(now);
|
|
|
|
entry->m_data.timeInfo.setLocationChanged(now);
|
|
|
|
}
|
|
|
|
|
2017-01-25 20:02:32 -05:00
|
|
|
if (flags & CloneRenameTitle)
|
|
|
|
entry->setTitle(entry->title() + tr(" - Clone"));
|
2012-11-06 17:34:16 -05:00
|
|
|
|
2012-05-15 15:10:39 -04:00
|
|
|
return entry;
|
|
|
|
}
|
|
|
|
|
2013-04-14 08:21:42 -04:00
|
|
|
void Entry::copyDataFrom(const Entry* other)
|
|
|
|
{
|
|
|
|
setUpdateTimeinfo(false);
|
|
|
|
m_data = other->m_data;
|
|
|
|
m_attributes->copyDataFrom(other->m_attributes);
|
|
|
|
m_attachments->copyDataFrom(other->m_attachments);
|
|
|
|
m_autoTypeAssociations->copyDataFrom(other->m_autoTypeAssociations);
|
|
|
|
setUpdateTimeinfo(true);
|
|
|
|
}
|
|
|
|
|
2012-04-23 15:06:04 -04:00
|
|
|
void Entry::beginUpdate()
|
|
|
|
{
|
|
|
|
Q_ASSERT(!m_tmpHistoryItem);
|
|
|
|
|
|
|
|
m_tmpHistoryItem = new Entry();
|
|
|
|
m_tmpHistoryItem->setUpdateTimeinfo(false);
|
|
|
|
m_tmpHistoryItem->m_uuid = m_uuid;
|
|
|
|
m_tmpHistoryItem->m_data = m_data;
|
2012-07-20 06:13:26 -04:00
|
|
|
m_tmpHistoryItem->m_attributes->copyDataFrom(m_attributes);
|
|
|
|
m_tmpHistoryItem->m_attachments->copyDataFrom(m_attachments);
|
2012-04-23 15:06:04 -04:00
|
|
|
|
|
|
|
m_modifiedSinceBegin = false;
|
|
|
|
}
|
|
|
|
|
2016-07-31 17:49:38 -04:00
|
|
|
bool Entry::endUpdate()
|
2012-04-23 15:06:04 -04:00
|
|
|
{
|
|
|
|
Q_ASSERT(m_tmpHistoryItem);
|
|
|
|
if (m_modifiedSinceBegin) {
|
|
|
|
m_tmpHistoryItem->setUpdateTimeinfo(true);
|
|
|
|
addHistoryItem(m_tmpHistoryItem);
|
2012-05-10 03:56:41 -04:00
|
|
|
truncateHistory();
|
2012-04-23 15:06:04 -04:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
delete m_tmpHistoryItem;
|
|
|
|
}
|
|
|
|
|
2015-07-24 12:28:12 -04:00
|
|
|
m_tmpHistoryItem = nullptr;
|
2016-07-31 17:49:38 -04:00
|
|
|
|
|
|
|
return m_modifiedSinceBegin;
|
2012-04-23 15:06:04 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void Entry::updateModifiedSinceBegin()
|
|
|
|
{
|
|
|
|
m_modifiedSinceBegin = true;
|
|
|
|
}
|
|
|
|
|
2017-10-16 03:35:40 -04:00
|
|
|
QString Entry::resolveMultiplePlaceholdersRecursive(const QString &str, int maxDepth) const
|
|
|
|
{
|
|
|
|
if (maxDepth <= 0) {
|
|
|
|
qWarning("Maximum depth of replacement has been reached. Entry uuid: %s", qPrintable(uuid().toHex()));
|
|
|
|
return str;
|
|
|
|
}
|
|
|
|
|
|
|
|
QString result = str;
|
|
|
|
QRegExp placeholderRegEx("(\\{[^\\}]+\\})", Qt::CaseInsensitive, QRegExp::RegExp2);
|
|
|
|
placeholderRegEx.setMinimal(true);
|
|
|
|
int pos = 0;
|
|
|
|
while ((pos = placeholderRegEx.indexIn(str, pos)) != -1) {
|
|
|
|
const QString found = placeholderRegEx.cap(1);
|
|
|
|
result.replace(found, resolvePlaceholderRecursive(found, maxDepth - 1));
|
|
|
|
pos += placeholderRegEx.matchedLength();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (result != str) {
|
|
|
|
result = resolveMultiplePlaceholdersRecursive(result, maxDepth - 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
QString Entry::resolvePlaceholderRecursive(const QString &placeholder, int maxDepth) const
|
|
|
|
{
|
|
|
|
const PlaceholderType typeOfPlaceholder = placeholderType(placeholder);
|
|
|
|
switch (typeOfPlaceholder) {
|
|
|
|
case PlaceholderType::NotPlaceholder:
|
|
|
|
return placeholder;
|
|
|
|
case PlaceholderType::Unknown:
|
2017-11-12 07:34:13 -05:00
|
|
|
#ifdef QT_DEBUG
|
|
|
|
qWarning("Can't resolve placeholder \"%s\" for entry with title: \"%s\" (uuid \"%s\") ",
|
|
|
|
qPrintable(placeholder), qPrintable(title()), qPrintable(uuid().toHex()));
|
|
|
|
#endif
|
2017-10-16 03:35:40 -04:00
|
|
|
return placeholder;
|
|
|
|
case PlaceholderType::Title:
|
|
|
|
return title();
|
|
|
|
case PlaceholderType::UserName:
|
|
|
|
return username();
|
|
|
|
case PlaceholderType::Password:
|
|
|
|
return password();
|
|
|
|
case PlaceholderType::Notes:
|
|
|
|
return notes();
|
|
|
|
case PlaceholderType::Totp:
|
|
|
|
return totp();
|
|
|
|
case PlaceholderType::Url:
|
|
|
|
return url();
|
|
|
|
case PlaceholderType::UrlWithoutScheme:
|
|
|
|
case PlaceholderType::UrlScheme:
|
|
|
|
case PlaceholderType::UrlHost:
|
|
|
|
case PlaceholderType::UrlPort:
|
|
|
|
case PlaceholderType::UrlPath:
|
|
|
|
case PlaceholderType::UrlQuery:
|
|
|
|
case PlaceholderType::UrlFragment:
|
|
|
|
case PlaceholderType::UrlUserInfo:
|
|
|
|
case PlaceholderType::UrlUserName:
|
|
|
|
case PlaceholderType::UrlPassword: {
|
|
|
|
const QString strUrl = resolveMultiplePlaceholdersRecursive(url(), maxDepth - 1);
|
|
|
|
return resolveUrlPlaceholder(strUrl, typeOfPlaceholder);
|
|
|
|
}
|
|
|
|
case PlaceholderType::CustomAttribute: {
|
|
|
|
const QString key = placeholder.mid(3, placeholder.length() - 4); // {S:attr} => mid(3, len - 4)
|
|
|
|
return attributes()->hasKey(key) ? attributes()->value(key) : QString();
|
|
|
|
}
|
|
|
|
case PlaceholderType::Reference: {
|
|
|
|
// resolving references in format: {REF:<WantedField>@I:<uuid of referenced entry>}
|
|
|
|
// using format from http://keepass.info/help/base/fieldrefs.html at the time of writing,
|
|
|
|
// but supporting lookups of standard fields and references by UUID only
|
|
|
|
|
|
|
|
QString result;
|
|
|
|
QRegExp* referenceRegExp = m_attributes->referenceRegExp();
|
|
|
|
if (referenceRegExp->indexIn(placeholder) != -1) {
|
|
|
|
constexpr int wantedFieldIndex = 1;
|
|
|
|
constexpr int referencedUuidIndex = 2;
|
|
|
|
const Uuid referencedUuid(QByteArray::fromHex(referenceRegExp->cap(referencedUuidIndex).toLatin1()));
|
|
|
|
const Entry* refEntry = m_group->database()->resolveEntry(referencedUuid);
|
|
|
|
if (refEntry) {
|
|
|
|
const QString wantedField = referenceRegExp->cap(wantedFieldIndex).toLower();
|
|
|
|
if (wantedField == "t") {
|
|
|
|
result = refEntry->title();
|
|
|
|
} else if (wantedField == "u") {
|
|
|
|
result = refEntry->username();
|
|
|
|
} else if (wantedField == "p") {
|
|
|
|
result = refEntry->password();
|
|
|
|
} else if (wantedField == "a") {
|
|
|
|
result = refEntry->url();
|
|
|
|
} else if (wantedField == "n") {
|
|
|
|
result = refEntry->notes();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Referencing fields of other entries only works with standard fields, not with custom user strings.
|
|
|
|
// If you want to reference a custom user string, you need to place a redirection in a standard field
|
|
|
|
// of the entry with the custom string, using {S:<Name>}, and reference the standard field.
|
|
|
|
result = refEntry->resolveMultiplePlaceholdersRecursive(result, maxDepth - 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return placeholder;
|
|
|
|
}
|
|
|
|
|
2010-10-06 16:54:07 -04:00
|
|
|
Group* Entry::group()
|
|
|
|
{
|
|
|
|
return m_group;
|
|
|
|
}
|
|
|
|
|
2012-07-23 12:06:04 -04:00
|
|
|
const Group* Entry::group() const
|
|
|
|
{
|
|
|
|
return m_group;
|
|
|
|
}
|
|
|
|
|
2010-08-12 15:38:59 -04:00
|
|
|
void Entry::setGroup(Group* group)
|
2010-08-07 09:10:44 -04:00
|
|
|
{
|
2012-04-27 04:50:32 -04:00
|
|
|
Q_ASSERT(group);
|
|
|
|
|
2012-04-22 06:09:12 -04:00
|
|
|
if (m_group == group) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2010-08-12 15:38:59 -04:00
|
|
|
if (m_group) {
|
2010-08-13 12:08:06 -04:00
|
|
|
m_group->removeEntry(this);
|
2012-04-27 04:50:32 -04:00
|
|
|
if (m_group->database() && m_group->database() != group->database()) {
|
2012-04-21 18:29:39 -04:00
|
|
|
m_group->database()->addDeletedObject(m_uuid);
|
2012-04-27 05:22:02 -04:00
|
|
|
|
|
|
|
// copy custom icon to the new database
|
|
|
|
if (!iconUuid().isNull() && group->database()
|
|
|
|
&& m_group->database()->metadata()->containsCustomIcon(iconUuid())
|
|
|
|
&& !group->database()->metadata()->containsCustomIcon(iconUuid())) {
|
|
|
|
group->database()->metadata()->addCustomIcon(iconUuid(), icon());
|
|
|
|
}
|
2012-04-21 13:06:28 -04:00
|
|
|
}
|
2010-08-12 15:38:59 -04:00
|
|
|
}
|
2012-04-21 13:06:28 -04:00
|
|
|
|
2010-08-12 15:38:59 -04:00
|
|
|
m_group = group;
|
2012-05-12 07:22:41 -04:00
|
|
|
group->addEntry(this);
|
|
|
|
|
2010-08-12 15:38:59 -04:00
|
|
|
QObject::setParent(group);
|
2012-04-22 06:09:12 -04:00
|
|
|
|
|
|
|
if (m_updateTimeinfo) {
|
2015-07-22 17:48:08 -04:00
|
|
|
m_data.timeInfo.setLocationChanged(QDateTime::currentDateTimeUtc());
|
2012-04-22 06:09:12 -04:00
|
|
|
}
|
2010-08-07 09:10:44 -04:00
|
|
|
}
|
2011-07-07 06:42:08 -04:00
|
|
|
|
2012-04-14 09:38:20 -04:00
|
|
|
void Entry::emitDataChanged()
|
2011-07-07 06:42:08 -04:00
|
|
|
{
|
2017-03-10 09:58:42 -05:00
|
|
|
emit dataChanged(this);
|
2011-07-07 06:42:08 -04:00
|
|
|
}
|
2012-04-18 07:57:57 -04:00
|
|
|
|
|
|
|
const Database* Entry::database() const
|
|
|
|
{
|
|
|
|
if (m_group) {
|
|
|
|
return m_group->database();
|
|
|
|
}
|
|
|
|
else {
|
2015-07-24 12:28:12 -04:00
|
|
|
return nullptr;
|
2012-04-18 07:57:57 -04:00
|
|
|
}
|
|
|
|
}
|
2012-05-12 07:22:41 -04:00
|
|
|
|
2017-09-29 14:10:54 -04:00
|
|
|
QString Entry::maskPasswordPlaceholders(const QString &str) const
|
|
|
|
{
|
|
|
|
QString result = str;
|
|
|
|
result.replace(QRegExp("(\\{PASSWORD\\})", Qt::CaseInsensitive, QRegExp::RegExp2), "******");
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2016-11-25 12:26:59 -05:00
|
|
|
QString Entry::resolveMultiplePlaceholders(const QString& str) const
|
2012-07-12 16:33:20 -04:00
|
|
|
{
|
2017-10-16 03:35:40 -04:00
|
|
|
return resolveMultiplePlaceholdersRecursive(str, ResolveMaximumDepth);
|
2016-11-25 12:26:59 -05:00
|
|
|
}
|
2012-07-12 16:33:20 -04:00
|
|
|
|
2017-10-14 12:23:22 -04:00
|
|
|
QString Entry::resolvePlaceholder(const QString& placeholder) const
|
2016-11-25 12:26:59 -05:00
|
|
|
{
|
2017-10-16 03:35:40 -04:00
|
|
|
return resolvePlaceholderRecursive(placeholder, ResolveMaximumDepth);
|
2017-10-14 12:23:22 -04:00
|
|
|
}
|
|
|
|
|
2017-10-16 03:35:40 -04:00
|
|
|
QString Entry::resolveUrlPlaceholder(const QString &str, Entry::PlaceholderType placeholderType) const
|
2017-10-14 12:23:22 -04:00
|
|
|
{
|
2017-10-16 03:35:40 -04:00
|
|
|
if (str.isEmpty())
|
2017-10-14 12:23:22 -04:00
|
|
|
return QString();
|
|
|
|
|
2017-10-16 03:35:40 -04:00
|
|
|
const QUrl qurl(str);
|
2017-10-14 12:23:22 -04:00
|
|
|
switch (placeholderType) {
|
|
|
|
case PlaceholderType::UrlWithoutScheme:
|
|
|
|
return qurl.toString(QUrl::RemoveScheme | QUrl::FullyDecoded);
|
|
|
|
case PlaceholderType::UrlScheme:
|
|
|
|
return qurl.scheme();
|
|
|
|
case PlaceholderType::UrlHost:
|
|
|
|
return qurl.host();
|
|
|
|
case PlaceholderType::UrlPort:
|
|
|
|
return QString::number(qurl.port());
|
|
|
|
case PlaceholderType::UrlPath:
|
|
|
|
return qurl.path();
|
|
|
|
case PlaceholderType::UrlQuery:
|
|
|
|
return qurl.query();
|
|
|
|
case PlaceholderType::UrlFragment:
|
|
|
|
return qurl.fragment();
|
|
|
|
case PlaceholderType::UrlUserInfo:
|
|
|
|
return qurl.userInfo();
|
|
|
|
case PlaceholderType::UrlUserName:
|
|
|
|
return qurl.userName();
|
|
|
|
case PlaceholderType::UrlPassword:
|
|
|
|
return qurl.password();
|
2017-10-16 03:35:40 -04:00
|
|
|
default: {
|
|
|
|
Q_ASSERT_X(false, "Entry::resolveUrlPlaceholder", "Bad url placeholder type");
|
2017-10-14 12:23:22 -04:00
|
|
|
break;
|
2017-02-27 20:25:56 -05:00
|
|
|
}
|
2017-10-16 03:35:40 -04:00
|
|
|
}
|
2017-02-27 20:25:56 -05:00
|
|
|
|
2017-10-16 03:35:40 -04:00
|
|
|
return QString();
|
2017-10-14 12:23:22 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
Entry::PlaceholderType Entry::placeholderType(const QString &placeholder) const
|
|
|
|
{
|
|
|
|
if (!placeholder.startsWith(QLatin1Char('{')) || !placeholder.endsWith(QLatin1Char('}'))) {
|
|
|
|
return PlaceholderType::NotPlaceholder;
|
|
|
|
} else if (placeholder.startsWith(QLatin1Literal("{S:"))) {
|
|
|
|
return PlaceholderType::CustomAttribute;
|
|
|
|
} else if (placeholder.startsWith(QLatin1Literal("{REF:"))) {
|
|
|
|
return PlaceholderType::Reference;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const QMap<QString, PlaceholderType> placeholders {
|
|
|
|
{ QStringLiteral("{TITLE}"), PlaceholderType::Title },
|
|
|
|
{ QStringLiteral("{USERNAME}"), PlaceholderType::UserName },
|
|
|
|
{ QStringLiteral("{PASSWORD}"), PlaceholderType::Password },
|
|
|
|
{ QStringLiteral("{NOTES}"), PlaceholderType::Notes },
|
|
|
|
{ QStringLiteral("{TOTP}"), PlaceholderType::Totp },
|
|
|
|
{ QStringLiteral("{URL}"), PlaceholderType::Url },
|
|
|
|
{ QStringLiteral("{URL:RMVSCM}"), PlaceholderType::UrlWithoutScheme },
|
|
|
|
{ QStringLiteral("{URL:WITHOUTSCHEME}"), PlaceholderType::UrlWithoutScheme },
|
|
|
|
{ QStringLiteral("{URL:SCM}"), PlaceholderType::UrlScheme },
|
|
|
|
{ QStringLiteral("{URL:SCHEME}"), PlaceholderType::UrlScheme },
|
|
|
|
{ QStringLiteral("{URL:HOST}"), PlaceholderType::UrlHost },
|
|
|
|
{ QStringLiteral("{URL:PORT}"), PlaceholderType::UrlPort },
|
|
|
|
{ QStringLiteral("{URL:PATH}"), PlaceholderType::UrlPath },
|
|
|
|
{ QStringLiteral("{URL:QUERY}"), PlaceholderType::UrlQuery },
|
|
|
|
{ QStringLiteral("{URL:FRAGMENT}"), PlaceholderType::UrlFragment },
|
|
|
|
{ QStringLiteral("{URL:USERINFO}"), PlaceholderType::UrlUserInfo },
|
|
|
|
{ QStringLiteral("{URL:USERNAME}"), PlaceholderType::UrlUserName },
|
|
|
|
{ QStringLiteral("{URL:PASSWORD}"), PlaceholderType::UrlPassword }
|
|
|
|
};
|
|
|
|
|
2017-10-16 03:35:40 -04:00
|
|
|
return placeholders.value(placeholder.toUpper(), PlaceholderType::Unknown);
|
2012-07-12 16:33:20 -04:00
|
|
|
}
|
2017-06-29 13:54:49 -04:00
|
|
|
|
|
|
|
QString Entry::resolveUrl(const QString& url) const
|
|
|
|
{
|
2017-07-13 17:10:15 -04:00
|
|
|
QString newUrl = url;
|
2017-10-03 13:11:00 -04:00
|
|
|
if (!url.isEmpty() && !url.contains("://")) {
|
2017-06-29 13:54:49 -04:00
|
|
|
// URL doesn't have a protocol, add https by default
|
2017-07-13 17:10:15 -04:00
|
|
|
newUrl.prepend("https://");
|
2017-06-29 13:54:49 -04:00
|
|
|
}
|
|
|
|
|
2017-10-03 13:11:00 -04:00
|
|
|
if (newUrl.startsWith("cmd://")) {
|
|
|
|
QStringList cmdList = newUrl.split(" ");
|
|
|
|
for (int i=1; i < cmdList.size(); ++i) {
|
|
|
|
// Don't pass arguments to the resolveUrl function (they look like URL's)
|
|
|
|
if (!cmdList[i].startsWith("-") && !cmdList[i].startsWith("/")) {
|
|
|
|
return resolveUrl(cmdList[i].remove(QRegExp("'|\"")));
|
2017-07-13 17:10:15 -04:00
|
|
|
}
|
2017-07-01 08:51:20 -04:00
|
|
|
}
|
2017-10-03 13:11:00 -04:00
|
|
|
|
|
|
|
// No URL in this command
|
|
|
|
return QString("");
|
2017-07-01 08:51:20 -04:00
|
|
|
}
|
2017-10-03 13:11:00 -04:00
|
|
|
|
|
|
|
// Validate the URL
|
|
|
|
QUrl tempUrl = QUrl(newUrl);
|
|
|
|
if (tempUrl.isValid() && (tempUrl.scheme() == "http" || tempUrl.scheme() == "https")) {
|
|
|
|
return tempUrl.url();
|
|
|
|
}
|
|
|
|
|
|
|
|
// No valid http URL's found
|
2017-07-01 08:51:20 -04:00
|
|
|
return QString("");
|
2017-09-29 14:10:54 -04:00
|
|
|
}
|