Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/keepassxreboot/keepassxc.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/Group.cpp')
-rw-r--r--src/core/Group.cpp317
1 files changed, 231 insertions, 86 deletions
diff --git a/src/core/Group.cpp b/src/core/Group.cpp
index a34421e63..692672652 100644
--- a/src/core/Group.cpp
+++ b/src/core/Group.cpp
@@ -25,15 +25,25 @@
const int Group::DefaultIconNumber = 48;
const int Group::RecycleBinIconNumber = 43;
+const QString Group::RootAutoTypeSequence = "{USERNAME}{TAB}{PASSWORD}{ENTER}";
+
+Group::CloneFlags Group::DefaultCloneFlags = static_cast<Group::CloneFlags>(
+ Group::CloneNewUuid | Group::CloneResetTimeInfo | Group::CloneIncludeEntries);
+Entry::CloneFlags Group::DefaultEntryCloneFlags = static_cast<Entry::CloneFlags>(
+ Entry::CloneNewUuid | Entry::CloneResetTimeInfo);
Group::Group()
- : m_updateTimeinfo(true)
+ : m_customData(new CustomData(this))
+ , m_updateTimeinfo(true)
{
m_data.iconNumber = DefaultIconNumber;
m_data.isExpanded = true;
m_data.autoTypeEnabled = Inherit;
m_data.searchingEnabled = Inherit;
m_data.mergeMode = ModeInherit;
+
+ connect(m_customData, SIGNAL(modified()), this, SIGNAL(modified()));
+ connect(this, SIGNAL(modified()), SLOT(updateTimeinfo()));
}
Group::~Group()
@@ -74,11 +84,9 @@ Group* Group::createRecycleBin()
template <class P, class V> inline bool Group::set(P& property, const V& value) {
if (property != value) {
property = value;
- updateTimeinfo();
emit modified();
return true;
- }
- else {
+ } else {
return false;
}
}
@@ -188,6 +196,10 @@ QString Group::defaultAutoTypeSequence() const
return m_data.defaultAutoTypeSequence;
}
+/**
+ * Determine the effective sequence that will be injected
+ * This function return an empty string if the current group or any parent has autotype disabled
+ */
QString Group::effectiveAutoTypeSequence() const
{
QString sequence;
@@ -203,7 +215,7 @@ QString Group::effectiveAutoTypeSequence() const
} while (group && sequence.isEmpty());
if (sequence.isEmpty()) {
- sequence = "{USERNAME}{TAB}{PASSWORD}{ENTER}";
+ sequence = RootAutoTypeSequence;
}
return sequence;
@@ -242,6 +254,16 @@ bool Group::isExpired() const
return m_data.timeInfo.expires() && m_data.timeInfo.expiryTime() < QDateTime::currentDateTimeUtc();
}
+CustomData* Group::customData()
+{
+ return m_customData;
+}
+
+const CustomData* Group::customData() const
+{
+ return m_customData;
+}
+
void Group::setUuid(const Uuid& uuid)
{
set(m_uuid, uuid);
@@ -266,8 +288,6 @@ void Group::setIcon(int iconNumber)
if (m_data.iconNumber != iconNumber || !m_data.customIcon.isNull()) {
m_data.iconNumber = iconNumber;
m_data.customIcon = Uuid();
-
- updateTimeinfo();
emit modified();
emit dataChanged(this);
}
@@ -280,8 +300,6 @@ void Group::setIcon(const Uuid& uuid)
if (m_data.customIcon != uuid) {
m_data.customIcon = uuid;
m_data.iconNumber = 0;
-
- updateTimeinfo();
emit modified();
emit dataChanged(this);
}
@@ -296,8 +314,8 @@ void Group::setExpanded(bool expanded)
{
if (m_data.isExpanded != expanded) {
m_data.isExpanded = expanded;
- updateTimeinfo();
if (config()->get("IgnoreGroupExpansion").toBool()) {
+ updateTimeinfo();
return;
}
emit modified();
@@ -328,7 +346,6 @@ void Group::setExpires(bool value)
{
if (m_data.timeInfo.expires() != value) {
m_data.timeInfo.setExpires(value);
- updateTimeinfo();
emit modified();
}
}
@@ -337,7 +354,6 @@ void Group::setExpiryTime(const QDateTime& dateTime)
{
if (m_data.timeInfo.expiryTime() != dateTime) {
m_data.timeInfo.setExpiryTime(dateTime);
- updateTimeinfo();
emit modified();
}
}
@@ -435,6 +451,23 @@ void Group::setParent(Database* db)
QObject::setParent(db);
}
+QStringList Group::hierarchy() const
+{
+ QStringList hierarchy;
+ const Group* group = this;
+ const Group* parent = m_parent;
+ hierarchy.prepend(group->name());
+
+ while (parent) {
+ group = group->parentGroup();
+ parent = group->parentGroup();
+
+ hierarchy.prepend(group->name());
+ }
+
+ return hierarchy;
+}
+
Database* Group::database()
{
return m_db;
@@ -488,18 +521,17 @@ Entry* Group::findEntry(QString entryId)
{
Q_ASSERT(!entryId.isNull());
+ Entry* entry;
if (Uuid::isUuid(entryId)) {
- Uuid entryUuid = Uuid::fromHex(entryId);
- for (Entry* entry : entriesRecursive(false)) {
- if (entry->uuid() == entryUuid) {
- return entry;
- }
+ entry = findEntryByUuid(Uuid::fromHex(entryId));
+ if (entry) {
+ return entry;
}
}
- Entry* entry = findEntryByPath(entryId);
+ entry = findEntryByPath(entryId);
if (entry) {
- return entry;
+ return entry;
}
for (Entry* entry : entriesRecursive(false)) {
@@ -514,7 +546,7 @@ Entry* Group::findEntry(QString entryId)
Entry* Group::findEntryByUuid(const Uuid& uuid)
{
Q_ASSERT(!uuid.isNull());
- for (Entry* entry : asConst(m_entries)) {
+ for (Entry* entry : entriesRecursive(false)) {
if (entry->uuid() == uuid) {
return entry;
}
@@ -575,10 +607,9 @@ Group* Group::findGroupByPath(QString groupPath, QString basePath)
}
return nullptr;
-
}
-QString Group::print(bool printUuids, QString baseName, int depth)
+QString Group::print(bool recursive, int depth)
{
QString response;
@@ -590,21 +621,14 @@ QString Group::print(bool printUuids, QString baseName, int depth)
}
for (Entry* entry : entries()) {
- response += indentation + entry->title();
- if (printUuids) {
- response += " " + entry->uuid().toHex();
- }
- response += "\n";
+ response += indentation + entry->title() + "\n";
}
for (Group* innerGroup : children()) {
- QString newBaseName = baseName + innerGroup->name() + "/";
- response += indentation + newBaseName;
- if (printUuids) {
- response += " " + innerGroup->uuid().toHex();
+ response += indentation + innerGroup->name() + "/\n";
+ if (recursive) {
+ response += innerGroup->print(recursive, depth + 1);
}
- response += "\n";
- response += innerGroup->print(printUuids, newBaseName, depth + 1);
}
return response;
@@ -662,31 +686,71 @@ QSet<Uuid> Group::customIconsRecursive() const
void Group::merge(const Group* other)
{
+
+ Group* rootGroup = this;
+ while (rootGroup->parentGroup()) {
+ rootGroup = rootGroup->parentGroup();
+ }
+
// merge entries
const QList<Entry*> dbEntries = other->entries();
for (Entry* entry : dbEntries) {
- // entries are searched by uuid
- if (!findEntryByUuid(entry->uuid())) {
+
+ Entry* existingEntry = rootGroup->findEntryByUuid(entry->uuid());
+
+ if (!existingEntry) {
+ // This entry does not exist at all. Create it.
+ qDebug("New entry %s detected. Creating it.", qPrintable(entry->title()));
entry->clone(Entry::CloneIncludeHistory)->setGroup(this);
} else {
- resolveConflict(findEntryByUuid(entry->uuid()), entry);
+ // Entry is already present in the database. Update it.
+ bool locationChanged = existingEntry->timeInfo().locationChanged() < entry->timeInfo().locationChanged();
+ if (locationChanged && existingEntry->group() != this) {
+ existingEntry->setGroup(this);
+ qDebug("Location changed for entry %s. Updating it", qPrintable(existingEntry->title()));
+ }
+ resolveEntryConflict(existingEntry, entry);
}
}
- // merge groups (recursively)
+ // merge groups recursively
const QList<Group*> dbChildren = other->children();
for (Group* group : dbChildren) {
- // groups are searched by name instead of uuid
- if (findChildByName(group->name())) {
- findChildByName(group->name())->merge(group);
+
+ Group* existingGroup = rootGroup->findChildByUuid(group->uuid());
+
+ if (!existingGroup) {
+ qDebug("New group %s detected. Creating it.", qPrintable(group->name()));
+ Group* newGroup = group->clone(Entry::CloneNoFlags, Group::CloneNoFlags);
+ newGroup->setParent(this);
+ newGroup->merge(group);
} else {
- group->setParent(this);
+ bool locationChanged = existingGroup->timeInfo().locationChanged() < group->timeInfo().locationChanged();
+ if (locationChanged && existingGroup->parent() != this) {
+ existingGroup->setParent(this);
+ qDebug("Location changed for group %s. Updating it", qPrintable(existingGroup->name()));
+ }
+ resolveGroupConflict(existingGroup, group);
+ existingGroup->merge(group);
}
+
}
emit modified();
}
+Group* Group::findChildByUuid(const Uuid& uuid)
+{
+ Q_ASSERT(!uuid.isNull());
+ for (Group* group : groupsRecursive(true)) {
+ if (group->uuid() == uuid) {
+ return group;
+ }
+ }
+
+ return nullptr;
+}
+
Group* Group::findChildByName(const QString& name)
{
for (Group* group : asConst(m_children)) {
@@ -698,34 +762,44 @@ Group* Group::findChildByName(const QString& name)
return nullptr;
}
-Group* Group::clone(Entry::CloneFlags entryFlags) const
+Group* Group::clone(Entry::CloneFlags entryFlags, Group::CloneFlags groupFlags) const
{
Group* clonedGroup = new Group();
clonedGroup->setUpdateTimeinfo(false);
- clonedGroup->setUuid(Uuid::random());
+ if (groupFlags & Group::CloneNewUuid) {
+ clonedGroup->setUuid(Uuid::random());
+ } else {
+ clonedGroup->setUuid(this->uuid());
+ }
+
clonedGroup->m_data = m_data;
+ clonedGroup->m_customData->copyDataFrom(m_customData);
- const QList<Entry*> entryList = entries();
- for (Entry* entry : entryList) {
- Entry* clonedEntry = entry->clone(entryFlags);
- clonedEntry->setGroup(clonedGroup);
- }
+ if (groupFlags & Group::CloneIncludeEntries) {
+ const QList<Entry*> entryList = entries();
+ for (Entry* entry : entryList) {
+ Entry* clonedEntry = entry->clone(entryFlags);
+ clonedEntry->setGroup(clonedGroup);
+ }
- const QList<Group*> childrenGroups = children();
- for (Group* groupChild : childrenGroups) {
- Group* clonedGroupChild = groupChild->clone(entryFlags);
- clonedGroupChild->setParent(clonedGroup);
+ const QList<Group*> childrenGroups = children();
+ for (Group* groupChild : childrenGroups) {
+ Group* clonedGroupChild = groupChild->clone(entryFlags, groupFlags);
+ clonedGroupChild->setParent(clonedGroup);
+ }
}
clonedGroup->setUpdateTimeinfo(true);
+ if (groupFlags & Group::CloneResetTimeInfo) {
- QDateTime now = QDateTime::currentDateTimeUtc();
- clonedGroup->m_data.timeInfo.setCreationTime(now);
- clonedGroup->m_data.timeInfo.setLastModificationTime(now);
- clonedGroup->m_data.timeInfo.setLastAccessTime(now);
- clonedGroup->m_data.timeInfo.setLocationChanged(now);
+ QDateTime now = QDateTime::currentDateTimeUtc();
+ clonedGroup->m_data.timeInfo.setCreationTime(now);
+ clonedGroup->m_data.timeInfo.setLastModificationTime(now);
+ clonedGroup->m_data.timeInfo.setLastAccessTime(now);
+ clonedGroup->m_data.timeInfo.setLocationChanged(now);
+ }
return clonedGroup;
}
@@ -733,6 +807,7 @@ Group* Group::clone(Entry::CloneFlags entryFlags) const
void Group::copyDataFrom(const Group* other)
{
m_data = other->m_data;
+ m_customData->copyDataFrom(other->m_customData);
m_lastTopVisibleEntry = other->m_lastTopVisibleEntry;
}
@@ -836,8 +911,7 @@ void Group::markOlderEntry(Entry* entry)
{
entry->attributes()->set(
"merged",
- QString("older entry merged from database \"%1\"").arg(entry->group()->database()->metadata()->name())
- );
+ QString("older entry merged from database \"%1\"").arg(entry->group()->database()->metadata()->name()));
}
bool Group::resolveSearchingEnabled() const
@@ -880,39 +954,110 @@ bool Group::resolveAutoTypeEnabled() const
}
}
-void Group::resolveConflict(Entry* existingEntry, Entry* otherEntry)
+void Group::resolveEntryConflict(Entry* existingEntry, Entry* otherEntry)
{
const QDateTime timeExisting = existingEntry->timeInfo().lastModificationTime();
const QDateTime timeOther = otherEntry->timeInfo().lastModificationTime();
Entry* clonedEntry;
- switch(mergeMode()) {
- case KeepBoth:
- // if one entry is newer, create a clone and add it to the group
- if (timeExisting > timeOther) {
- clonedEntry = otherEntry->clone(Entry::CloneIncludeHistory);
- clonedEntry->setGroup(this);
- markOlderEntry(clonedEntry);
- } else if (timeExisting < timeOther) {
- clonedEntry = otherEntry->clone(Entry::CloneIncludeHistory);
- clonedEntry->setGroup(this);
- markOlderEntry(existingEntry);
- }
- break;
- case KeepNewer:
- if (timeExisting < timeOther) {
- // only if other entry is newer, replace existing one
- removeEntry(existingEntry);
- clonedEntry = otherEntry->clone(Entry::CloneIncludeHistory);
- clonedEntry->setGroup(this);
- }
+ switch (mergeMode()) {
+ case KeepBoth:
+ // if one entry is newer, create a clone and add it to the group
+ if (timeExisting > timeOther) {
+ clonedEntry = otherEntry->clone(Entry::CloneNewUuid | Entry::CloneIncludeHistory);
+ clonedEntry->setGroup(this);
+ markOlderEntry(clonedEntry);
+ } else if (timeExisting < timeOther) {
+ clonedEntry = otherEntry->clone(Entry::CloneNewUuid | Entry::CloneIncludeHistory);
+ clonedEntry->setGroup(this);
+ markOlderEntry(existingEntry);
+ }
+ break;
+ case KeepNewer:
+ if (timeExisting < timeOther) {
+ qDebug("Updating entry %s.", qPrintable(existingEntry->title()));
+ // only if other entry is newer, replace existing one
+ Group* currentGroup = existingEntry->group();
+ currentGroup->removeEntry(existingEntry);
+ otherEntry->clone(Entry::CloneIncludeHistory)->setGroup(currentGroup);
+ }
- break;
- case KeepExisting:
- break;
- default:
- // do nothing
- break;
+ break;
+ case KeepExisting:
+ break;
+ default:
+ // do nothing
+ break;
+ }
+}
+
+void Group::resolveGroupConflict(Group* existingGroup, Group* otherGroup)
+{
+ const QDateTime timeExisting = existingGroup->timeInfo().lastModificationTime();
+ const QDateTime timeOther = otherGroup->timeInfo().lastModificationTime();
+
+ // only if the other group is newer, update the existing one.
+ if (timeExisting < timeOther) {
+ qDebug("Updating group %s.", qPrintable(existingGroup->name()));
+ existingGroup->setName(otherGroup->name());
+ existingGroup->setNotes(otherGroup->notes());
+ if (otherGroup->iconNumber() == 0) {
+ existingGroup->setIcon(otherGroup->iconUuid());
+ } else {
+ existingGroup->setIcon(otherGroup->iconNumber());
+ }
+ existingGroup->setExpiryTime(otherGroup->timeInfo().expiryTime());
}
+
+}
+
+QStringList Group::locate(QString locateTerm, QString currentPath)
+{
+ Q_ASSERT(!locateTerm.isNull());
+ QStringList response;
+
+ for (Entry* entry : asConst(m_entries)) {
+ QString entryPath = currentPath + entry->title();
+ if (entryPath.toLower().contains(locateTerm.toLower())) {
+ response << entryPath;
+ }
+ }
+
+ for (Group* group : asConst(m_children)) {
+ for (QString path : group->locate(locateTerm, currentPath + group->name() + QString("/"))) {
+ response << path;
+ }
+ }
+
+ return response;
+}
+
+Entry* Group::addEntryWithPath(QString entryPath)
+{
+ Q_ASSERT(!entryPath.isNull());
+ if (this->findEntryByPath(entryPath)) {
+ return nullptr;
+ }
+
+ QStringList groups = entryPath.split("/");
+ QString entryTitle = groups.takeLast();
+ QString groupPath = groups.join("/");
+ if (groupPath.isNull()) {
+ groupPath = QString("");
+ }
+
+ Q_ASSERT(!groupPath.isNull());
+ Group* group = this->findGroupByPath(groupPath);
+ if (!group) {
+ return nullptr;
+ }
+
+ Entry* entry = new Entry();
+ entry->setTitle(entryTitle);
+ entry->setUuid(Uuid::random());
+ entry->setGroup(group);
+
+ return entry;
+
}