10#include <config-libkleo.h>
12#include "cryptoconfigmodule.h"
14#include "cryptoconfigentryreaderport_p.h"
15#include "cryptoconfigmodule_p.h"
16#include "directoryserviceswidget.h"
17#include "filenamerequester.h"
19#include <libkleo/compliance.h>
20#include <libkleo/formatting.h>
21#include <libkleo/gnupg.h>
22#include <libkleo/keyserverconfig.h>
24#include <kleo_ui_debug.h>
26#include <KLazyLocalizedString>
28#include <KLocalizedString>
32#include <QGpgME/CryptoConfig>
36#include <QDialogButtonBox>
44#include <QRegularExpression>
67Kleo::CryptoConfigModule::CryptoConfigModule(QGpgME::CryptoConfig *config,
QWidget *parent)
74void Kleo::CryptoConfigModule::init()
81 QGpgME::CryptoConfig *
const config = mConfig;
83 const QStringList components = sortComponentList(config->componentList());
86 QGpgME::CryptoConfigComponent *comp = config->component(*it);
88 if (comp->groupList().empty()) {
92 std::unique_ptr<CryptoConfigComponentGUI> compGUI(
new CryptoConfigComponentGUI(
this, comp));
93 compGUI->setObjectName(*it);
95 mComponentGUIs.
append(compGUI.get());
99 scrollArea->setWidgetResizable(
true);
101 addTab(scrollArea, comp->description());
103 scrollArea->setWidget(compGUI.release());
105 if (mComponentGUIs.
empty()) {
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 "
115 auto label =
new QLabel(msg,
this);
116 label->setWordWrap(
true);
117 label->setMinimumHeight(
fontMetrics().lineSpacing() * 5);
124template<
typename Iterator>
125QStringList sortConfigEntries(
const Iterator orderBegin,
const Iterator orderEnd,
const QStringList &entries)
132 for (
auto it = orderBegin; it != orderEnd; ++it) {
137 for (
const auto &item : entries) {
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"),
158 return sortConfigEntries(order.begin(), order.end(), components);
164 static const std::array<QString, 4> order = {
165 QStringLiteral(
"Keyserver"),
166 QStringLiteral(
"Configuration"),
167 QStringLiteral(
"Monitor"),
168 QStringLiteral(
"Debug"),
170 return sortConfigEntries(order.begin(), order.end(), groups);
172 static const std::array<QString, 4> order = {
173 QStringLiteral(
"Security"),
174 QStringLiteral(
"Configuration"),
175 QStringLiteral(
"Monitor"),
176 QStringLiteral(
"Debug"),
178 return sortConfigEntries(order.begin(), order.end(), groups);
180 static const std::array<QString, 5> order = {
181 QStringLiteral(
"Security"),
182 QStringLiteral(
"Passphrase policy"),
183 QStringLiteral(
"Configuration"),
184 QStringLiteral(
"Monitor"),
185 QStringLiteral(
"Debug"),
187 return sortConfigEntries(order.begin(), order.end(), groups);
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"),
201 return sortConfigEntries(order.begin(), order.end(), groups);
203 static const std::array<QString, 4> order = {
204 QStringLiteral(
"Monitor"),
205 QStringLiteral(
"Configuration"),
206 QStringLiteral(
"Security"),
207 QStringLiteral(
"Debug"),
209 return sortConfigEntries(order.begin(), order.end(), groups);
211 qCDebug(KLEO_UI_LOG) <<
"Configuration groups order is not defined for " << moduleName;
218bool Kleo::CryptoConfigModule::hasError()
const
220 return mComponentGUIs.empty();
223void Kleo::CryptoConfigModule::save()
225 bool changed =
false;
227 for (; it != mComponentGUIs.
end(); ++it) {
233 mConfig->sync(
true );
237void Kleo::CryptoConfigModule::reset()
240 for (; it != mComponentGUIs.
end(); ++it) {
245void Kleo::CryptoConfigModule::defaults()
248 for (; it != mComponentGUIs.
end(); ++it) {
253void Kleo::CryptoConfigModule::cancel()
262bool offerEntryForConfiguration(QGpgME::CryptoConfigEntry *entry)
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))) {
272 entriesToExclude.insert(QStringLiteral(
"gpgsm/keyserver"));
276 const bool de_vs = DeVSCompliance::isActive();
279 const auto maxEntryLevel = de_vs ? QGpgME::CryptoConfigEntry::Level_Advanced
280 : QGpgME::CryptoConfigEntry::Level_Expert + 1;
283 const auto entryId = entry->path().replace(entryPathGroupSegmentRegexp,
QLatin1StringView{
"/"}).toLower();
284 return (entry->level() <= maxEntryLevel) && (entriesToExclude.find(entryId) == entriesToExclude.end());
287auto getGroupEntriesToOfferForConfiguration(QGpgME::CryptoConfigGroup *group)
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);
294 if (offerEntryForConfiguration(entry)) {
295 result.push_back(entry);
297 qCDebug(KLEO_UI_LOG) <<
"entry" << entry->path() <<
"too advanced or excluded explicitly, skipping";
304Kleo::CryptoConfigComponentGUI::CryptoConfigComponentGUI(
CryptoConfigModule *module, QGpgME::CryptoConfigComponent *component,
QWidget *parent)
306 , mComponent(component)
309 const QStringList groups =
module->sortGroupList(mComponent->name(), mComponent->groupList());
310 if (groups.
size() > 1) {
311 glay->setColumnMinimumWidth(0, 30);
313 QGpgME::CryptoConfigGroup *group = mComponent->group(*it);
318 auto groupEntries = getGroupEntriesToOfferForConfiguration(group);
319 if (groupEntries.size() == 0) {
323 const QString title = group->description();
327 const int row = glay->rowCount();
328 glay->addLayout(hbox, row, 0, 1, 3);
329 mGroupGUIs.append(
new CryptoConfigGroupGUI(module, group, groupEntries, glay,
this));
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));
338 glay->setRowStretch(glay->rowCount(), 1);
341bool Kleo::CryptoConfigComponentGUI::save()
343 bool changed =
false;
345 for (; it != mGroupGUIs.
end(); ++it) {
353void Kleo::CryptoConfigComponentGUI::load()
356 for (; it != mGroupGUIs.
end(); ++it) {
361void Kleo::CryptoConfigComponentGUI::defaults()
364 for (; it != mGroupGUIs.
end(); ++it) {
372 QGpgME::CryptoConfigGroup *group,
373 const std::vector<QGpgME::CryptoConfigEntry *> &entries,
378 const int startRow = glay->
rowCount();
379 for (
auto entry : entries) {
380 CryptoConfigEntryGUI *entryGUI = CryptoConfigEntryGUIFactory::createEntryGUI(module, entry, entry->name(), glay, widget);
382 mEntryGUIs.append(entryGUI);
386 const int endRow = glay->
rowCount() - 1;
387 if (endRow < startRow) {
391 const QString iconName = group->iconName();
397 l->
setPixmap(loadIcon(iconName).pixmap(32, 32));
401bool Kleo::CryptoConfigGroupGUI::save()
403 bool changed =
false;
405 for (; it != mEntryGUIs.
end(); ++it) {
406 if ((*it)->isChanged()) {
414void Kleo::CryptoConfigGroupGUI::load()
417 for (; it != mEntryGUIs.
end(); ++it) {
422void Kleo::CryptoConfigGroupGUI::defaults()
425 for (; it != mEntryGUIs.
end(); ++it) {
426 (*it)->resetToDefault();
436template<
typename T_W
idget>
439 return new T_Widget(m, e, n, l, p);
443static const struct WidgetsByEntryName {
444 const char *entryGlob;
446} widgetsByEntryName[] = {
447 {
"*/*/debug-level", &_create<CryptoConfigEntryDebugLevel>},
448 {
"scdaemon/*/reader-port", &_create<CryptoConfigEntryReaderPort>},
450static const unsigned int numWidgetsByEntryName =
sizeof widgetsByEntryName /
sizeof *widgetsByEntryName;
452static const constructor listWidgets[QGpgME::CryptoConfigEntry::NumArgType] = {
454 &_create<CryptoConfigEntrySpinBox>,
457 &_create<CryptoConfigEntryLineEdit>,
458 &_create<CryptoConfigEntryLineEdit>,
461 &_create<CryptoConfigEntryLDAPURL>,
465static const constructor scalarWidgets[QGpgME::CryptoConfigEntry::NumArgType] = {
467 &_create<CryptoConfigEntryCheckBox>,
468 &_create<CryptoConfigEntryLineEdit>,
469 &_create<CryptoConfigEntrySpinBox>,
470 &_create<CryptoConfigEntrySpinBox>,
471 &_create<CryptoConfigEntryPath>,
474 &_create<CryptoConfigEntryDirPath>,
478CryptoConfigEntryGUI *Kleo::CryptoConfigEntryGUIFactory::createEntryGUI(
CryptoConfigModule *module,
479 QGpgME::CryptoConfigEntry *entry,
488 for (
unsigned int i = 0; i < numWidgetsByEntryName; ++i) {
490 return widgetsByEntryName[i].create(module, entry, entryName, glay, widget);
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);
501 qCWarning(KLEO_UI_LOG) <<
"No widget implemented for list of type" << entry->argType();
503 }
else if (
const constructor create = scalarWidgets[argType]) {
504 return create(module, entry, entryName, glay, widget);
506 qCWarning(KLEO_UI_LOG) <<
"No widget implemented for type" << entry->argType();
514Kleo::CryptoConfigEntryGUI::CryptoConfigEntryGUI(
CryptoConfigModule *module, QGpgME::CryptoConfigEntry *entry,
const QString &entryName)
520 connect(
this, &CryptoConfigEntryGUI::changed, module, &CryptoConfigModule::changed);
523QString Kleo::CryptoConfigEntryGUI::description()
const
525 QString descr = mEntry->description();
529 return QStringLiteral(
"\"%1\"").arg(mName);
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.",
545void Kleo::CryptoConfigEntryGUI::resetToDefault()
547 mEntry->resetToDefault();
554 QGpgME::CryptoConfigEntry *entry,
558 : CryptoConfigEntryGUI(module, entry, entryName)
563 label->setBuddy(mLineEdit);
566 if (entry->isReadOnly()) {
567 label->setEnabled(
false);
568 mLineEdit->setEnabled(
false);
574void Kleo::CryptoConfigEntryLineEdit::doSave()
576 mEntry->setStringValue(mLineEdit->text());
579void Kleo::CryptoConfigEntryLineEdit::doLoad()
581 mLineEdit->setText(mEntry->stringValue());
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"},
598static const unsigned int numDebugLevels =
sizeof debugLevels /
sizeof *debugLevels;
600Kleo::CryptoConfigEntryDebugLevel::CryptoConfigEntryDebugLevel(
CryptoConfigModule *module,
601 QGpgME::CryptoConfigEntry *entry,
605 : CryptoConfigEntryGUI(module, entry, entryName)
608 QLabel *label =
new QLabel(
i18nc(
"@label:textbox",
"Set the debugging level to"), widget);
609 label->setBuddy(mComboBox);
611 for (
unsigned int i = 0; i < numDebugLevels; ++i) {
615 if (entry->isReadOnly()) {
616 label->setEnabled(
false);
617 mComboBox->setEnabled(
false);
627void Kleo::CryptoConfigEntryDebugLevel::doSave()
629 const unsigned int idx = mComboBox->currentIndex();
630 if (idx < numDebugLevels) {
633 mEntry->setStringValue(
QString());
637void Kleo::CryptoConfigEntryDebugLevel::doLoad()
639 const QString str = mEntry->stringValue();
640 for (
unsigned int i = 0; i < numDebugLevels; ++i) {
642 mComboBox->setCurrentIndex(i);
646 mComboBox->setCurrentIndex(0);
652 QGpgME::CryptoConfigEntry *entry,
656 : CryptoConfigEntryGUI(module, entry, entryName)
657 , mFileNameRequester(nullptr)
660 mFileNameRequester =
new FileNameRequester(widget);
661 mFileNameRequester->setExistingOnly(
false);
664 label->setBuddy(mFileNameRequester);
666 glay->
addWidget(mFileNameRequester, row, 2);
667 if (entry->isReadOnly()) {
668 label->setEnabled(
false);
669 mFileNameRequester->setEnabled(
false);
671 connect(mFileNameRequester, &FileNameRequester::fileNameChanged,
this, &CryptoConfigEntryPath::slotChanged);
675void Kleo::CryptoConfigEntryPath::doSave()
680void Kleo::CryptoConfigEntryPath::doLoad()
682 if (mEntry->urlValue().isLocalFile()) {
683 mFileNameRequester->setFileName(mEntry->urlValue().toLocalFile());
685 mFileNameRequester->setFileName(mEntry->urlValue().toString());
692 QGpgME::CryptoConfigEntry *entry,
696 : CryptoConfigEntryGUI(module, entry, entryName)
697 , mFileNameRequester(nullptr)
700 mFileNameRequester =
new FileNameRequester(widget);
701 mFileNameRequester->setExistingOnly(
false);
704 label->setBuddy(mFileNameRequester);
706 glay->
addWidget(mFileNameRequester, row, 2);
707 if (entry->isReadOnly()) {
708 label->setEnabled(
false);
709 mFileNameRequester->setEnabled(
false);
711 connect(mFileNameRequester, &FileNameRequester::fileNameChanged,
this, &CryptoConfigEntryDirPath::slotChanged);
715void Kleo::CryptoConfigEntryDirPath::doSave()
720void Kleo::CryptoConfigEntryDirPath::doLoad()
722 mFileNameRequester->setFileName(mEntry->urlValue().toLocalFile());
728 QGpgME::CryptoConfigEntry *entry,
732 : CryptoConfigEntryGUI(module, entry, entryName)
734 if (entry->argType() == QGpgME::CryptoConfigEntry::ArgType_None && entry->isList()) {
736 }
else if (entry->argType() == QGpgME::CryptoConfigEntry::ArgType_UInt) {
739 Q_ASSERT(entry->argType() == QGpgME::CryptoConfigEntry::ArgType_Int);
746 label->setBuddy(mNumInput);
750 if (entry->isReadOnly()) {
751 label->setEnabled(
false);
752 mNumInput->setEnabled(
false);
754 mNumInput->setMinimum(mKind == Int ? std::numeric_limits<int>::min() : 0);
755 mNumInput->setMaximum(std::numeric_limits<int>::max());
760void Kleo::CryptoConfigEntrySpinBox::doSave()
762 int value = mNumInput->value();
765 mEntry->setNumberOfTimesSet(value);
768 mEntry->setUIntValue(value);
771 mEntry->setIntValue(value);
776void Kleo::CryptoConfigEntrySpinBox::doLoad()
781 value = mEntry->numberOfTimesSet();
784 value = mEntry->uintValue();
787 value = mEntry->intValue();
790 mNumInput->setValue(value);
796 QGpgME::CryptoConfigEntry *entry,
800 : CryptoConfigEntryGUI(module, entry, entryName)
804 glay->
addWidget(mCheckBox, row, 1, 1, 2);
805 mCheckBox->setText(description());
806 if (entry->isReadOnly()) {
807 mCheckBox->setEnabled(
false);
813void Kleo::CryptoConfigEntryCheckBox::doSave()
815 mEntry->setBoolValue(mCheckBox->isChecked());
818void Kleo::CryptoConfigEntryCheckBox::doLoad()
820 mCheckBox->setChecked(mEntry->boolValue());
824 QGpgME::CryptoConfigEntry *entry,
828 : CryptoConfigEntryGUI(module, entry, entryName)
830 mLabel =
new QLabel(widget);
831 mPushButton =
new QPushButton(entry->isReadOnly() ?
i18n(
"Show...") :
i18n(
"Edit..."), widget);
835 label->setBuddy(mPushButton);
839 hlay->addWidget(mLabel, 1);
840 hlay->addWidget(mPushButton);
842 if (entry->isReadOnly()) {
843 mLabel->setEnabled(
false);
848void Kleo::CryptoConfigEntryLDAPURL::doLoad()
850 setURLList(mEntry->urlValueList());
853void Kleo::CryptoConfigEntryLDAPURL::doSave()
855 mEntry->setURLValueList(mURLList);
858void prepareURLCfgDialog(
QDialog *dialog, DirectoryServicesWidget *dirserv,
bool readOnly)
876 layout->addWidget(buttonBox);
880void Kleo::CryptoConfigEntryLDAPURL::slotOpenDialog()
884 QDialog dialog(mPushButton->parentWidget());
887 auto dirserv =
new DirectoryServicesWidget(&dialog);
889 prepareURLCfgDialog(&dialog, dirserv, mEntry->isReadOnly());
891 dirserv->setReadOnly(mEntry->isReadOnly());
893 std::vector<KeyserverConfig> servers;
894 std::transform(std::cbegin(mURLList), std::cend(mURLList), std::back_inserter(servers), [](
const auto &url) {
895 return KeyserverConfig::fromUrl(url);
897 dirserv->setKeyservers(servers);
901 const auto servers = dirserv->keyservers();
902 std::transform(std::begin(servers), std::end(servers), std::back_inserter(urls), [](
const auto &server) {
903 return server.toUrl();
910void Kleo::CryptoConfigEntryLDAPURL::setURLList(
const QList<QUrl> &urlList)
913 if (mURLList.isEmpty()) {
914 mLabel->setText(
i18n(
"None configured"));
916 mLabel->setText(
i18np(
"1 server configured",
"%1 servers configured", mURLList.count()));
920#include "moc_cryptoconfigmodule_p.cpp"
922#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 * load(const QObject *recvr, const char *slot, QObject *parent)
QString path(const QString &relativePath)
QAction * create(StandardAction id, const Receiver *recvr, Func slot, QObject *parent, std::optional< Qt::ConnectionType > connectionType=std::nullopt)
void currentIndexChanged(int index)
void addLayout(QLayout *layout, int row, int column, Qt::Alignment alignment)
int rowCount() const const
QIcon fromTheme(const QString &name)
void setPixmap(const QPixmap &)
void setContentsMargins(const QMargins &margins)
void textChanged(const QString &text)
void append(QList< T > &&value)
qsizetype size() const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
QRegularExpression fromWildcard(QStringView pattern, Qt::CaseSensitivity cs, WildcardConversionOptions options)
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)
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
QUrl fromLocalFile(const QString &localFile)