Libkleo

cryptoconfigmodule.cpp
1/*
2 cryptoconfigmodule.cpp
3
4 This file is part of kgpgcertmanager
5 SPDX-FileCopyrightText: 2004 Klarälvdalens Datakonsult AB
6
7 SPDX-License-Identifier: GPL-2.0-or-later
8*/
9
10#include <config-libkleo.h>
11
12#include "cryptoconfigmodule.h"
13
14#include "cryptoconfigentryreaderport_p.h"
15#include "cryptoconfigmodule_p.h"
16#include "directoryserviceswidget.h"
17#include "filenamerequester.h"
18
19#include <libkleo/compliance.h>
20#include <libkleo/formatting.h>
21#include <libkleo/gnupg.h>
22#include <libkleo/keyserverconfig.h>
23
24#include <kleo_ui_debug.h>
25
26#include <KLazyLocalizedString>
27#include <KLineEdit>
28#include <KLocalizedString>
29#include <KMessageBox>
30#include <KSeparator>
31
32#include <QGpgME/CryptoConfig>
33
34#include <QCheckBox>
35#include <QComboBox>
36#include <QDialogButtonBox>
37#include <QGridLayout>
38#include <QGroupBox>
39#include <QHBoxLayout>
40#include <QIcon>
41#include <QLabel>
42#include <QLayout>
43#include <QPushButton>
44#include <QRegularExpression>
45#include <QScreen>
46#include <QScrollArea>
47#include <QSpinBox>
48#include <QStyle>
49#include <QVBoxLayout>
50
51#include <gpgme.h>
52
53#include <array>
54#include <limits>
55#include <memory>
56#include <set>
57
58using namespace Kleo;
59
60inline QIcon loadIcon(const QString &s)
61{
62 QString ss = s;
63 const static QRegularExpression reg(QRegularExpression(QLatin1StringView("[^a-zA-Z0-9_]")));
64 return QIcon::fromTheme(ss.replace(reg, QStringLiteral("-")));
65}
66
67Kleo::CryptoConfigModule::CryptoConfigModule(QGpgME::CryptoConfig *config, QWidget *parent)
68 : QTabWidget(parent)
69 , mConfig(config)
70{
71 init();
72}
73
74void Kleo::CryptoConfigModule::init()
75{
76 if (layout()) {
77 layout()->setContentsMargins(0, 0, 0, 0);
78 }
79 setDocumentMode(true);
80
81 QGpgME::CryptoConfig *const config = mConfig;
82
83 const QStringList components = sortComponentList(config->componentList());
84 for (QStringList::const_iterator it = components.begin(); it != components.end(); ++it) {
85 // qCDebug(KLEO_UI_LOG) <<"Component" << (*it).toLocal8Bit() <<":";
86 QGpgME::CryptoConfigComponent *comp = config->component(*it);
87 Q_ASSERT(comp);
88 if (comp->groupList().empty()) {
89 continue;
90 }
91
92 std::unique_ptr<CryptoConfigComponentGUI> compGUI(new CryptoConfigComponentGUI(this, comp));
93 compGUI->setObjectName(*it);
94
95 mComponentGUIs.append(compGUI.get());
96
97 auto scrollArea = new QScrollArea(this);
98 scrollArea->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred);
99 scrollArea->setWidgetResizable(true);
100
101 addTab(scrollArea, comp->description());
102
103 scrollArea->setWidget(compGUI.release());
104 }
105 if (mComponentGUIs.empty()) {
106 const QString msg = i18n(
107 "The gpgconf tool used to provide the information "
108 "for this dialog does not seem to be installed "
109 "properly. It did not return any components. "
110 "Try running \"%1\" on the command line for more "
111 "information.",
112 components.empty() ? QLatin1StringView("gpgconf --list-components") : QLatin1String("gpgconf --list-options gpg"));
113 auto layout = new QVBoxLayout;
115 auto label = new QLabel(msg, this);
116 label->setWordWrap(true);
117 label->setMinimumHeight(fontMetrics().lineSpacing() * 5);
118 layout->addWidget(label);
119 }
120}
121
122namespace
123{
124template<typename Iterator>
125QStringList sortConfigEntries(const Iterator orderBegin, const Iterator orderEnd, const QStringList &entries)
126{
127 // components sorting algorithm:
128 // 1. components with predefined order (provided via orderBegin / orderEnd)
129 // 2. other components sorted alphabetically
130 QStringList result;
131 QStringList others;
132 for (auto it = orderBegin; it != orderEnd; ++it) {
133 if (entries.contains(*it)) {
134 result.append(*it);
135 }
136 }
137 for (const auto &item : entries) {
138 if (!result.contains(item)) {
139 others.append(item);
140 }
141 }
142 others.sort();
143 result.append(others);
144 return result;
145}
146} // namespace
147
148QStringList Kleo::CryptoConfigModule::sortComponentList(const QStringList &components)
149{
150 static const std::array<QString, 6> order = {
151 QStringLiteral("gpg"),
152 QStringLiteral("gpgsm"),
153 QStringLiteral("gpg-agent"),
154 QStringLiteral("dirmngr"),
155 QStringLiteral("pinentry"),
156 QStringLiteral("scdaemon"),
157 };
158 return sortConfigEntries(order.begin(), order.end(), components);
159}
160
161QStringList Kleo::CryptoConfigModule::sortGroupList(const QString &moduleName, const QStringList &groups)
162{
163 if (moduleName == QStringLiteral("gpg")) {
164 static const std::array<QString, 4> order = {
165 QStringLiteral("Keyserver"),
166 QStringLiteral("Configuration"),
167 QStringLiteral("Monitor"),
168 QStringLiteral("Debug"),
169 };
170 return sortConfigEntries(order.begin(), order.end(), groups);
171 } else if (moduleName == QStringLiteral("gpgsm")) {
172 static const std::array<QString, 4> order = {
173 QStringLiteral("Security"),
174 QStringLiteral("Configuration"),
175 QStringLiteral("Monitor"),
176 QStringLiteral("Debug"),
177 };
178 return sortConfigEntries(order.begin(), order.end(), groups);
179 } else if (moduleName == QStringLiteral("gpg-agent")) {
180 static const std::array<QString, 5> order = {
181 QStringLiteral("Security"),
182 QStringLiteral("Passphrase policy"),
183 QStringLiteral("Configuration"),
184 QStringLiteral("Monitor"),
185 QStringLiteral("Debug"),
186 };
187 return sortConfigEntries(order.begin(), order.end(), groups);
188 } else if (moduleName == QStringLiteral("dirmngr")) {
189 static const std::array<QString, 10> order = {
190 QStringLiteral("Keyserver"),
191 QStringLiteral("HTTP"),
192 QStringLiteral("LDAP"),
193 QStringLiteral("OCSP"),
194 QStringLiteral("Tor"),
195 QStringLiteral("Enforcement"),
196 QStringLiteral("Configuration"),
197 QStringLiteral("Format"),
198 QStringLiteral("Monitor"),
199 QStringLiteral("Debug"),
200 };
201 return sortConfigEntries(order.begin(), order.end(), groups);
202 } else if (moduleName == QStringLiteral("scdaemon")) {
203 static const std::array<QString, 4> order = {
204 QStringLiteral("Monitor"),
205 QStringLiteral("Configuration"),
206 QStringLiteral("Security"),
207 QStringLiteral("Debug"),
208 };
209 return sortConfigEntries(order.begin(), order.end(), groups);
210 } else {
211 qCDebug(KLEO_UI_LOG) << "Configuration groups order is not defined for " << moduleName;
212 QStringList result(groups);
213 result.sort();
214 return result;
215 }
216}
217
218bool Kleo::CryptoConfigModule::hasError() const
219{
220 return mComponentGUIs.empty();
221}
222
223void Kleo::CryptoConfigModule::save()
224{
225 bool changed = false;
227 for (; it != mComponentGUIs.end(); ++it) {
228 if ((*it)->save()) {
229 changed = true;
230 }
231 }
232 if (changed) {
233 mConfig->sync(true /*runtime*/);
234 }
235}
236
237void Kleo::CryptoConfigModule::reset()
238{
240 for (; it != mComponentGUIs.end(); ++it) {
241 (*it)->load();
242 }
243}
244
245void Kleo::CryptoConfigModule::defaults()
246{
248 for (; it != mComponentGUIs.end(); ++it) {
249 (*it)->defaults();
250 }
251}
252
253void Kleo::CryptoConfigModule::cancel()
254{
255 mConfig->clear();
256}
257
258////
259
260namespace
261{
262bool offerEntryForConfiguration(QGpgME::CryptoConfigEntry *entry)
263{
264 static const QRegularExpression entryPathGroupSegmentRegexp{QStringLiteral("/.*/")};
265
266 static std::set<QString> entriesToExclude;
267 if (entriesToExclude.empty()) {
268 entriesToExclude.insert(QStringLiteral("gpg/keyserver"));
269 if (engineIsVersion(2, 3, 5, GpgME::GpgConfEngine)
270 || (engineIsVersion(2, 2, 34, GpgME::GpgConfEngine) && !engineIsVersion(2, 3, 0, GpgME::GpgConfEngine))) {
271 // exclude for 2.2.{34,...} and 2.3.5+
272 entriesToExclude.insert(QStringLiteral("gpgsm/keyserver"));
273 }
274 }
275
276 const bool de_vs = DeVSCompliance::isActive();
277 // Skip "dangerous" expert options if we are running in CO_DE_VS.
278 // Otherwise, skip any options beyond "invisible" (== expert + 1) level.
279 const auto maxEntryLevel = de_vs ? QGpgME::CryptoConfigEntry::Level_Advanced //
280 : QGpgME::CryptoConfigEntry::Level_Expert + 1;
281 // we ignore the group when looking up entries to exclude because entries
282 // are uniquely identified by their name and their component
283 const auto entryId = entry->path().replace(entryPathGroupSegmentRegexp, QLatin1StringView{"/"}).toLower();
284 return (entry->level() <= maxEntryLevel) && (entriesToExclude.find(entryId) == entriesToExclude.end());
285}
286
287auto getGroupEntriesToOfferForConfiguration(QGpgME::CryptoConfigGroup *group)
288{
289 std::vector<QGpgME::CryptoConfigEntry *> result;
290 const auto entryNames = group->entryList();
291 for (const auto &entryName : entryNames) {
292 auto *const entry = group->entry(entryName);
293 Q_ASSERT(entry);
294 if (offerEntryForConfiguration(entry)) {
295 result.push_back(entry);
296 } else {
297 qCDebug(KLEO_UI_LOG) << "entry" << entry->path() << "too advanced or excluded explicitly, skipping";
298 }
299 }
300 return result;
301}
302}
303
304Kleo::CryptoConfigComponentGUI::CryptoConfigComponentGUI(CryptoConfigModule *module, QGpgME::CryptoConfigComponent *component, QWidget *parent)
305 : QWidget(parent)
306 , mComponent(component)
307{
308 auto glay = new QGridLayout(this);
309 const QStringList groups = module->sortGroupList(mComponent->name(), mComponent->groupList());
310 if (groups.size() > 1) {
311 glay->setColumnMinimumWidth(0, 30);
312 for (QStringList::const_iterator it = groups.begin(), end = groups.end(); it != end; ++it) {
313 QGpgME::CryptoConfigGroup *group = mComponent->group(*it);
314 Q_ASSERT(group);
315 if (!group) {
316 continue;
317 }
318 auto groupEntries = getGroupEntriesToOfferForConfiguration(group);
319 if (groupEntries.size() == 0) {
320 // skip groups without entries to be offered in the UI
321 continue;
322 }
323 const QString title = group->description();
324 auto hbox = new QHBoxLayout;
325 hbox->addWidget(new QLabel{title.isEmpty() ? *it : title, this});
326 hbox->addWidget(new KSeparator{Qt::Horizontal, this}, 1);
327 const int row = glay->rowCount();
328 glay->addLayout(hbox, row, 0, 1, 3);
329 mGroupGUIs.append(new CryptoConfigGroupGUI(module, group, groupEntries, glay, this));
330 }
331 } else if (!groups.empty()) {
332 auto *const group = mComponent->group(groups.front());
333 auto groupEntries = getGroupEntriesToOfferForConfiguration(group);
334 if (groupEntries.size() > 0) {
335 mGroupGUIs.append(new CryptoConfigGroupGUI(module, group, groupEntries, glay, this));
336 }
337 }
338 glay->setRowStretch(glay->rowCount(), 1);
339}
340
341bool Kleo::CryptoConfigComponentGUI::save()
342{
343 bool changed = false;
345 for (; it != mGroupGUIs.end(); ++it) {
346 if ((*it)->save()) {
347 changed = true;
348 }
349 }
350 return changed;
351}
352
353void Kleo::CryptoConfigComponentGUI::load()
354{
356 for (; it != mGroupGUIs.end(); ++it) {
357 (*it)->load();
358 }
359}
360
361void Kleo::CryptoConfigComponentGUI::defaults()
362{
364 for (; it != mGroupGUIs.end(); ++it) {
365 (*it)->defaults();
366 }
367}
368
369////
370
371Kleo::CryptoConfigGroupGUI::CryptoConfigGroupGUI(CryptoConfigModule *module,
372 QGpgME::CryptoConfigGroup *group,
373 const std::vector<QGpgME::CryptoConfigEntry *> &entries,
374 QGridLayout *glay,
375 QWidget *widget)
376 : QObject(module)
377{
378 const int startRow = glay->rowCount();
379 for (auto entry : entries) {
380 CryptoConfigEntryGUI *entryGUI = CryptoConfigEntryGUIFactory::createEntryGUI(module, entry, entry->name(), glay, widget);
381 if (entryGUI) {
382 mEntryGUIs.append(entryGUI);
383 entryGUI->load();
384 }
385 }
386 const int endRow = glay->rowCount() - 1;
387 if (endRow < startRow) {
388 return;
389 }
390
391 const QString iconName = group->iconName();
392 if (iconName.isEmpty()) {
393 return;
394 }
395
396 QLabel *l = new QLabel(widget);
397 l->setPixmap(loadIcon(iconName).pixmap(32, 32));
398 glay->addWidget(l, startRow, 0, endRow - startRow + 1, 1, Qt::AlignTop);
399}
400
401bool Kleo::CryptoConfigGroupGUI::save()
402{
403 bool changed = false;
405 for (; it != mEntryGUIs.end(); ++it) {
406 if ((*it)->isChanged()) {
407 (*it)->save();
408 changed = true;
409 }
410 }
411 return changed;
412}
413
414void Kleo::CryptoConfigGroupGUI::load()
415{
417 for (; it != mEntryGUIs.end(); ++it) {
418 (*it)->load();
419 }
420}
421
422void Kleo::CryptoConfigGroupGUI::defaults()
423{
425 for (; it != mEntryGUIs.end(); ++it) {
426 (*it)->resetToDefault();
427 }
428}
429
430////
431
432using constructor = CryptoConfigEntryGUI *(*)(CryptoConfigModule *, QGpgME::CryptoConfigEntry *, const QString &, QGridLayout *, QWidget *);
433
434namespace
435{
436template<typename T_Widget>
437CryptoConfigEntryGUI *_create(CryptoConfigModule *m, QGpgME::CryptoConfigEntry *e, const QString &n, QGridLayout *l, QWidget *p)
438{
439 return new T_Widget(m, e, n, l, p);
440}
441}
442
443static const struct WidgetsByEntryName {
444 const char *entryGlob;
445 constructor create;
446} widgetsByEntryName[] = {
447 {"*/*/debug-level", &_create<CryptoConfigEntryDebugLevel>},
448 {"scdaemon/*/reader-port", &_create<CryptoConfigEntryReaderPort>},
449};
450static const unsigned int numWidgetsByEntryName = sizeof widgetsByEntryName / sizeof *widgetsByEntryName;
451
452static const constructor listWidgets[QGpgME::CryptoConfigEntry::NumArgType] = {
453 // None: A list of options with no arguments (e.g. -v -v -v) is shown as a spinbox
454 &_create<CryptoConfigEntrySpinBox>,
455 nullptr, // String
456 // Int/UInt: Let people type list of numbers (1,2,3....). Untested.
457 &_create<CryptoConfigEntryLineEdit>,
458 &_create<CryptoConfigEntryLineEdit>,
459 nullptr, // Path
460 nullptr, // Formerly URL
461 &_create<CryptoConfigEntryLDAPURL>,
462 nullptr, // DirPath
463};
464
465static const constructor scalarWidgets[QGpgME::CryptoConfigEntry::NumArgType] = {
466 // clang-format off
467 &_create<CryptoConfigEntryCheckBox>, // None
468 &_create<CryptoConfigEntryLineEdit>, // String
469 &_create<CryptoConfigEntrySpinBox>, // Int
470 &_create<CryptoConfigEntrySpinBox>, // UInt
471 &_create<CryptoConfigEntryPath>, // Path
472 nullptr, // Formerly URL
473 nullptr, // LDAPURL
474 &_create<CryptoConfigEntryDirPath>, // DirPath
475 // clang-format on
476};
477
478CryptoConfigEntryGUI *Kleo::CryptoConfigEntryGUIFactory::createEntryGUI(CryptoConfigModule *module,
479 QGpgME::CryptoConfigEntry *entry,
480 const QString &entryName,
481 QGridLayout *glay,
482 QWidget *widget)
483{
484 Q_ASSERT(entry);
485
486 // try to lookup by path:
487 const QString path = entry->path();
488 for (unsigned int i = 0; i < numWidgetsByEntryName; ++i) {
489 if (QRegularExpression::fromWildcard(QString::fromLatin1(widgetsByEntryName[i].entryGlob), Qt::CaseSensitive).match(path).hasMatch()) {
490 return widgetsByEntryName[i].create(module, entry, entryName, glay, widget);
491 }
492 }
493
494 // none found, so look up by type:
495 const unsigned int argType = entry->argType();
496 Q_ASSERT(argType < QGpgME::CryptoConfigEntry::NumArgType);
497 if (entry->isList()) {
498 if (const constructor create = listWidgets[argType]) {
499 return create(module, entry, entryName, glay, widget);
500 } else {
501 qCWarning(KLEO_UI_LOG) << "No widget implemented for list of type" << entry->argType();
502 }
503 } else if (const constructor create = scalarWidgets[argType]) {
504 return create(module, entry, entryName, glay, widget);
505 } else {
506 qCWarning(KLEO_UI_LOG) << "No widget implemented for type" << entry->argType();
507 }
508
509 return nullptr;
510}
511
512////
513
514Kleo::CryptoConfigEntryGUI::CryptoConfigEntryGUI(CryptoConfigModule *module, QGpgME::CryptoConfigEntry *entry, const QString &entryName)
515 : QObject(module)
516 , mEntry(entry)
517 , mName(entryName)
518 , mChanged(false)
519{
520 connect(this, &CryptoConfigEntryGUI::changed, module, &CryptoConfigModule::changed);
521}
522
523QString Kleo::CryptoConfigEntryGUI::description() const
524{
525 QString descr = mEntry->description();
526 if (descr.isEmpty()) { // happens for expert options
527 // String does not need to be translated because the options itself
528 // are also not translated
529 return QStringLiteral("\"%1\"").arg(mName);
530 }
531 if (i18nc("Translate this to 'yes' or 'no' (use the English words!) "
532 "depending on whether your language uses "
533 "Sentence style capitalization in GUI labels (yes) or not (no). "
534 "Context: We get some backend strings in that have the wrong "
535 "capitalization (in English, at least) so we need to force the "
536 "first character to upper-case. It is this behaviour you can "
537 "control for your language with this translation.",
538 "yes")
539 == QLatin1StringView("yes")) {
540 descr[0] = descr[0].toUpper();
541 }
542 return descr;
543}
544
545void Kleo::CryptoConfigEntryGUI::resetToDefault()
546{
547 mEntry->resetToDefault();
548 load();
549}
550
551////
552
553Kleo::CryptoConfigEntryLineEdit::CryptoConfigEntryLineEdit(CryptoConfigModule *module,
554 QGpgME::CryptoConfigEntry *entry,
555 const QString &entryName,
556 QGridLayout *glay,
557 QWidget *widget)
558 : CryptoConfigEntryGUI(module, entry, entryName)
559{
560 const int row = glay->rowCount();
561 mLineEdit = new KLineEdit(widget);
562 QLabel *label = new QLabel(description(), widget);
563 label->setBuddy(mLineEdit);
564 glay->addWidget(label, row, 1);
565 glay->addWidget(mLineEdit, row, 2);
566 if (entry->isReadOnly()) {
567 label->setEnabled(false);
568 mLineEdit->setEnabled(false);
569 } else {
570 connect(mLineEdit, &KLineEdit::textChanged, this, &CryptoConfigEntryLineEdit::slotChanged);
571 }
572}
573
574void Kleo::CryptoConfigEntryLineEdit::doSave()
575{
576 mEntry->setStringValue(mLineEdit->text());
577}
578
579void Kleo::CryptoConfigEntryLineEdit::doLoad()
580{
581 mLineEdit->setText(mEntry->stringValue());
582}
583
584////
585/* Note: Do not use "guru" as debug level but use the value 10. The
586 former also enables the creation of hash dump files and thus leaves
587 traces of plaintext on the disk. */
588static const struct {
589 const KLazyLocalizedString label;
590 const char *name;
591} debugLevels[] = {
592 {kli18n("0 - None"), "none"},
593 {kli18n("1 - Basic"), "basic"},
594 {kli18n("2 - Verbose"), "advanced"},
595 {kli18n("3 - More Verbose"), "expert"},
596 {kli18n("4 - All"), "10"},
597};
598static const unsigned int numDebugLevels = sizeof debugLevels / sizeof *debugLevels;
599
600Kleo::CryptoConfigEntryDebugLevel::CryptoConfigEntryDebugLevel(CryptoConfigModule *module,
601 QGpgME::CryptoConfigEntry *entry,
602 const QString &entryName,
603 QGridLayout *glay,
604 QWidget *widget)
605 : CryptoConfigEntryGUI(module, entry, entryName)
606 , mComboBox(new QComboBox(widget))
607{
608 QLabel *label = new QLabel(i18n("Set the debugging level to"), widget);
609 label->setBuddy(mComboBox);
610
611 for (unsigned int i = 0; i < numDebugLevels; ++i) {
612 mComboBox->addItem(KLocalizedString(debugLevels[i].label).toString());
613 }
614
615 if (entry->isReadOnly()) {
616 label->setEnabled(false);
617 mComboBox->setEnabled(false);
618 } else {
619 connect(mComboBox, &QComboBox::currentIndexChanged, this, &CryptoConfigEntryDebugLevel::slotChanged);
620 }
621
622 const int row = glay->rowCount();
623 glay->addWidget(label, row, 1);
624 glay->addWidget(mComboBox, row, 2);
625}
626
627void Kleo::CryptoConfigEntryDebugLevel::doSave()
628{
629 const unsigned int idx = mComboBox->currentIndex();
630 if (idx < numDebugLevels) {
631 mEntry->setStringValue(QLatin1StringView(debugLevels[idx].name));
632 } else {
633 mEntry->setStringValue(QString());
634 }
635}
636
637void Kleo::CryptoConfigEntryDebugLevel::doLoad()
638{
639 const QString str = mEntry->stringValue();
640 for (unsigned int i = 0; i < numDebugLevels; ++i) {
641 if (str == QLatin1StringView(debugLevels[i].name)) {
642 mComboBox->setCurrentIndex(i);
643 return;
644 }
645 }
646 mComboBox->setCurrentIndex(0);
647}
648
649////
650
651Kleo::CryptoConfigEntryPath::CryptoConfigEntryPath(CryptoConfigModule *module,
652 QGpgME::CryptoConfigEntry *entry,
653 const QString &entryName,
654 QGridLayout *glay,
655 QWidget *widget)
656 : CryptoConfigEntryGUI(module, entry, entryName)
657 , mFileNameRequester(nullptr)
658{
659 const int row = glay->rowCount();
660 mFileNameRequester = new FileNameRequester(widget);
661 mFileNameRequester->setExistingOnly(false);
662 mFileNameRequester->setFilter(QDir::Files);
663 QLabel *label = new QLabel(description(), widget);
664 label->setBuddy(mFileNameRequester);
665 glay->addWidget(label, row, 1);
666 glay->addWidget(mFileNameRequester, row, 2);
667 if (entry->isReadOnly()) {
668 label->setEnabled(false);
669 mFileNameRequester->setEnabled(false);
670 } else {
671 connect(mFileNameRequester, &FileNameRequester::fileNameChanged, this, &CryptoConfigEntryPath::slotChanged);
672 }
673}
674
675void Kleo::CryptoConfigEntryPath::doSave()
676{
677 mEntry->setURLValue(QUrl::fromLocalFile(mFileNameRequester->fileName()));
678}
679
680void Kleo::CryptoConfigEntryPath::doLoad()
681{
682 if (mEntry->urlValue().isLocalFile()) {
683 mFileNameRequester->setFileName(mEntry->urlValue().toLocalFile());
684 } else {
685 mFileNameRequester->setFileName(mEntry->urlValue().toString());
686 }
687}
688
689////
690
691Kleo::CryptoConfigEntryDirPath::CryptoConfigEntryDirPath(CryptoConfigModule *module,
692 QGpgME::CryptoConfigEntry *entry,
693 const QString &entryName,
694 QGridLayout *glay,
695 QWidget *widget)
696 : CryptoConfigEntryGUI(module, entry, entryName)
697 , mFileNameRequester(nullptr)
698{
699 const int row = glay->rowCount();
700 mFileNameRequester = new FileNameRequester(widget);
701 mFileNameRequester->setExistingOnly(false);
702 mFileNameRequester->setFilter(QDir::Dirs);
703 QLabel *label = new QLabel(description(), widget);
704 label->setBuddy(mFileNameRequester);
705 glay->addWidget(label, row, 1);
706 glay->addWidget(mFileNameRequester, row, 2);
707 if (entry->isReadOnly()) {
708 label->setEnabled(false);
709 mFileNameRequester->setEnabled(false);
710 } else {
711 connect(mFileNameRequester, &FileNameRequester::fileNameChanged, this, &CryptoConfigEntryDirPath::slotChanged);
712 }
713}
714
715void Kleo::CryptoConfigEntryDirPath::doSave()
716{
717 mEntry->setURLValue(QUrl::fromLocalFile(mFileNameRequester->fileName()));
718}
719
720void Kleo::CryptoConfigEntryDirPath::doLoad()
721{
722 mFileNameRequester->setFileName(mEntry->urlValue().toLocalFile());
723}
724
725////
726
727Kleo::CryptoConfigEntrySpinBox::CryptoConfigEntrySpinBox(CryptoConfigModule *module,
728 QGpgME::CryptoConfigEntry *entry,
729 const QString &entryName,
730 QGridLayout *glay,
731 QWidget *widget)
732 : CryptoConfigEntryGUI(module, entry, entryName)
733{
734 if (entry->argType() == QGpgME::CryptoConfigEntry::ArgType_None && entry->isList()) {
735 mKind = ListOfNone;
736 } else if (entry->argType() == QGpgME::CryptoConfigEntry::ArgType_UInt) {
737 mKind = UInt;
738 } else {
739 Q_ASSERT(entry->argType() == QGpgME::CryptoConfigEntry::ArgType_Int);
740 mKind = Int;
741 }
742
743 const int row = glay->rowCount();
744 mNumInput = new QSpinBox(widget);
745 QLabel *label = new QLabel(description(), widget);
746 label->setBuddy(mNumInput);
747 glay->addWidget(label, row, 1);
748 glay->addWidget(mNumInput, row, 2);
749
750 if (entry->isReadOnly()) {
751 label->setEnabled(false);
752 mNumInput->setEnabled(false);
753 } else {
754 mNumInput->setMinimum(mKind == Int ? std::numeric_limits<int>::min() : 0);
755 mNumInput->setMaximum(std::numeric_limits<int>::max());
756 connect(mNumInput, &QSpinBox::valueChanged, this, &CryptoConfigEntrySpinBox::slotChanged);
757 }
758}
759
760void Kleo::CryptoConfigEntrySpinBox::doSave()
761{
762 int value = mNumInput->value();
763 switch (mKind) {
764 case ListOfNone:
765 mEntry->setNumberOfTimesSet(value);
766 break;
767 case UInt:
768 mEntry->setUIntValue(value);
769 break;
770 case Int:
771 mEntry->setIntValue(value);
772 break;
773 }
774}
775
776void Kleo::CryptoConfigEntrySpinBox::doLoad()
777{
778 int value = 0;
779 switch (mKind) {
780 case ListOfNone:
781 value = mEntry->numberOfTimesSet();
782 break;
783 case UInt:
784 value = mEntry->uintValue();
785 break;
786 case Int:
787 value = mEntry->intValue();
788 break;
789 }
790 mNumInput->setValue(value);
791}
792
793////
794
795Kleo::CryptoConfigEntryCheckBox::CryptoConfigEntryCheckBox(CryptoConfigModule *module,
796 QGpgME::CryptoConfigEntry *entry,
797 const QString &entryName,
798 QGridLayout *glay,
799 QWidget *widget)
800 : CryptoConfigEntryGUI(module, entry, entryName)
801{
802 const int row = glay->rowCount();
803 mCheckBox = new QCheckBox(widget);
804 glay->addWidget(mCheckBox, row, 1, 1, 2);
805 mCheckBox->setText(description());
806 if (entry->isReadOnly()) {
807 mCheckBox->setEnabled(false);
808 } else {
809 connect(mCheckBox, &QCheckBox::toggled, this, &CryptoConfigEntryCheckBox::slotChanged);
810 }
811}
812
813void Kleo::CryptoConfigEntryCheckBox::doSave()
814{
815 mEntry->setBoolValue(mCheckBox->isChecked());
816}
817
818void Kleo::CryptoConfigEntryCheckBox::doLoad()
819{
820 mCheckBox->setChecked(mEntry->boolValue());
821}
822
823Kleo::CryptoConfigEntryLDAPURL::CryptoConfigEntryLDAPURL(CryptoConfigModule *module,
824 QGpgME::CryptoConfigEntry *entry,
825 const QString &entryName,
826 QGridLayout *glay,
827 QWidget *widget)
828 : CryptoConfigEntryGUI(module, entry, entryName)
829{
830 mLabel = new QLabel(widget);
831 mPushButton = new QPushButton(entry->isReadOnly() ? i18n("Show...") : i18n("Edit..."), widget);
832
833 const int row = glay->rowCount();
834 QLabel *label = new QLabel(description(), widget);
835 label->setBuddy(mPushButton);
836 glay->addWidget(label, row, 1);
837 auto hlay = new QHBoxLayout;
838 glay->addLayout(hlay, row, 2);
839 hlay->addWidget(mLabel, 1);
840 hlay->addWidget(mPushButton);
841
842 if (entry->isReadOnly()) {
843 mLabel->setEnabled(false);
844 }
845 connect(mPushButton, &QPushButton::clicked, this, &CryptoConfigEntryLDAPURL::slotOpenDialog);
846}
847
848void Kleo::CryptoConfigEntryLDAPURL::doLoad()
849{
850 setURLList(mEntry->urlValueList());
851}
852
853void Kleo::CryptoConfigEntryLDAPURL::doSave()
854{
855 mEntry->setURLValueList(mURLList);
856}
857
858void prepareURLCfgDialog(QDialog *dialog, DirectoryServicesWidget *dirserv, bool readOnly)
859{
861
862 if (!readOnly) {
865
866 QPushButton *defaultsBtn = buttonBox->button(QDialogButtonBox::RestoreDefaults);
867
868 QObject::connect(defaultsBtn, &QPushButton::clicked, dirserv, &DirectoryServicesWidget::clear);
870 }
871
873
874 auto layout = new QVBoxLayout;
875 layout->addWidget(dirserv);
876 layout->addWidget(buttonBox);
877 dialog->setLayout(layout);
878}
879
880void Kleo::CryptoConfigEntryLDAPURL::slotOpenDialog()
881{
882 if (!gpgme_check_version("1.16.0")) {
883 KMessageBox::error(mPushButton->parentWidget(),
884 i18n("Configuration of directory services is not possible "
885 "because the used gpgme libraries are too old."),
886 i18n("Sorry"));
887 return;
888 }
889
890 // I'm a bad boy and I do it all on the stack. Enough classes already :)
891 // This is just a simple dialog around the directory-services-widget
892 QDialog dialog(mPushButton->parentWidget());
893 dialog.setWindowTitle(i18nc("@title:window", "Configure Directory Services"));
894
895 auto dirserv = new DirectoryServicesWidget(&dialog);
896
897 prepareURLCfgDialog(&dialog, dirserv, mEntry->isReadOnly());
898
899 dirserv->setReadOnly(mEntry->isReadOnly());
900
901 std::vector<KeyserverConfig> servers;
902 std::transform(std::cbegin(mURLList), std::cend(mURLList), std::back_inserter(servers), [](const auto &url) {
903 return KeyserverConfig::fromUrl(url);
904 });
905 dirserv->setKeyservers(servers);
906
907 if (dialog.exec()) {
908 QList<QUrl> urls;
909 const auto servers = dirserv->keyservers();
910 std::transform(std::begin(servers), std::end(servers), std::back_inserter(urls), [](const auto &server) {
911 return server.toUrl();
912 });
913 setURLList(urls);
914 slotChanged();
915 }
916}
917
918void Kleo::CryptoConfigEntryLDAPURL::setURLList(const QList<QUrl> &urlList)
919{
920 mURLList = urlList;
921 if (mURLList.isEmpty()) {
922 mLabel->setText(i18n("None configured"));
923 } else {
924 mLabel->setText(i18np("1 server configured", "%1 servers configured", mURLList.count()));
925 }
926}
927
928#include "moc_cryptoconfigmodule_p.cpp"
929
930#include "moc_cryptoconfigmodule.cpp"
Crypto Config Module widget, dynamically generated from CryptoConfig It's a simple QWidget so that it...
QString i18np(const char *singular, const char *plural, const TYPE &arg...)
QString i18nc(const char *context, const char *text, const TYPE &arg...)
QString i18n(const char *text, const TYPE &arg...)
char * toString(const EngineQuery &query)
KCOREADDONS_EXPORT Result match(QStringView pattern, QStringView str)
QAction * create(GameStandardAction id, const QObject *recvr, const char *slot, QObject *parent)
QAction * load(const QObject *recvr, const char *slot, QObject *parent)
QString path(const QString &relativePath)
void error(QWidget *parent, const QString &text, const QString &title, const KGuiItem &buttonOk, Options options=Notify)
const QList< QKeySequence > & end()
QCA_EXPORT void init()
void clicked(bool checked)
void toggled(bool checked)
void addWidget(QWidget *widget, int stretch, Qt::Alignment alignment)
void currentIndexChanged(int index)
virtual void accept()
virtual int exec()
virtual void reject()
QPushButton * addButton(StandardButton button)
QPushButton * button(StandardButton which) const const
void addLayout(QLayout *layout, int row, int column, Qt::Alignment alignment)
void addWidget(QWidget *widget, int fromRow, int fromColumn, int rowSpan, int columnSpan, Qt::Alignment alignment)
int rowCount() const const
QIcon fromTheme(const QString &name)
void setPixmap(const QPixmap &)
void addWidget(QWidget *w)
void setContentsMargins(const QMargins &margins)
void textChanged(const QString &text)
void append(QList< T > &&value)
iterator begin()
bool empty() const const
iterator end()
reference front()
qsizetype size() const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
QRegularExpression fromWildcard(QStringView pattern, Qt::CaseSensitivity cs, WildcardConversionOptions options)
void valueChanged(int i)
QString arg(Args &&... args) const const
QString fromLatin1(QByteArrayView str)
bool isEmpty() const const
QString & replace(QChar before, QChar after, Qt::CaseSensitivity cs)
QString toUpper() const const
bool contains(QLatin1StringView str, Qt::CaseSensitivity cs) const const
void sort(Qt::CaseSensitivity cs)
AlignTop
CaseSensitive
Horizontal
int addTab(QWidget *page, const QIcon &icon, const QString &label)
void setDocumentMode(bool set)
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
QUrl fromLocalFile(const QString &localFile)
QFontMetrics fontMetrics() const const
QLayout * layout() const const
void setLayout(QLayout *layout)
void setWindowTitle(const QString &)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:14:12 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.