mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2024-10-01 01:26:01 -04:00
CLI : basic entry manipulation commands. (#919)
* CLI : basic entry manipulation commands. * Code review.
This commit is contained in:
parent
1220b7d501
commit
6e1fd0694f
150
src/cli/Add.cpp
Normal file
150
src/cli/Add.cpp
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
|
||||||
|
*
|
||||||
|
* 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 <cstdlib>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "Add.h"
|
||||||
|
|
||||||
|
#include <QCommandLineParser>
|
||||||
|
#include <QTextStream>
|
||||||
|
|
||||||
|
#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;
|
||||||
|
}
|
31
src/cli/Add.h
Normal file
31
src/cli/Add.h
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
|
||||||
|
*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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
|
@ -14,10 +14,14 @@
|
|||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
set(cli_SOURCES
|
set(cli_SOURCES
|
||||||
|
Add.cpp
|
||||||
|
Add.h
|
||||||
Clip.cpp
|
Clip.cpp
|
||||||
Clip.h
|
Clip.h
|
||||||
Command.cpp
|
Command.cpp
|
||||||
Command.h
|
Command.h
|
||||||
|
Edit.cpp
|
||||||
|
Edit.h
|
||||||
EntropyMeter.cpp
|
EntropyMeter.cpp
|
||||||
EntropyMeter.h
|
EntropyMeter.h
|
||||||
Extract.cpp
|
Extract.cpp
|
||||||
@ -28,6 +32,8 @@ set(cli_SOURCES
|
|||||||
Locate.h
|
Locate.h
|
||||||
Merge.cpp
|
Merge.cpp
|
||||||
Merge.h
|
Merge.h
|
||||||
|
Remove.cpp
|
||||||
|
Remove.h
|
||||||
Show.cpp
|
Show.cpp
|
||||||
Show.h)
|
Show.h)
|
||||||
|
|
||||||
|
@ -22,12 +22,15 @@
|
|||||||
|
|
||||||
#include "Command.h"
|
#include "Command.h"
|
||||||
|
|
||||||
|
#include "Add.h"
|
||||||
|
#include "Edit.h"
|
||||||
#include "Clip.h"
|
#include "Clip.h"
|
||||||
#include "EntropyMeter.h"
|
#include "EntropyMeter.h"
|
||||||
#include "Extract.h"
|
#include "Extract.h"
|
||||||
#include "List.h"
|
#include "List.h"
|
||||||
#include "Locate.h"
|
#include "Locate.h"
|
||||||
#include "Merge.h"
|
#include "Merge.h"
|
||||||
|
#include "Remove.h"
|
||||||
#include "Show.h"
|
#include "Show.h"
|
||||||
|
|
||||||
QMap<QString, Command*> commands;
|
QMap<QString, Command*> commands;
|
||||||
@ -56,12 +59,15 @@ QString Command::getDescriptionLine()
|
|||||||
void populateCommands()
|
void populateCommands()
|
||||||
{
|
{
|
||||||
if (commands.isEmpty()) {
|
if (commands.isEmpty()) {
|
||||||
|
commands.insert(QString("add"), new Add());
|
||||||
commands.insert(QString("clip"), new Clip());
|
commands.insert(QString("clip"), new Clip());
|
||||||
|
commands.insert(QString("edit"), new Edit());
|
||||||
commands.insert(QString("entropy-meter"), new EntropyMeter());
|
commands.insert(QString("entropy-meter"), new EntropyMeter());
|
||||||
commands.insert(QString("extract"), new Extract());
|
commands.insert(QString("extract"), new Extract());
|
||||||
commands.insert(QString("locate"), new Locate());
|
commands.insert(QString("locate"), new Locate());
|
||||||
commands.insert(QString("ls"), new List());
|
commands.insert(QString("ls"), new List());
|
||||||
commands.insert(QString("merge"), new Merge());
|
commands.insert(QString("merge"), new Merge());
|
||||||
|
commands.insert(QString("rm"), new Remove());
|
||||||
commands.insert(QString("show"), new Show());
|
commands.insert(QString("show"), new Show());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
168
src/cli/Edit.cpp
Normal file
168
src/cli/Edit.cpp
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
|
||||||
|
*
|
||||||
|
* 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 <cstdlib>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "Edit.h"
|
||||||
|
|
||||||
|
#include <QCommandLineParser>
|
||||||
|
#include <QTextStream>
|
||||||
|
|
||||||
|
#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;
|
||||||
|
}
|
31
src/cli/Edit.h
Normal file
31
src/cli/Edit.h
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
|
||||||
|
*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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
|
106
src/cli/Remove.cpp
Normal file
106
src/cli/Remove.cpp
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
|
||||||
|
*
|
||||||
|
* 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 <cstdlib>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "Remove.h"
|
||||||
|
|
||||||
|
#include <QCommandLineParser>
|
||||||
|
#include <QCoreApplication>
|
||||||
|
#include <QStringList>
|
||||||
|
#include <QTextStream>
|
||||||
|
|
||||||
|
#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;
|
||||||
|
}
|
34
src/cli/Remove.h
Normal file
34
src/cli/Remove.h
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
|
||||||
|
*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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
|
@ -9,13 +9,19 @@ keepassxc-cli \- command line interface for the \fBKeePassXC\fP password manager
|
|||||||
.I command
|
.I command
|
||||||
|
|
||||||
.SH DESCRIPTION
|
.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
|
.SH COMMANDS
|
||||||
|
|
||||||
|
.IP "add [options] <database> <entry>"
|
||||||
|
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] <database> <entry> [timeout]"
|
.IP "clip [options] <database> <entry> [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.
|
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] <database> <entry>"
|
||||||
|
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 ...]"
|
.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.
|
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] <database1> <database2>"
|
.IP "merge [options] <database1> <database2>"
|
||||||
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.
|
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] <database> <entry>"
|
||||||
|
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] <database> <entry>"
|
.IP "show [options] <database> <entry>"
|
||||||
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.
|
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
|
.SH OPTIONS
|
||||||
|
|
||||||
|
.SS "General options"
|
||||||
|
|
||||||
.IP "-k, --key-file <path>"
|
.IP "-k, --key-file <path>"
|
||||||
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.
|
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 <path>"
|
|
||||||
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"
|
.IP "-h, --help"
|
||||||
Displays help information.
|
Displays help information.
|
||||||
|
|
||||||
.IP "-v, --version"
|
.IP "-v, --version"
|
||||||
Shows the program version.
|
Shows the program version.
|
||||||
|
|
||||||
|
|
||||||
|
.SS "Merge options"
|
||||||
|
|
||||||
|
.IP "-f, --key-file-from <path>"
|
||||||
|
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 <username>"
|
||||||
|
Specify the username of the entry.
|
||||||
|
|
||||||
|
.IP "--url <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 <title>"
|
||||||
|
Specify the title of the entry.
|
||||||
|
|
||||||
|
|
||||||
.SH REPORTING BUGS
|
.SH REPORTING BUGS
|
||||||
Bugs and feature requests can be reported on GitHub at https://github.com/keepassxreboot/keepassxc/issues.
|
Bugs and feature requests can be reported on GitHub at https://github.com/keepassxreboot/keepassxc/issues.
|
||||||
|
|
||||||
|
@ -949,3 +949,32 @@ QStringList Group::locate(QString locateTerm, QString currentPath)
|
|||||||
|
|
||||||
return response;
|
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;
|
||||||
|
|
||||||
|
}
|
||||||
|
@ -85,6 +85,7 @@ public:
|
|||||||
Entry* findEntryByPath(QString entryPath, QString basePath = QString(""));
|
Entry* findEntryByPath(QString entryPath, QString basePath = QString(""));
|
||||||
Group* findGroupByPath(QString groupPath, QString basePath = QString("/"));
|
Group* findGroupByPath(QString groupPath, QString basePath = QString("/"));
|
||||||
QStringList locate(QString locateTerm, QString currentPath = QString("/"));
|
QStringList locate(QString locateTerm, QString currentPath = QString("/"));
|
||||||
|
Entry* addEntryWithPath(QString entryPath);
|
||||||
void setUuid(const Uuid& uuid);
|
void setUuid(const Uuid& uuid);
|
||||||
void setName(const QString& name);
|
void setName(const QString& name);
|
||||||
void setNotes(const QString& notes);
|
void setNotes(const QString& notes);
|
||||||
|
@ -58,6 +58,8 @@ public:
|
|||||||
QString generatePassword() const;
|
QString generatePassword() const;
|
||||||
int getbits() const;
|
int getbits() const;
|
||||||
|
|
||||||
|
static const int DefaultLength = 16;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QVector<PasswordGroup> passwordGroups() const;
|
QVector<PasswordGroup> passwordGroups() const;
|
||||||
int numCharClasses() const;
|
int numCharClasses() const;
|
||||||
|
@ -98,7 +98,7 @@ void PasswordGeneratorWidget::loadSettings()
|
|||||||
m_ui->checkBoxExtASCII->setChecked(config()->get("generator/EASCII", false).toBool());
|
m_ui->checkBoxExtASCII->setChecked(config()->get("generator/EASCII", false).toBool());
|
||||||
m_ui->checkBoxExcludeAlike->setChecked(config()->get("generator/ExcludeAlike", true).toBool());
|
m_ui->checkBoxExcludeAlike->setChecked(config()->get("generator/ExcludeAlike", true).toBool());
|
||||||
m_ui->checkBoxEnsureEvery->setChecked(config()->get("generator/EnsureEvery", 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
|
// Diceware config
|
||||||
m_ui->spinBoxWordCount->setValue(config()->get("generator/WordCount", 6).toInt());
|
m_ui->spinBoxWordCount->setValue(config()->get("generator/WordCount", 6).toInt());
|
||||||
|
@ -56,7 +56,7 @@ void HttpPasswordGeneratorWidget::loadSettings()
|
|||||||
m_ui->checkBoxExcludeAlike->setChecked(config()->get("Http/generator/ExcludeAlike", true).toBool());
|
m_ui->checkBoxExcludeAlike->setChecked(config()->get("Http/generator/ExcludeAlike", true).toBool());
|
||||||
m_ui->checkBoxEnsureEvery->setChecked(config()->get("Http/generator/EnsureEvery", 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()
|
void HttpPasswordGeneratorWidget::saveSettings()
|
||||||
|
@ -698,3 +698,51 @@ void TestGroup::testLocate()
|
|||||||
|
|
||||||
delete db;
|
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;
|
||||||
|
}
|
||||||
|
@ -39,6 +39,7 @@ private slots:
|
|||||||
void testFindGroupByPath();
|
void testFindGroupByPath();
|
||||||
void testPrint();
|
void testPrint();
|
||||||
void testLocate();
|
void testLocate();
|
||||||
|
void testAddEntryWithPath();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // KEEPASSX_TESTGROUP_H
|
#endif // KEEPASSX_TESTGROUP_H
|
||||||
|
Loading…
Reference in New Issue
Block a user