kwatchgnupgmainwin.cpp 9.78 KB
Newer Older
Laurent Montel's avatar
Port    
Laurent Montel committed
1
2
3
4
/*
    kwatchgnupgmainwin.cpp

    This file is part of Kleopatra, the KDE keymanager
5
6
7
    SPDX-FileCopyrightText: 2001, 2002, 2004 Klar �vdalens Datakonsult AB

    SPDX-License-Identifier: GPL-2.0-or-later
Laurent Montel's avatar
Port    
Laurent Montel committed
8
9
*/

Marc Mutz's avatar
Marc Mutz committed
10
11
#include <config-kleopatra.h>

Laurent Montel's avatar
Port    
Laurent Montel committed
12
13
#include "kwatchgnupgmainwin.h"
#include "kwatchgnupgconfig.h"
Laurent Montel's avatar
Laurent Montel committed
14
#include "kwatchgnupg.h"
Laurent Montel's avatar
Port    
Laurent Montel committed
15
16
#include "tray.h"

17
18
#include <QGpgME/Protocol>
#include <QGpgME/CryptoConfig>
Laurent Montel's avatar
Port    
Laurent Montel committed
19

20
#include <QTextEdit>
21

22
#include <KMessageBox>
Laurent Montel's avatar
Laurent Montel committed
23
#include <KLocalizedString>
Laurent Montel's avatar
Laurent Montel committed
24
#include <QApplication>
Laurent Montel's avatar
Laurent Montel committed
25
#include <QAction>
26
27
28
29
30
31
#include <KActionCollection>
#include <KStandardAction>
#include <KProcess>
#include <KConfig>
#include <KEditToolBar>
#include <KShortcutsDialog>
Laurent Montel's avatar
Laurent Montel committed
32
#include <QIcon>
Laurent Montel's avatar
Laurent Montel committed
33
#include <KConfigGroup>
34
#include <kxmlgui_version.h>
Laurent Montel's avatar
Port    
Laurent Montel committed
35

36
#include <QEventLoop>
Laurent Montel's avatar
Port    
Laurent Montel committed
37
#include <QTextStream>
38
#include <QDateTime>
39
#include <QFileDialog>
40
#include <KSharedConfig>
Laurent Montel's avatar
Port    
Laurent Montel committed
41

42
43
44
45
46
#include <gpgme++/gpgmepp_version.h>
#if GPGMEPP_VERSION >= 0x10F02 // 1.15.2
# define CRYPTOCONFIG_HAS_GROUPLESS_ENTRY_OVERLOAD
#endif

Laurent Montel's avatar
Laurent Montel committed
47
KWatchGnuPGMainWindow::KWatchGnuPGMainWindow(QWidget *parent)
Laurent Montel's avatar
Laurent Montel committed
48
    : KXmlGuiWindow(parent, Qt::Window), mConfig(nullptr)
Laurent Montel's avatar
Port    
Laurent Montel committed
49
{
Laurent Montel's avatar
Laurent Montel committed
50
51
    createActions();
    createGUI();
Laurent Montel's avatar
Port    
Laurent Montel committed
52

53
54
    mCentralWidget = new QTextEdit(this);
    mCentralWidget->setReadOnly(true);
55

Laurent Montel's avatar
Laurent Montel committed
56
    setCentralWidget(mCentralWidget);
Laurent Montel's avatar
Port    
Laurent Montel committed
57

Laurent Montel's avatar
Laurent Montel committed
58
59
60
    mWatcher = new KProcess;
    connect(mWatcher, SIGNAL(finished(int,QProcess::ExitStatus)),
            this, SLOT(slotWatcherExited(int,QProcess::ExitStatus)));
Laurent Montel's avatar
Laurent Montel committed
61

Laurent Montel's avatar
Laurent Montel committed
62
63
    connect(mWatcher, &QProcess::readyReadStandardOutput,
            this, &KWatchGnuPGMainWindow::slotReadStdout);
Laurent Montel's avatar
Port    
Laurent Montel committed
64

Laurent Montel's avatar
Laurent Montel committed
65
66
    slotReadConfig();
    mSysTray = new KWatchGnuPGTray(this);
Laurent Montel's avatar
Laurent Montel committed
67
68
    QAction *act = mSysTray->action(QStringLiteral("quit"));
    if (act) {
Laurent Montel's avatar
Laurent Montel committed
69
70
        connect(act, &QAction::triggered, this, &KWatchGnuPGMainWindow::slotQuit);
    }
71

Laurent Montel's avatar
Laurent Montel committed
72
    setAutoSaveSettings();
Laurent Montel's avatar
Port    
Laurent Montel committed
73
74
75
76
}

KWatchGnuPGMainWindow::~KWatchGnuPGMainWindow()
{
Laurent Montel's avatar
Laurent Montel committed
77
    delete mWatcher;
Laurent Montel's avatar
Port    
Laurent Montel committed
78
79
80
81
}

void KWatchGnuPGMainWindow::slotClear()
{
Laurent Montel's avatar
Laurent Montel committed
82
    mCentralWidget->clear();
83
    mCentralWidget->append(i18n("[%1] Log cleared", QDateTime::currentDateTime().toString(Qt::ISODate)));
Laurent Montel's avatar
Port    
Laurent Montel committed
84
85
86
87
}

void KWatchGnuPGMainWindow::createActions()
{
Laurent Montel's avatar
Laurent Montel committed
88
    QAction *action = actionCollection()->addAction(QStringLiteral("clear_log"));
Laurent Montel's avatar
Laurent Montel committed
89
    action->setIcon(QIcon::fromTheme(QStringLiteral("edit-clear-history")));
Laurent Montel's avatar
Laurent Montel committed
90
91
    action->setText(i18n("C&lear History"));
    connect(action, &QAction::triggered, this, &KWatchGnuPGMainWindow::slotClear);
92
    actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::CTRL | Qt::Key_L));
Laurent Montel's avatar
Laurent Montel committed
93
94
95
96
97
98
    (void)KStandardAction::saveAs(this, SLOT(slotSaveAs()), actionCollection());
    (void)KStandardAction::close(this, SLOT(close()), actionCollection());
    (void)KStandardAction::quit(this, SLOT(slotQuit()), actionCollection());
    (void)KStandardAction::preferences(this, SLOT(slotConfigure()), actionCollection());
    (void)KStandardAction::keyBindings(this, SLOT(configureShortcuts()), actionCollection());
    (void)KStandardAction::configureToolbars(this, SLOT(slotConfigureToolbars()), actionCollection());
Laurent Montel's avatar
Port    
Laurent Montel committed
99
100
101
102
}

void KWatchGnuPGMainWindow::configureShortcuts()
{
103
#if KXMLGUI_VERSION < QT_VERSION_CHECK(5,84,0)
Laurent Montel's avatar
Laurent Montel committed
104
    KShortcutsDialog::configure(actionCollection(), KShortcutsEditor::LetterShortcutsAllowed, this);
105
106
107
#else
    KShortcutsDialog::showDialog(actionCollection(),  KShortcutsEditor::LetterShortcutsAllowed, true /*isModal*/, this);
#endif
Laurent Montel's avatar
Port    
Laurent Montel committed
108
109
110
111
}

void KWatchGnuPGMainWindow::slotConfigureToolbars()
{
Laurent Montel's avatar
Laurent Montel committed
112
    KEditToolBar dlg(factory());
Laurent Montel's avatar
Port    
Laurent Montel committed
113
114
115
116
117
    dlg.exec();
}

void KWatchGnuPGMainWindow::startWatcher()
{
Laurent Montel's avatar
Laurent Montel committed
118
119
120
    disconnect(mWatcher, SIGNAL(finished(int,QProcess::ExitStatus)),
               this, SLOT(slotWatcherExited(int,QProcess::ExitStatus)));
    if (mWatcher->state() == QProcess::Running) {
121
        mWatcher->kill();
Laurent Montel's avatar
Laurent Montel committed
122
123
        while (mWatcher->state() == QProcess::Running) {
            qApp->processEvents(QEventLoop::ExcludeUserInputEvents);
124
        }
125
126
        mCentralWidget->append(i18n("[%1] Log stopped", QDateTime::currentDateTime().toString(Qt::ISODate)));
        mCentralWidget->ensureCursorVisible();
Laurent Montel's avatar
Laurent Montel committed
127
128
129
130
131
132
    }
    mWatcher->clearProgram();

    {
        const KConfigGroup config(KSharedConfig::openConfig(), "WatchGnuPG");
        *mWatcher << config.readEntry("Executable", WATCHGNUPGBINARY);
Laurent Montel's avatar
Laurent Montel committed
133
        *mWatcher << QStringLiteral("--force");
Laurent Montel's avatar
Laurent Montel committed
134
135
136
137
138
139
140
141
142
        *mWatcher << config.readEntry("Socket", WATCHGNUPGSOCKET);
    }

    mWatcher->setOutputChannelMode(KProcess::OnlyStdoutChannel);
    mWatcher->start();
    const bool ok = mWatcher->waitForStarted();
    if (!ok) {
        KMessageBox::sorry(this, i18n("The watchgnupg logging process could not be started.\nPlease install watchgnupg somewhere in your $PATH.\nThis log window is unable to display any useful information."));
    } else {
143
144
        mCentralWidget->append(i18n("[%1] Log started", QDateTime::currentDateTime().toString(Qt::ISODate)));
        mCentralWidget->ensureCursorVisible();
Laurent Montel's avatar
Laurent Montel committed
145
146
147
    }
    connect(mWatcher, SIGNAL(finished(int,QProcess::ExitStatus)),
            this, SLOT(slotWatcherExited(int,QProcess::ExitStatus)));
Laurent Montel's avatar
Port    
Laurent Montel committed
148
149
}

150
151
152
153
154
155
156
157
namespace
{
QGpgME::CryptoConfigEntry *getCryptoConfigEntry(const QGpgME::CryptoConfig *config, const QString &componentName, const char *entryName)
{
    // copied from utils/compat.cpp in libkleopatra
#ifdef CRYPTOCONFIG_HAS_GROUPLESS_ENTRY_OVERLOAD
    return config->entry(componentName, QString::fromLatin1(entryName));
#else
158
159
    using namespace QGpgME;
    const CryptoConfigComponent *const comp = config->component(componentName);
160
161
162
163
164
165
166
167
168
169
170
171
    const QStringList groupNames = comp->groupList();
    for (const auto &groupName : groupNames) {
        const CryptoConfigGroup *const group = comp ? comp->group(groupName) : nullptr;
        if (CryptoConfigEntry *const entry = group->entry(QString::fromLatin1(entryName))) {
            return entry;
        }
    }
    return nullptr;
#endif
}
}

Laurent Montel's avatar
Port    
Laurent Montel committed
172
173
void KWatchGnuPGMainWindow::setGnuPGConfig()
{
Laurent Montel's avatar
Laurent Montel committed
174
175
    QStringList logclients;
    // Get config object
176
    QGpgME::CryptoConfig *const cconfig = QGpgME::cryptoConfig();
Laurent Montel's avatar
Laurent Montel committed
177
178
179
180
181
182
    if (!cconfig) {
        return;
    }
    KConfigGroup config(KSharedConfig::openConfig(), "WatchGnuPG");
    const QStringList comps = cconfig->componentList();
    for (QStringList::const_iterator it = comps.constBegin(); it != comps.constEnd(); ++it) {
183
        const QGpgME::CryptoConfigComponent *const comp = cconfig->component(*it);
184
        Q_ASSERT(comp);
185
186
        {
            QGpgME::CryptoConfigEntry *const entry = getCryptoConfigEntry(cconfig, comp->name(), "log-file");
Laurent Montel's avatar
Laurent Montel committed
187
            if (entry) {
188
                entry->setStringValue(QLatin1String("socket://") + config.readEntry("Socket", WATCHGNUPGSOCKET));
Laurent Montel's avatar
Laurent Montel committed
189
                logclients << QStringLiteral("%1 (%2)").arg(*it, comp->description());
Laurent Montel's avatar
Laurent Montel committed
190
            }
191
192
193
        }
        {
            QGpgME::CryptoConfigEntry *const entry = getCryptoConfigEntry(cconfig, comp->name(), "debug-level");
Laurent Montel's avatar
Laurent Montel committed
194
195
196
            if (entry) {
                entry->setStringValue(config.readEntry("LogLevel", "basic"));
            }
197
        }
Laurent Montel's avatar
Laurent Montel committed
198
199
200
    }
    cconfig->sync(true);
    if (logclients.isEmpty()) {
Laurent Montel's avatar
Laurent Montel committed
201
        KMessageBox::sorry(nullptr, i18n("There are no components available that support logging."));
Laurent Montel's avatar
Laurent Montel committed
202
    }
Laurent Montel's avatar
Port    
Laurent Montel committed
203
204
}

Laurent Montel's avatar
Laurent Montel committed
205
void KWatchGnuPGMainWindow::slotWatcherExited(int, QProcess::ExitStatus)
Laurent Montel's avatar
Port    
Laurent Montel committed
206
{
Laurent Montel's avatar
Laurent Montel committed
207
    if (KMessageBox::questionYesNo(this, i18n("The watchgnupg logging process died.\nDo you want to try to restart it?"), QString(), KGuiItem(i18n("Try Restart")), KGuiItem(i18n("Do Not Try"))) == KMessageBox::Yes) {
208
209
        mCentralWidget->append(i18n("====== Restarting logging process ====="));
        mCentralWidget->ensureCursorVisible();
210
        startWatcher();
Laurent Montel's avatar
Laurent Montel committed
211
212
213
    } else {
        KMessageBox::sorry(this, i18n("The watchgnupg logging process is not running.\nThis log window is unable to display any useful information."));
    }
Laurent Montel's avatar
Port    
Laurent Montel committed
214
215
216
217
}

void KWatchGnuPGMainWindow::slotReadStdout()
{
Laurent Montel's avatar
Laurent Montel committed
218
219
220
221
222
223
224
225
226
227
228
    if (!mWatcher) {
        return;
    }
    while (mWatcher->canReadLine()) {
        QString str = QString::fromUtf8(mWatcher->readLine());
        if (str.endsWith(QLatin1Char('\n'))) {
            str.chop(1);
        }
        if (str.endsWith(QLatin1Char('\r'))) {
            str.chop(1);
        }
229
230
        mCentralWidget->append(str);
        mCentralWidget->ensureCursorVisible();
Laurent Montel's avatar
Laurent Montel committed
231
        if (!isVisible()) {
232
233
            // Change tray icon to show something happened
            // PENDING(steffen)
Laurent Montel's avatar
Laurent Montel committed
234
            mSysTray->setAttention(true);
235
        }
Laurent Montel's avatar
Laurent Montel committed
236
    }
Laurent Montel's avatar
Port    
Laurent Montel committed
237
238
239
240
}

void KWatchGnuPGMainWindow::show()
{
Laurent Montel's avatar
Laurent Montel committed
241
242
    mSysTray->setAttention(false);
    KMainWindow::show();
Laurent Montel's avatar
Port    
Laurent Montel committed
243
244
245
246
}

void KWatchGnuPGMainWindow::slotSaveAs()
{
Laurent Montel's avatar
Laurent Montel committed
247
248
249
    const QString filename = QFileDialog::getSaveFileName(this, i18n("Save Log to File"));
    if (filename.isEmpty()) {
        return;
250
    }
Laurent Montel's avatar
Laurent Montel committed
251
252
    QFile file(filename);
    if (file.open(QIODevice::WriteOnly)) {
253
        QTextStream(&file) << mCentralWidget->document()->toRawText();
Laurent Montel's avatar
Laurent Montel committed
254
255
256
    } else
        KMessageBox::information(this, i18n("Could not save file %1: %2",
                                            filename, file.errorString()));
Laurent Montel's avatar
Port    
Laurent Montel committed
257
258
259
260
}

void KWatchGnuPGMainWindow::slotQuit()
{
Laurent Montel's avatar
Laurent Montel committed
261
262
263
264
    disconnect(mWatcher, SIGNAL(finished(int,QProcess::ExitStatus)),
               this, SLOT(slotWatcherExited(int,QProcess::ExitStatus)));
    mWatcher->kill();
    qApp->quit();
Laurent Montel's avatar
Port    
Laurent Montel committed
265
266
267
268
}

void KWatchGnuPGMainWindow::slotConfigure()
{
Laurent Montel's avatar
Laurent Montel committed
269
270
    if (!mConfig) {
        mConfig = new KWatchGnuPGConfig(this);
Laurent Montel's avatar
Laurent Montel committed
271
        mConfig->setObjectName(QStringLiteral("config dialog"));
Laurent Montel's avatar
Laurent Montel committed
272
273
        connect(mConfig, &KWatchGnuPGConfig::reconfigure,
                this, &KWatchGnuPGMainWindow::slotReadConfig);
Laurent Montel's avatar
Laurent Montel committed
274
275
276
    }
    mConfig->loadConfig();
    mConfig->exec();
Laurent Montel's avatar
Port    
Laurent Montel committed
277
278
279
280
}

void KWatchGnuPGMainWindow::slotReadConfig()
{
Laurent Montel's avatar
Laurent Montel committed
281
282
    const KConfigGroup config(KSharedConfig::openConfig(), "LogWindow");
    const int maxLogLen = config.readEntry("MaxLogLen", 10000);
283
    mCentralWidget->document()->setMaximumBlockCount(maxLogLen < 1 ? -1 : maxLogLen);
Laurent Montel's avatar
Laurent Montel committed
284
285
    setGnuPGConfig();
    startWatcher();
Laurent Montel's avatar
Port    
Laurent Montel committed
286
287
288
289
}

bool KWatchGnuPGMainWindow::queryClose()
{
Laurent Montel's avatar
Laurent Montel committed
290
    if (!qApp->isSavingSession()) {
Laurent Montel's avatar
Laurent Montel committed
291
292
293
294
        hide();
        return false;
    }
    return KMainWindow::queryClose();
Laurent Montel's avatar
Port    
Laurent Montel committed
295
296
}