keepassxc

Форк
0
/
keepassxc-cli.cpp 
257 строк · 6.9 Кб
1
/*
2
 *  Copyright (C) 2020 KeePassXC Team <team@keepassxc.org>
3
 *
4
 *  This program is free software: you can redistribute it and/or modify
5
 *  it under the terms of the GNU General Public License as published by
6
 *  the Free Software Foundation, either version 2 or (at your option)
7
 *  version 3 of the License.
8
 *
9
 *  This program is distributed in the hope that it will be useful,
10
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
 *  GNU General Public License for more details.
13
 *
14
 *  You should have received a copy of the GNU General Public License
15
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
16
 */
17

18
#include <QCommandLineParser>
19
#include <QFileInfo>
20

21
#include "Command.h"
22
#include "Open.h"
23
#include "TextStream.h"
24
#include "Utils.h"
25
#include "config-keepassx.h"
26
#include "core/Bootstrap.h"
27
#include "core/Config.h"
28
#include "core/Metadata.h"
29
#include "core/Tools.h"
30
#include "crypto/Crypto.h"
31

32
#if defined(WITH_ASAN) && defined(WITH_LSAN)
33
#include <sanitizer/lsan_interface.h>
34
#endif
35

36
#if defined(USE_READLINE)
37
#include <readline/history.h>
38
#include <readline/readline.h>
39
#endif
40

41
class LineReader
42
{
43
public:
44
    virtual ~LineReader() = default;
45
    virtual QString readLine(QString prompt) = 0;
46
    virtual bool isFinished() = 0;
47
};
48

49
class SimpleLineReader : public LineReader
50
{
51
public:
52
    SimpleLineReader()
53
        : inStream(stdin, QIODevice::ReadOnly)
54
        , outStream(stdout, QIODevice::WriteOnly)
55
        , finished(false)
56
    {
57
    }
58

59
    QString readLine(QString prompt) override
60
    {
61
        outStream << prompt;
62
        outStream.flush();
63
        QString result = inStream.readLine();
64
        if (result.isNull()) {
65
            finished = true;
66
        }
67
        return result;
68
    }
69

70
    bool isFinished() override
71
    {
72
        return finished;
73
    }
74

75
private:
76
    TextStream inStream;
77
    TextStream outStream;
78
    bool finished;
79
};
80

81
#if defined(USE_READLINE)
82
class ReadlineLineReader : public LineReader
83
{
84
public:
85
    ReadlineLineReader()
86
        : finished(false)
87
    {
88
    }
89

90
    QString readLine(QString prompt) override
91
    {
92
        char* result = readline(prompt.toStdString().c_str());
93
        if (!result) {
94
            finished = true;
95
            return {};
96
        }
97
        add_history(result);
98
        QString qstr(result);
99
        free(result);
100
        return qstr;
101
    }
102

103
    bool isFinished() override
104
    {
105
        return finished;
106
    }
107

108
private:
109
    bool finished;
110
};
111
#endif
112

113
int enterInteractiveMode(const QStringList& arguments)
114
{
115
    auto& err = Utils::STDERR;
116
    // Replace command list with interactive version
117
    Commands::setupCommands(true);
118

119
    Open openCmd;
120
    QStringList openArgs(arguments);
121
    openArgs.removeFirst();
122
    if (openCmd.execute(openArgs) != EXIT_SUCCESS) {
123
        return EXIT_FAILURE;
124
    };
125

126
    QScopedPointer<LineReader> reader;
127
#if defined(USE_READLINE)
128
    reader.reset(new ReadlineLineReader());
129
#else
130
    reader.reset(new SimpleLineReader());
131
#endif
132

133
    QSharedPointer<Database> currentDatabase(openCmd.currentDatabase);
134

135
    QString command;
136
    while (true) {
137
        QString prompt;
138
        if (currentDatabase) {
139
            prompt += currentDatabase->metadata()->name();
140
            if (prompt.isEmpty()) {
141
                prompt += QFileInfo(currentDatabase->filePath()).fileName();
142
            }
143
        }
144
        prompt += "> ";
145
        command = reader->readLine(prompt);
146
        if (reader->isFinished()) {
147
            break;
148
        }
149

150
        QStringList args = Utils::splitCommandString(command);
151
        if (args.empty()) {
152
            continue;
153
        }
154

155
        auto cmd = Commands::getCommand(args[0]);
156
        if (!cmd) {
157
            err << QObject::tr("Unknown command %1").arg(args[0]) << endl;
158
            continue;
159
        } else if (cmd->name == "quit" || cmd->name == "exit") {
160
            break;
161
        }
162

163
        cmd->currentDatabase.swap(currentDatabase);
164
        cmd->execute(args);
165
        currentDatabase.swap(cmd->currentDatabase);
166
    }
167

168
    if (currentDatabase) {
169
        currentDatabase->releaseData();
170
    }
171

172
    return EXIT_SUCCESS;
173
}
174

175
int main(int argc, char** argv)
176
{
177
    if (!Crypto::init()) {
178
        qWarning("Fatal error while testing the cryptographic functions:\n%s", qPrintable(Crypto::errorString()));
179
        return EXIT_FAILURE;
180
    }
181

182
    QCoreApplication app(argc, argv);
183
    QCoreApplication::setApplicationVersion(KEEPASSXC_VERSION);
184

185
    Bootstrap::bootstrap(config()->get(Config::GUI_Language).toString());
186
    Utils::setDefaultTextStreams();
187
    Commands::setupCommands(false);
188

189
    auto& out = Utils::STDOUT;
190
    auto& err = Utils::STDERR;
191

192
    QStringList arguments;
193
    for (int i = 0; i < argc; ++i) {
194
        arguments << QString(argv[i]);
195
    }
196
    QCommandLineParser parser;
197

198
    QString description("KeePassXC command line interface.");
199
    description = description.append(QObject::tr("\n\nAvailable commands:\n"));
200
    for (auto& command : Commands::getCommands()) {
201
        description = description.append(command->getDescriptionLine());
202
    }
203
    parser.setApplicationDescription(description);
204

205
    parser.addPositionalArgument("command", QObject::tr("Name of the command to execute."));
206

207
    QCommandLineOption debugInfoOption(QStringList() << "debug-info", QObject::tr("Displays debugging information."));
208
    parser.addOption(debugInfoOption);
209
    parser.addHelpOption();
210
    parser.addVersionOption();
211
    // TODO : use the setOptionsAfterPositionalArgumentsMode (Qt 5.6) function
212
    // when available. Until then, options passed to sub-commands won't be
213
    // recognized by this parser.
214
    parser.parse(arguments);
215

216
    if (parser.positionalArguments().empty()) {
217
        if (parser.isSet("version")) {
218
            // Switch to parser.showVersion() when available (QT 5.4).
219
            out << KEEPASSXC_VERSION << endl;
220
            return EXIT_SUCCESS;
221
        } else if (parser.isSet(debugInfoOption)) {
222
            QString debugInfo = Tools::debugInfo().append("\n").append(Crypto::debugInfo());
223
            out << debugInfo << endl;
224
            return EXIT_SUCCESS;
225
        }
226
        // showHelp exits the application immediately.
227
        parser.showHelp();
228
    }
229

230
    QString commandName = parser.positionalArguments().at(0);
231
    if (commandName == "open") {
232
        return enterInteractiveMode(arguments);
233
    }
234

235
    auto command = Commands::getCommand(commandName);
236
    if (!command) {
237
        err << QObject::tr("Invalid command %1.").arg(commandName) << endl;
238
        err << parser.helpText();
239
        return EXIT_FAILURE;
240
    }
241

242
    // Removing the first argument (keepassxc).
243
    arguments.removeFirst();
244
    int exitCode = command->execute(arguments);
245

246
    if (command->currentDatabase) {
247
        command->currentDatabase.reset();
248
    }
249

250
#if defined(WITH_ASAN) && defined(WITH_LSAN)
251
    // do leak check here to prevent massive tail of end-of-process leak errors from third-party libraries
252
    __lsan_do_leak_check();
253
    __lsan_disable();
254
#endif
255

256
    return exitCode;
257
}
258

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.