diff --git a/src/cli/Add.cpp b/src/cli/Add.cpp new file mode 100644 index 000000000..13023b5bb --- /dev/null +++ b/src/cli/Add.cpp @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2017 KeePassXC Team + * + * 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 . + */ + +#include +#include + +#include "Add.h" + +#include +#include + +#include "cli/Utils.h" +#include "core/Database.h" +#include "core/Entry.h" +#include "core/Group.h" +#include "core/PasswordGenerator.h" + +Add::Add() +{ + this->name = QString("add"); + this->description = QObject::tr("Add a new entry to a database."); +} + +Add::~Add() +{ +} + +int Add::execute(QStringList arguments) +{ + + QTextStream inputTextStream(stdin, QIODevice::ReadOnly); + QTextStream outputTextStream(stdout, QIODevice::WriteOnly); + + QCommandLineParser parser; + parser.setApplicationDescription(this->description); + parser.addPositionalArgument("database", QObject::tr("Path of the database.")); + + QCommandLineOption keyFile(QStringList() << "k" + << "key-file", + QObject::tr("Key file of the database."), + QObject::tr("path")); + parser.addOption(keyFile); + + QCommandLineOption username(QStringList() << "u" + << "username", + QObject::tr("Username for the entry."), + QObject::tr("username")); + parser.addOption(username); + + QCommandLineOption url(QStringList() << "url", QObject::tr("URL for the entry."), QObject::tr("URL")); + parser.addOption(url); + + QCommandLineOption prompt(QStringList() << "p" + << "password-prompt", + QObject::tr("Prompt for the entry's password.")); + parser.addOption(prompt); + + QCommandLineOption generate(QStringList() << "g" + << "generate", + QObject::tr("Generate a password for the entry.")); + parser.addOption(generate); + + QCommandLineOption length(QStringList() << "l" + << "password-length", + QObject::tr("Length for the generated password."), + QObject::tr("length")); + parser.addOption(length); + + parser.addPositionalArgument("entry", QObject::tr("Path of the entry to add.")); + parser.process(arguments); + + const QStringList args = parser.positionalArguments(); + if (args.size() != 2) { + outputTextStream << parser.helpText().replace("keepassxc-cli", "keepassxc-cli add"); + return EXIT_FAILURE; + } + + QString databasePath = args.at(0); + QString entryPath = args.at(1); + + Database* db = Database::unlockFromStdin(databasePath, parser.value(keyFile)); + if (db == nullptr) { + return EXIT_FAILURE; + } + + // Validating the password length here, before we actually create + // the entry. + QString passwordLength = parser.value(length); + if (!passwordLength.isEmpty() && !passwordLength.toInt()) { + qCritical("Invalid value for password length %s.", qPrintable(passwordLength)); + return EXIT_FAILURE; + } + + Entry* entry = db->rootGroup()->addEntryWithPath(entryPath); + if (!entry) { + qCritical("Could not create entry with path %s.", qPrintable(entryPath)); + return EXIT_FAILURE; + } + + if (!parser.value("username").isEmpty()) { + entry->setUsername(parser.value("username")); + } + + if (!parser.value("url").isEmpty()) { + entry->setUrl(parser.value("url")); + } + + if (parser.isSet(prompt)) { + outputTextStream << "Enter password for new entry: "; + outputTextStream.flush(); + QString password = Utils::getPassword(); + entry->setPassword(password); + } else if (parser.isSet(generate)) { + PasswordGenerator passwordGenerator; + + if (passwordLength.isEmpty()) { + passwordGenerator.setLength(PasswordGenerator::DefaultLength); + } else { + passwordGenerator.setLength(passwordLength.toInt()); + } + + passwordGenerator.setCharClasses(PasswordGenerator::LowerLetters | PasswordGenerator::UpperLetters | + PasswordGenerator::Numbers); + QString password = passwordGenerator.generatePassword(); + entry->setPassword(password); + } + + QString errorMessage = db->saveToFile(databasePath); + if (!errorMessage.isEmpty()) { + qCritical("Writing the database failed %s.", qPrintable(errorMessage)); + return EXIT_FAILURE; + } + + outputTextStream << "Successfully added entry " << entry->title() << "." << endl; + return EXIT_SUCCESS; +} diff --git a/src/cli/Add.h b/src/cli/Add.h new file mode 100644 index 000000000..14356c418 --- /dev/null +++ b/src/cli/Add.h @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2017 KeePassXC Team + * + * 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 . + */ + +#ifndef KEEPASSXC_ADD_H +#define KEEPASSXC_ADD_H + +#include "Command.h" + +class Add : public Command +{ +public: + Add(); + ~Add(); + int execute(QStringList arguments); +}; + +#endif // KEEPASSXC_ADD_H diff --git a/src/cli/CMakeLists.txt b/src/cli/CMakeLists.txt index 43ed06be6..1ee232a48 100644 --- a/src/cli/CMakeLists.txt +++ b/src/cli/CMakeLists.txt @@ -14,10 +14,14 @@ # along with this program. If not, see . set(cli_SOURCES + Add.cpp + Add.h Clip.cpp Clip.h Command.cpp Command.h + Edit.cpp + Edit.h EntropyMeter.cpp EntropyMeter.h Extract.cpp @@ -28,6 +32,8 @@ set(cli_SOURCES Locate.h Merge.cpp Merge.h + Remove.cpp + Remove.h Show.cpp Show.h) diff --git a/src/cli/Command.cpp b/src/cli/Command.cpp index 721710776..c7eff6b08 100644 --- a/src/cli/Command.cpp +++ b/src/cli/Command.cpp @@ -22,12 +22,15 @@ #include "Command.h" +#include "Add.h" +#include "Edit.h" #include "Clip.h" #include "EntropyMeter.h" #include "Extract.h" #include "List.h" #include "Locate.h" #include "Merge.h" +#include "Remove.h" #include "Show.h" QMap commands; @@ -56,12 +59,15 @@ QString Command::getDescriptionLine() void populateCommands() { if (commands.isEmpty()) { + commands.insert(QString("add"), new Add()); commands.insert(QString("clip"), new Clip()); + commands.insert(QString("edit"), new Edit()); commands.insert(QString("entropy-meter"), new EntropyMeter()); commands.insert(QString("extract"), new Extract()); commands.insert(QString("locate"), new Locate()); commands.insert(QString("ls"), new List()); commands.insert(QString("merge"), new Merge()); + commands.insert(QString("rm"), new Remove()); commands.insert(QString("show"), new Show()); } } diff --git a/src/cli/Edit.cpp b/src/cli/Edit.cpp new file mode 100644 index 000000000..25d2fd456 --- /dev/null +++ b/src/cli/Edit.cpp @@ -0,0 +1,168 @@ +/* + * Copyright (C) 2017 KeePassXC Team + * + * 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 . + */ + +#include +#include + +#include "Edit.h" + +#include +#include + +#include "cli/Utils.h" +#include "core/Database.h" +#include "core/Entry.h" +#include "core/Group.h" +#include "core/PasswordGenerator.h" + +Edit::Edit() +{ + this->name = QString("edit"); + this->description = QObject::tr("Edit an entry."); +} + +Edit::~Edit() +{ +} + +int Edit::execute(QStringList arguments) +{ + + QTextStream inputTextStream(stdin, QIODevice::ReadOnly); + QTextStream outputTextStream(stdout, QIODevice::WriteOnly); + + QCommandLineParser parser; + parser.setApplicationDescription(this->description); + parser.addPositionalArgument("database", QObject::tr("Path of the database.")); + + QCommandLineOption keyFile(QStringList() << "k" + << "key-file", + QObject::tr("Key file of the database."), + QObject::tr("path")); + parser.addOption(keyFile); + + QCommandLineOption username(QStringList() << "u" + << "username", + QObject::tr("Username for the entry."), + QObject::tr("username")); + parser.addOption(username); + + QCommandLineOption url(QStringList() << "url", QObject::tr("URL for the entry."), QObject::tr("URL")); + parser.addOption(url); + + QCommandLineOption title(QStringList() << "t" + << "title", + QObject::tr("Title for the entry."), + QObject::tr("title")); + parser.addOption(title); + + QCommandLineOption prompt(QStringList() << "p" + << "password-prompt", + QObject::tr("Prompt for the entry's password.")); + parser.addOption(prompt); + + QCommandLineOption generate(QStringList() << "g" + << "generate", + QObject::tr("Generate a password for the entry.")); + parser.addOption(generate); + + QCommandLineOption length(QStringList() << "l" + << "password-length", + QObject::tr("Length for the generated password."), + QObject::tr("length")); + parser.addOption(length); + + parser.addPositionalArgument("entry", QObject::tr("Path of the entry to edit.")); + parser.process(arguments); + + const QStringList args = parser.positionalArguments(); + if (args.size() != 2) { + outputTextStream << parser.helpText().replace("keepassxc-cli", "keepassxc-cli edit"); + return EXIT_FAILURE; + } + + QString databasePath = args.at(0); + QString entryPath = args.at(1); + + Database* db = Database::unlockFromStdin(databasePath, parser.value(keyFile)); + if (db == nullptr) { + return EXIT_FAILURE; + } + + QString passwordLength = parser.value(length); + if (!passwordLength.isEmpty() && !passwordLength.toInt()) { + qCritical("Invalid value for password length %s.", qPrintable(passwordLength)); + return EXIT_FAILURE; + } + + Entry* entry = db->rootGroup()->findEntryByPath(entryPath); + if (!entry) { + qCritical("Could not find entry with path %s.", qPrintable(entryPath)); + return EXIT_FAILURE; + } + + if (parser.value("username").isEmpty() && parser.value("url").isEmpty() && parser.value("title").isEmpty() && + !parser.isSet(prompt) && !parser.isSet(generate)) { + qCritical("Not changing any field for entry %s.", qPrintable(entryPath)); + return EXIT_FAILURE; + } + + entry->beginUpdate(); + + if (!parser.value("title").isEmpty()) { + entry->setTitle(parser.value("title")); + } + + if (!parser.value("username").isEmpty()) { + entry->setUsername(parser.value("username")); + } + + if (!parser.value("url").isEmpty()) { + entry->setUrl(parser.value("url")); + } + + if (parser.isSet(prompt)) { + outputTextStream << "Enter new password for entry: "; + outputTextStream.flush(); + QString password = Utils::getPassword(); + entry->setPassword(password); + } else if (parser.isSet(generate)) { + PasswordGenerator passwordGenerator; + + if (passwordLength.isEmpty()) { + passwordGenerator.setLength(PasswordGenerator::DefaultLength); + } else { + passwordGenerator.setLength(passwordLength.toInt()); + } + + passwordGenerator.setCharClasses(PasswordGenerator::LowerLetters | PasswordGenerator::UpperLetters | + PasswordGenerator::Numbers); + QString password = passwordGenerator.generatePassword(); + entry->setPassword(password); + } + + entry->endUpdate(); + + QString errorMessage = db->saveToFile(databasePath); + if (!errorMessage.isEmpty()) { + qCritical("Writing the database failed %s.", qPrintable(errorMessage)); + return EXIT_FAILURE; + } + + outputTextStream << "Successfully edited entry " << entry->title() << "." << endl; + return EXIT_SUCCESS; +} diff --git a/src/cli/Edit.h b/src/cli/Edit.h new file mode 100644 index 000000000..e52069ff0 --- /dev/null +++ b/src/cli/Edit.h @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2017 KeePassXC Team + * + * 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 . + */ + +#ifndef KEEPASSXC_EDIT_H +#define KEEPASSXC_EDIT_H + +#include "Command.h" + +class Edit : public Command +{ +public: + Edit(); + ~Edit(); + int execute(QStringList arguments); +}; + +#endif // KEEPASSXC_EDIT_H diff --git a/src/cli/Remove.cpp b/src/cli/Remove.cpp new file mode 100644 index 000000000..6abb68f1c --- /dev/null +++ b/src/cli/Remove.cpp @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2017 KeePassXC Team + * + * 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 . + */ + +#include +#include + +#include "Remove.h" + +#include +#include +#include +#include + +#include "cli/Utils.h" +#include "core/Database.h" +#include "core/Entry.h" +#include "core/Group.h" +#include "core/Metadata.h" +#include "core/Tools.h" + +Remove::Remove() +{ + this->name = QString("rm"); + this->description = QString("Remove an entry from the database."); +} + +Remove::~Remove() +{ +} + +int Remove::execute(QStringList arguments) +{ + QTextStream outputTextStream(stdout, QIODevice::WriteOnly); + + QCommandLineParser parser; + parser.setApplicationDescription(QCoreApplication::translate("main", "Remove an entry from the database.")); + parser.addPositionalArgument("database", QCoreApplication::translate("main", "Path of the database.")); + QCommandLineOption keyFile(QStringList() << "k" + << "key-file", + QObject::tr("Key file of the database."), + QObject::tr("path")); + parser.addOption(keyFile); + parser.addPositionalArgument("entry", QCoreApplication::translate("main", "Path of the entry to remove.")); + parser.process(arguments); + + const QStringList args = parser.positionalArguments(); + if (args.size() != 2) { + outputTextStream << parser.helpText().replace("keepassxc-cli", "keepassxc-cli rm"); + return EXIT_FAILURE; + } + + Database* db = Database::unlockFromStdin(args.at(0), parser.value(keyFile)); + if (db == nullptr) { + return EXIT_FAILURE; + } + + return this->removeEntry(db, args.at(0), args.at(1)); +} + +int Remove::removeEntry(Database* database, QString databasePath, QString entryPath) +{ + + QTextStream outputTextStream(stdout, QIODevice::WriteOnly); + Entry* entry = database->rootGroup()->findEntryByPath(entryPath); + if (!entry) { + qCritical("Entry %s not found.", qPrintable(entryPath)); + return EXIT_FAILURE; + } + + QString entryTitle = entry->title(); + bool recycled = true; + if (Tools::hasChild(database->metadata()->recycleBin(), entry) || !database->metadata()->recycleBinEnabled()) { + delete entry; + recycled = false; + } else { + database->recycleEntry(entry); + }; + + QString errorMessage = database->saveToFile(databasePath); + if (!errorMessage.isEmpty()) { + qCritical("Unable to save database to file : %s", qPrintable(errorMessage)); + return EXIT_FAILURE; + } + + if (recycled) { + outputTextStream << "Successfully recycled entry " << entryTitle << "." << endl; + } else { + outputTextStream << "Successfully deleted entry " << entryTitle << "." << endl; + } + + return EXIT_SUCCESS; +} diff --git a/src/cli/Remove.h b/src/cli/Remove.h new file mode 100644 index 000000000..2440c201c --- /dev/null +++ b/src/cli/Remove.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2017 KeePassXC Team + * + * 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 . + */ + +#ifndef KEEPASSXC_REMOVE_H +#define KEEPASSXC_REMOVE_H + +#include "Command.h" + +#include "core/Database.h" + +class Remove : public Command +{ +public: + Remove(); + ~Remove(); + int execute(QStringList arguments); + int removeEntry(Database* database, QString databasePath, QString entryPath); +}; + +#endif // KEEPASSXC_REMOVE_H diff --git a/src/cli/keepassxc-cli.1 b/src/cli/keepassxc-cli.1 index b88215631..940b3d4cf 100644 --- a/src/cli/keepassxc-cli.1 +++ b/src/cli/keepassxc-cli.1 @@ -9,13 +9,19 @@ keepassxc-cli \- command line interface for the \fBKeePassXC\fP password manager .I command .SH DESCRIPTION -\fBkeepassxc-cli\fP is the command line interface for the \fBKeePassXC\fP password manager. It provides the ability of listing the entries of a database, displaying the contents of an entry and many more, directly from the command line. +\fBkeepassxc-cli\fP is the command line interface for the \fBKeePassXC\fP password manager. It provides the ability to query and modify the entries of a KeePass database, directly from the command line. .SH COMMANDS +.IP "add [options] " +Adds a new entry to a database. A password can be generated (\fI-g\fP option), or a prompt can be displayed to input the password (\fI-p\fP option). + .IP "clip [options] [timeout]" Copies the password of a database entry to the clipboard. If multiple entries with the same name exist in different groups, only the password for the first one is going to be copied. For copying the password of an entry in a specific group, the group path to the entry should be specified as well, instead of just the name. Optionally, a timeout in seconds can be specified to automatically clear the clipboard. +.IP "edit [options] " +Edits a database entry. A password can be generated (\fI-g\fP option), or a prompt can be displayed to input the password (\fI-p\fP option). + .IP "entropy-meter [-a pwd1 pwd2 ...]" Calculates the entropy of a single, or multiple passwords specified using the \fI-a\fP option. If no passwords are specified, the program will run in interactive mode and prompt the user to enter a password. @@ -31,26 +37,59 @@ Lists the contents of a group in a database. If no group is specified, it will d .IP "merge [options] " Merges two databases together. The first database file is going to be replaced by the result of the merge, for that reason it is advisable to keep a backup of the two database files before attempting a merge. In the case that both databases make use of the same credentials, the \fI--same-credentials\fP or \fI-s\fP option can be used. +.IP "rm [options] " +Removes an entry from a database. If the database has a recycle bin, the entry will be moved there. If the entry is already in the recycle bin, it will be removed permanently. + .IP "show [options] " Shows the title, username, password, URL and notes of a database entry. Regarding the occurrence of multiple entries with the same name in different groups, everything stated in the \fIclip\fP command section also applies here. .SH OPTIONS +.SS "General options" + .IP "-k, --key-file " Specifies a path to a key file for unlocking the database. In a merge operation this option is used to specify the key file path for the first database. -.IP "-f, --key-file-from " -Specifies a path to a key file for the second database in a merge operation. - -.IP "-s, --same-credentials" -Tells the program to use the same credentials for unlocking both of the database files in a merge operation. - .IP "-h, --help" Displays help information. .IP "-v, --version" Shows the program version. + +.SS "Merge options" + +.IP "-f, --key-file-from " +Path of the key file for the second database. + +.IP "-s, --same-credentials" +Use the same credentials for unlocking both database. + + +.SS "Add and edit options" + +.IP "-u, --username " +Specify the username of the entry. + +.IP "--url " +Specify the URL of the entry. + +.IP "-p, --password-prompt" +Use a password prompt for the entry's password. + +.IP "-g, --generate" +Generate a new password for the entry. + +.IP "-l, --password-length" +Specify the length of the password to generate. + + +.SS "Edit options" + +.IP "-t, --title " +Specify the title of the entry. + + .SH REPORTING BUGS Bugs and feature requests can be reported on GitHub at https://github.com/keepassxreboot/keepassxc/issues. diff --git a/src/core/Group.cpp b/src/core/Group.cpp index 92e0269e5..6da1f2cee 100644 --- a/src/core/Group.cpp +++ b/src/core/Group.cpp @@ -949,3 +949,32 @@ QStringList Group::locate(QString locateTerm, QString currentPath) 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; + +} diff --git a/src/core/Group.h b/src/core/Group.h index 73b791590..27400703e 100644 --- a/src/core/Group.h +++ b/src/core/Group.h @@ -85,6 +85,7 @@ public: Entry* findEntryByPath(QString entryPath, QString basePath = QString("")); Group* findGroupByPath(QString groupPath, QString basePath = QString("/")); QStringList locate(QString locateTerm, QString currentPath = QString("/")); + Entry* addEntryWithPath(QString entryPath); void setUuid(const Uuid& uuid); void setName(const QString& name); void setNotes(const QString& notes); diff --git a/src/core/PasswordGenerator.h b/src/core/PasswordGenerator.h index 5a5c7a3f6..98bb58b6a 100644 --- a/src/core/PasswordGenerator.h +++ b/src/core/PasswordGenerator.h @@ -58,6 +58,8 @@ public: QString generatePassword() const; int getbits() const; + static const int DefaultLength = 16; + private: QVector<PasswordGroup> passwordGroups() const; int numCharClasses() const; diff --git a/src/gui/PasswordGeneratorWidget.cpp b/src/gui/PasswordGeneratorWidget.cpp index 3753071d1..d77634415 100644 --- a/src/gui/PasswordGeneratorWidget.cpp +++ b/src/gui/PasswordGeneratorWidget.cpp @@ -98,7 +98,7 @@ void PasswordGeneratorWidget::loadSettings() m_ui->checkBoxExtASCII->setChecked(config()->get("generator/EASCII", false).toBool()); m_ui->checkBoxExcludeAlike->setChecked(config()->get("generator/ExcludeAlike", true).toBool()); m_ui->checkBoxEnsureEvery->setChecked(config()->get("generator/EnsureEvery", true).toBool()); - m_ui->spinBoxLength->setValue(config()->get("generator/Length", 16).toInt()); + m_ui->spinBoxLength->setValue(config()->get("generator/Length", PasswordGenerator::DefaultLength).toInt()); // Diceware config m_ui->spinBoxWordCount->setValue(config()->get("generator/WordCount", 6).toInt()); diff --git a/src/http/HttpPasswordGeneratorWidget.cpp b/src/http/HttpPasswordGeneratorWidget.cpp index 55e5b08fc..b722a85f3 100644 --- a/src/http/HttpPasswordGeneratorWidget.cpp +++ b/src/http/HttpPasswordGeneratorWidget.cpp @@ -56,7 +56,7 @@ void HttpPasswordGeneratorWidget::loadSettings() m_ui->checkBoxExcludeAlike->setChecked(config()->get("Http/generator/ExcludeAlike", true).toBool()); m_ui->checkBoxEnsureEvery->setChecked(config()->get("Http/generator/EnsureEvery", true).toBool()); - m_ui->spinBoxLength->setValue(config()->get("Http/generator/Length", 16).toInt()); + m_ui->spinBoxLength->setValue(config()->get("Http/generator/Length", PasswordGenerator::DefaultLength).toInt()); } void HttpPasswordGeneratorWidget::saveSettings() diff --git a/tests/TestGroup.cpp b/tests/TestGroup.cpp index efdcb32e6..5a809670f 100644 --- a/tests/TestGroup.cpp +++ b/tests/TestGroup.cpp @@ -698,3 +698,51 @@ void TestGroup::testLocate() delete db; } + +void TestGroup::testAddEntryWithPath() +{ + Database* db = new Database(); + + Group* group1 = new Group(); + group1->setName("group1"); + group1->setParent(db->rootGroup()); + + Group* group2 = new Group(); + group2->setName("group2"); + group2->setParent(group1); + + Entry* entry = db->rootGroup()->addEntryWithPath("entry1"); + QVERIFY(entry != nullptr); + QVERIFY(!entry->uuid().isNull()); + + entry = db->rootGroup()->addEntryWithPath("entry1"); + QVERIFY(entry == nullptr); + + entry = db->rootGroup()->addEntryWithPath("/entry1"); + QVERIFY(entry == nullptr); + + entry = db->rootGroup()->addEntryWithPath("entry2"); + QVERIFY(entry != nullptr); + QVERIFY(entry->title() == "entry2"); + QVERIFY(!entry->uuid().isNull()); + + entry = db->rootGroup()->addEntryWithPath("/entry3"); + QVERIFY(entry != nullptr); + QVERIFY(entry->title() == "entry3"); + QVERIFY(!entry->uuid().isNull()); + + entry = db->rootGroup()->addEntryWithPath("/group1/entry4"); + QVERIFY(entry != nullptr); + QVERIFY(entry->title() == "entry4"); + QVERIFY(!entry->uuid().isNull()); + + entry = db->rootGroup()->addEntryWithPath("/group1/group2/entry5"); + QVERIFY(entry != nullptr); + QVERIFY(entry->title() == "entry5"); + QVERIFY(!entry->uuid().isNull()); + + entry = db->rootGroup()->addEntryWithPath("/group1/invalid_group/entry6"); + QVERIFY(entry == nullptr); + + delete db; +} diff --git a/tests/TestGroup.h b/tests/TestGroup.h index fabe860af..11abfe129 100644 --- a/tests/TestGroup.h +++ b/tests/TestGroup.h @@ -39,6 +39,7 @@ private slots: void testFindGroupByPath(); void testPrint(); void testLocate(); + void testAddEntryWithPath(); }; #endif // KEEPASSX_TESTGROUP_H