diff options
Diffstat (limited to 'src/core/Group.cpp')
-rw-r--r-- | src/core/Group.cpp | 317 |
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; + } |