keepassxc/src/core/Entry.cpp

635 lines
15 KiB
C++
Raw Normal View History

2010-08-07 09:10:44 -04:00
/*
* Copyright (C) 2010 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 "Entry.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"
#include "core/Tools.h"
2012-07-01 15:58:45 -04:00
const int Entry::DefaultIconNumber = 0;
2010-08-13 12:08:06 -04:00
Entry::Entry()
: m_attributes(new EntryAttributes(this))
, m_attachments(new EntryAttachments(this))
, m_autoTypeAssociations(new AutoTypeAssociations(this))
, m_tmpHistoryItem(Q_NULLPTR)
, m_updateTimeinfo(true)
{
m_data.iconNumber = DefaultIconNumber;
m_data.autoTypeEnabled = true;
m_data.autoTypeObfuscation = 0;
connect(m_attributes, SIGNAL(modified()), this, SIGNAL(modified()));
connect(m_attributes, SIGNAL(defaultKeyModified()), SLOT(emitDataChanged()));
connect(m_attachments, SIGNAL(modified()), this, SIGNAL(modified()));
connect(m_autoTypeAssociations, SIGNAL(modified()), SIGNAL(modified()));
connect(this, SIGNAL(modified()), SLOT(updateTimeinfo()));
connect(this, SIGNAL(modified()), SLOT(updateModifiedSinceBegin()));
}
2010-08-18 10:22:48 -04:00
Entry::~Entry()
{
if (m_group) {
m_group->removeEntry(this);
2012-04-21 13:06:28 -04:00
if (m_group->database()) {
m_group->database()->addDeletedObject(m_uuid);
}
}
qDeleteAll(m_history);
2010-08-18 10:22:48 -04:00
}
template <class T> bool Entry::set(T& property, const T& value)
{
2012-04-11 13:51:54 -04:00
if (property != value) {
property = value;
Q_EMIT modified();
return true;
}
else {
return false;
}
}
void Entry::updateTimeinfo()
{
if (m_updateTimeinfo) {
m_data.timeInfo.setLastModificationTime(Tools::currentDateTimeUtc());
m_data.timeInfo.setLastAccessTime(Tools::currentDateTimeUtc());
}
}
void Entry::setUpdateTimeinfo(bool value)
{
2012-04-11 13:51:54 -04:00
m_updateTimeinfo = value;
}
Uuid Entry::uuid() const
{
return m_uuid;
}
QImage Entry::icon() const
{
if (m_data.customIcon.isNull()) {
return databaseIcons()->icon(m_data.iconNumber);
2010-08-24 17:12:01 -04:00
}
else {
// TODO: check if database() is 0
return database()->metadata()->customIcon(m_data.customIcon);
2010-08-24 17:12:01 -04:00
}
}
QPixmap Entry::iconPixmap() const
{
if (m_data.customIcon.isNull()) {
return databaseIcons()->iconPixmap(m_data.iconNumber);
}
else {
QPixmap pixmap;
if (!QPixmapCache::find(m_pixmapCacheKey, &pixmap)) {
// TODO: check if database() is 0
pixmap = QPixmap::fromImage(database()->metadata()->customIcon(m_data.customIcon));
const_cast<Entry*>(this)->m_pixmapCacheKey = QPixmapCache::insert(pixmap);
}
return pixmap;
}
}
int Entry::iconNumber() const
{
return m_data.iconNumber;
}
Uuid Entry::iconUuid() const
{
return m_data.customIcon;
}
QColor Entry::foregroundColor() const
{
return m_data.foregroundColor;
}
QColor Entry::backgroundColor() const
{
return m_data.backgroundColor;
}
QString Entry::overrideUrl() const
{
return m_data.overrideUrl;
}
QString Entry::tags() const
{
return m_data.tags;
}
TimeInfo Entry::timeInfo() const
{
return m_data.timeInfo;
}
bool Entry::autoTypeEnabled() const
{
return m_data.autoTypeEnabled;
}
int Entry::autoTypeObfuscation() const
{
return m_data.autoTypeObfuscation;
}
QString Entry::defaultAutoTypeSequence() const
{
return m_data.defaultAutoTypeSequence;
}
AutoTypeAssociations* Entry::autoTypeAssociations()
{
return m_autoTypeAssociations;
}
const AutoTypeAssociations* Entry::autoTypeAssociations() const
{
return m_autoTypeAssociations;
}
QString Entry::autoTypeSequence(const QString& windowTitle) const
{
if (!m_data.autoTypeEnabled) {
return QString();
}
bool enableSet = false;
QString sequence;
if (!windowTitle.isEmpty()) {
bool match = false;
Q_FOREACH (const AutoTypeAssociations::Association& assoc, m_autoTypeAssociations->getAll()) {
if (windowMatches(windowTitle, assoc.window)) {
if (!assoc.sequence.isEmpty()) {
sequence = assoc.sequence;
}
else {
sequence = m_data.defaultAutoTypeSequence;
}
match = true;
break;
}
}
if (!match) {
return QString();
}
}
else {
sequence = m_data.defaultAutoTypeSequence;
}
Group* group = m_group;
do {
if (!enableSet) {
if (group->autoTypeEnabled() == Group::Disable) {
return QString();
}
else if (group->autoTypeEnabled() == Group::Enable) {
enableSet = true;
}
}
if (sequence.isEmpty()) {
sequence = group->defaultAutoTypeSequence();
}
group = group->parentGroup();
} while (group && (!enableSet || sequence.isEmpty()));
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;
}
QString Entry::title() const
{
return m_attributes->value("Title");
}
QString Entry::url() const
{
return m_attributes->value("URL");
}
QString Entry::username() const
{
return m_attributes->value("UserName");
}
QString Entry::password() const
{
return m_attributes->value("Password");
}
QString Entry::notes() const
{
return m_attributes->value("Notes");
}
bool Entry::isExpired() const
{
return m_data.timeInfo.expires() && m_data.timeInfo.expiryTime() < Tools::currentDateTimeUtc();
}
EntryAttributes* Entry::attributes()
{
return m_attributes;
}
const EntryAttributes* Entry::attributes() const
{
return m_attributes;
}
EntryAttachments* Entry::attachments()
{
return m_attachments;
}
const EntryAttachments* Entry::attachments() const
{
return m_attachments;
}
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);
}
void Entry::setIcon(int iconNumber)
{
2010-08-14 06:24:35 -04:00
Q_ASSERT(iconNumber >= 0);
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
m_pixmapCacheKey = QPixmapCache::Key();
2012-04-11 13:51:54 -04:00
Q_EMIT modified();
emitDataChanged();
2012-04-11 13:51:54 -04:00
}
}
void Entry::setIcon(const Uuid& uuid)
{
2010-08-14 06:24:35 -04:00
Q_ASSERT(!uuid.isNull());
if (m_data.customIcon != uuid) {
m_data.customIcon = uuid;
m_data.iconNumber = 0;
2012-04-11 13:51:54 -04:00
m_pixmapCacheKey = QPixmapCache::Key();
Q_EMIT modified();
emitDataChanged();
2012-04-11 13:51:54 -04:00
}
}
void Entry::setForegroundColor(const QColor& color)
{
set(m_data.foregroundColor, color);
}
void Entry::setBackgroundColor(const QColor& color)
{
set(m_data.backgroundColor, color);
}
void Entry::setOverrideUrl(const QString& url)
{
set(m_data.overrideUrl, url);
}
void Entry::setTags(const QString& tags)
{
set(m_data.tags, tags);
}
void Entry::setTimeInfo(const TimeInfo& timeInfo)
{
m_data.timeInfo = timeInfo;
}
void Entry::setAutoTypeEnabled(bool enable)
{
set(m_data.autoTypeEnabled, enable);
}
void Entry::setAutoTypeObfuscation(int obfuscation)
{
set(m_data.autoTypeObfuscation, obfuscation);
}
void Entry::setDefaultAutoTypeSequence(const QString& sequence)
{
set(m_data.defaultAutoTypeSequence, sequence);
}
void Entry::setTitle(const QString& title)
{
m_attributes->set("Title", title, m_attributes->isProtected("Title"));
}
void Entry::setUrl(const QString& url)
{
m_attributes->set("URL", url, m_attributes->isProtected("URL"));
}
void Entry::setUsername(const QString& username)
{
m_attributes->set("UserName", username, m_attributes->isProtected("UserName"));
}
void Entry::setPassword(const QString& password)
{
m_attributes->set("Password", password, m_attributes->isProtected("Password"));
}
void Entry::setNotes(const QString& notes)
{
m_attributes->set("Notes", notes, m_attributes->isProtected("Notes"));
}
void Entry::setExpires(const bool& value)
{
if (m_data.timeInfo.expires() != value) {
m_data.timeInfo.setExpires(value);
Q_EMIT modified();
}
}
void Entry::setExpiryTime(const QDateTime& dateTime)
{
if (m_data.timeInfo.expiryTime() != dateTime) {
m_data.timeInfo.setExpiryTime(dateTime);
Q_EMIT modified();
}
}
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());
Q_ASSERT(entry->uuid() == uuid());
m_history.append(entry);
2012-04-11 13:51:54 -04:00
Q_EMIT modified();
}
void Entry::removeHistoryItems(QList<Entry*> historyEntries)
{
bool emitModified = historyEntries.count() > 0;
Q_FOREACH (Entry* entry, historyEntries) {
Q_ASSERT(!entry->parent());
Q_ASSERT(entry->uuid() == uuid());
Q_ASSERT(m_history.removeAll(entry) > 0);
delete entry;
}
if (emitModified) {
Q_EMIT modified();
}
}
2012-06-29 18:22:07 -04:00
void Entry::truncateHistory()
{
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;
QSet<QByteArray> foundAttachements = attachments()->values().toSet();
2012-05-14 10:27:59 -04:00
QMutableListIterator<Entry*> i(m_history);
i.toBack();
while (i.hasPrevious()) {
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) {
size += historyItem->attributes()->attributesSize();
QSet<QByteArray> newAttachments = historyItem->attachments()->values().toSet() - foundAttachements;
Q_FOREACH (const QByteArray& attachment, newAttachments) {
size += attachment.size();
}
foundAttachements += 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) {
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
}
}
}
2012-05-15 15:10:39 -04:00
Entry* Entry::clone() const
{
Entry* entry = new Entry();
entry->setUpdateTimeinfo(false);
entry->m_uuid = m_uuid;
entry->m_data = m_data;
entry->m_attributes->copyDataFrom(m_attributes);
entry->m_attachments->copyDataFrom(m_attachments);
entry->m_autoTypeAssociations->copyDataFrom(this->m_autoTypeAssociations);
2012-05-15 15:10:39 -04:00
entry->setUpdateTimeinfo(true);
return entry;
}
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;
m_tmpHistoryItem->m_attributes->copyDataFrom(m_attributes);
m_tmpHistoryItem->m_attachments->copyDataFrom(m_attachments);
m_modifiedSinceBegin = false;
}
void Entry::endUpdate()
{
Q_ASSERT(m_tmpHistoryItem);
if (m_modifiedSinceBegin) {
m_tmpHistoryItem->setUpdateTimeinfo(true);
addHistoryItem(m_tmpHistoryItem);
2012-05-10 03:56:41 -04:00
truncateHistory();
}
else {
delete m_tmpHistoryItem;
}
2012-06-29 08:15:16 -04:00
m_tmpHistoryItem = Q_NULLPTR;
}
void Entry::updateModifiedSinceBegin()
{
m_modifiedSinceBegin = true;
}
2010-10-06 16:54:07 -04:00
Group* Entry::group()
{
return m_group;
}
void Entry::setGroup(Group* group)
2010-08-07 09:10:44 -04:00
{
Q_ASSERT(group);
if (m_group == group) {
return;
}
if (m_group) {
2010-08-13 12:08:06 -04:00
m_group->removeEntry(this);
if (m_group->database() && m_group->database() != group->database()) {
m_group->database()->addDeletedObject(m_uuid);
// 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
}
}
2012-04-21 13:06:28 -04:00
m_group = group;
2012-05-12 07:22:41 -04:00
group->addEntry(this);
QObject::setParent(group);
if (m_updateTimeinfo) {
m_data.timeInfo.setLocationChanged(Tools::currentDateTimeUtc());
}
2010-08-07 09:10:44 -04:00
}
void Entry::emitDataChanged()
{
Q_EMIT dataChanged(this);
}
const Database* Entry::database() const
{
if (m_group) {
return m_group->database();
}
else {
return Q_NULLPTR;
}
}
2012-05-12 07:22:41 -04:00
bool Entry::match(const QString& searchTerm, Qt::CaseSensitivity caseSensitivity)
{
return title().contains(searchTerm, caseSensitivity) ||
username().contains(searchTerm, caseSensitivity) ||
url().contains(searchTerm, caseSensitivity) ||
notes().contains(searchTerm, caseSensitivity);
}
bool Entry::windowMatches(const QString& windowTitle, const QString& windowPattern)
{
QRegExp regExp;
regExp.setCaseSensitivity(Qt::CaseInsensitive);
if (windowPattern.startsWith("//") && windowPattern.endsWith("//") && windowPattern.size() >= 4) {
regExp.setPatternSyntax(QRegExp::RegExp2);
regExp.setPattern(windowPattern.mid(2, windowPattern.size() - 4));
}
else {
regExp.setPatternSyntax(QRegExp::Wildcard);
regExp.setPattern(windowPattern);
}
return regExp.exactMatch(windowTitle);
}
QString Entry::resolvePlaceholders(const QString& str) const
{
QString result = str;
result.replace("{TITLE}", title(), Qt::CaseInsensitive);
result.replace("{USERNAME}", username(), Qt::CaseInsensitive);
result.replace("{URL}", url(), Qt::CaseInsensitive);
result.replace("{PASSWORD}", password(), Qt::CaseInsensitive);
result.replace("{NOTES}", notes(), Qt::CaseInsensitive);
// TODO: lots of other placeholders missing
return result;
}