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

KDE's Doxygen guidelines are available online.