mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2026-01-06 02:55:43 -05:00
Improve OPVault handling and replace test opvault
* Fix various bugs in opvault parsing to include: TOTP parsing, date handling, naming convention, attachments, and multiple url's. * Remove category groups that don't have any entries. * Simplify tests by focusing on the resulting database instead of the parsing mechanics. * Remove proprietary "freddy" opvault in favor of self-made "keepassxc" opvault. * Fix #4069, select opvault file on macOS
This commit is contained in:
parent
560209550c
commit
612f8d2e5b
38 changed files with 176 additions and 937 deletions
|
|
@ -31,10 +31,29 @@
|
|||
#include <QUrlQuery>
|
||||
#include <QUuid>
|
||||
|
||||
namespace
|
||||
{
|
||||
QDateTime resolveDate(const QString& kind, const QJsonValue& value)
|
||||
{
|
||||
QDateTime date;
|
||||
if (kind == "monthYear") {
|
||||
// 1Password programmers are sadistic...
|
||||
auto dateValue = QString::number(value.toInt());
|
||||
date = QDateTime::fromString(dateValue, "yyyyMM");
|
||||
date.setTimeSpec(Qt::UTC);
|
||||
} else if (value.isString()) {
|
||||
date = QDateTime::fromTime_t(value.toString().toUInt(), Qt::UTC);
|
||||
} else {
|
||||
date = QDateTime::fromTime_t(value.toInt(), Qt::UTC);
|
||||
}
|
||||
return date;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void OpVaultReader::fillFromSection(Entry* entry, const QJsonObject& section)
|
||||
{
|
||||
const auto uuid = entry->uuid();
|
||||
const QString& sectionName = section["name"].toString();
|
||||
QString sectionName = section["name"].toString();
|
||||
|
||||
if (!section.contains("fields")) {
|
||||
auto sectionNameLC = sectionName.toLower();
|
||||
|
|
@ -47,6 +66,12 @@ void OpVaultReader::fillFromSection(Entry* entry, const QJsonObject& section)
|
|||
qWarning() << R"(Skipping non-Array "fields" in UUID ")" << uuid << "\"\n";
|
||||
return;
|
||||
}
|
||||
|
||||
// If we have a default section name then replace with the section title if not empty
|
||||
if (sectionName.startsWith("Section_") && !section["title"].toString().isEmpty()) {
|
||||
sectionName = section["title"].toString();
|
||||
}
|
||||
|
||||
QJsonArray sectionFields = section["fields"].toArray();
|
||||
for (const QJsonValue sectionField : sectionFields) {
|
||||
if (!sectionField.isObject()) {
|
||||
|
|
@ -68,7 +93,7 @@ void OpVaultReader::fillFromSectionField(Entry* entry, const QString& sectionNam
|
|||
// Ignore "a" and "inputTraits" fields, they don't apply to KPXC
|
||||
|
||||
auto attrName = resolveAttributeName(sectionName, field["n"].toString(), field["t"].toString());
|
||||
auto attrValue = field.value("v").toVariant().toString();
|
||||
auto attrValue = field.value("v").toString();
|
||||
auto kind = field["k"].toString();
|
||||
|
||||
if (attrName.startsWith("TOTP_")) {
|
||||
|
|
@ -82,30 +107,37 @@ void OpVaultReader::fillFromSectionField(Entry* entry, const QString& sectionNam
|
|||
query.addQueryItem("period", QString("%1").arg(Totp::DEFAULT_STEP));
|
||||
}
|
||||
attrValue = query.toString(QUrl::FullyEncoded);
|
||||
}
|
||||
entry->setTotp(Totp::parseSettings(attrValue));
|
||||
} else if (attrName.startsWith("expir", Qt::CaseInsensitive)) {
|
||||
QDateTime expiry;
|
||||
if (kind == "date") {
|
||||
expiry = QDateTime::fromTime_t(attrValue.toUInt(), Qt::UTC);
|
||||
entry->setTotp(Totp::parseSettings(attrValue));
|
||||
} else {
|
||||
expiry = QDateTime::fromString(attrValue, "yyyyMM");
|
||||
expiry.setTimeSpec(Qt::UTC);
|
||||
entry->setTotp(Totp::parseSettings({}, attrValue));
|
||||
}
|
||||
|
||||
} else if (attrName.startsWith("expir", Qt::CaseInsensitive)) {
|
||||
QDateTime expiry = resolveDate(kind, field.value("v"));
|
||||
if (expiry.isValid()) {
|
||||
entry->setExpiryTime(expiry);
|
||||
entry->setExpires(true);
|
||||
} else {
|
||||
qWarning() << QString("[%1] Invalid expiration date found: %2").arg(entry->title(), attrValue);
|
||||
}
|
||||
} else {
|
||||
if (kind == "date") {
|
||||
auto date = QDateTime::fromTime_t(attrValue.toUInt(), Qt::UTC);
|
||||
if (kind == "date" || kind == "monthYear") {
|
||||
QDateTime date = resolveDate(kind, field.value("v"));
|
||||
if (date.isValid()) {
|
||||
attrValue = date.toString();
|
||||
entry->attributes()->set(attrName, date.toString(Qt::SystemLocaleShortDate));
|
||||
} else {
|
||||
qWarning()
|
||||
<< QString("[%1] Invalid date attribute found: %2 = %3").arg(entry->title(), attrName, attrValue);
|
||||
}
|
||||
} else if (kind == "address") {
|
||||
// Expand address into multiple attributes
|
||||
auto addrFields = field.value("v").toObject().toVariantMap();
|
||||
for (auto part : addrFields.keys()) {
|
||||
entry->attributes()->set(attrName + QString("_%1").arg(part), addrFields.value(part).toString());
|
||||
}
|
||||
} else {
|
||||
entry->attributes()->set(attrName, attrValue, (kind == "password" || kind == "concealed"));
|
||||
}
|
||||
|
||||
entry->attributes()->set(attrName, attrValue, (kind == "password" || kind == "concealed"));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -118,7 +150,7 @@ QString OpVaultReader::resolveAttributeName(const QString& section, const QStrin
|
|||
|
||||
auto lowName = name.toLower();
|
||||
auto lowText = text.toLower();
|
||||
if (section.isEmpty()) {
|
||||
if (section.isEmpty() || name.startsWith("address")) {
|
||||
// Empty section implies these are core attributes
|
||||
// try to find username, password, url
|
||||
if (lowName == "password" || lowText == "password") {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue