qt: Undeprecate API that I find useful
[gpgme.git] / lang / qt / src / qgpgmenewcryptoconfig.cpp
1 /*
2     qgpgmenewcryptoconfig.cpp
3
4     This file is part of qgpgme, the Qt API binding for gpgme
5     Copyright (c) 2010 Klarälvdalens Datakonsult AB
6     Copyright (c) 2016 by Bundesamt für Sicherheit in der Informationstechnik
7     Software engineering by Intevation GmbH
8
9     QGpgME is free software; you can redistribute it and/or
10     modify it under the terms of the GNU General Public License as
11     published by the Free Software Foundation; either version 2 of the
12     License, or (at your option) any later version.
13
14     QGpgME is distributed in the hope that it will be useful,
15     but WITHOUT ANY WARRANTY; without even the implied warranty of
16     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17     General Public License for more details.
18
19     You should have received a copy of the GNU General Public License
20     along with this program; if not, write to the Free Software
21     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
22
23     In addition, as a special exception, the copyright holders give
24     permission to link the code of this program with any edition of
25     the Qt library by Trolltech AS, Norway (or with modified versions
26     of Qt that use the same license as Qt), and distribute linked
27     combinations including the two.  You must obey the GNU General
28     Public License in all respects for all of the code used other than
29     Qt.  If you modify this file, you may extend this exception to
30     your version of the file, but you are not obligated to do so.  If
31     you do not wish to do so, delete this exception statement from
32     your version.
33 */
34
35 #ifdef HAVE_CONFIG_H
36  #include "config.h"
37 #endif
38
39 #include "qgpgmenewcryptoconfig.h"
40
41 #include <QDebug>
42 #include "gpgme_backend_debug.h"
43
44 #include <QFile>
45
46 #include "global.h"
47 #include "error.h"
48
49
50 #include <sstream>
51 #include <string>
52 #include <cassert>
53 #include <functional>
54
55 using namespace QGpgME;
56 using namespace GpgME;
57 using namespace GpgME::Configuration;
58
59 namespace
60 {
61 struct Select1St {
62     template <typename U, typename V>
63     const U &operator()(const std::pair<U, V> &p) const
64     {
65         return p.first;
66     }
67     template <typename U, typename V>
68     const U &operator()(const QPair<U, V> &p) const
69     {
70         return p.first;
71     }
72 };
73 }
74
75 // Just for the Q_ASSERT in the dtor. Not thread-safe, but who would
76 // have 2 threads talking to gpgconf anyway? :)
77 static bool s_duringClear = false;
78
79 QGpgMENewCryptoConfig::QGpgMENewCryptoConfig()
80     :  m_parsed(false)
81 {
82 }
83
84 QGpgMENewCryptoConfig::~QGpgMENewCryptoConfig()
85 {
86     clear();
87 }
88
89 void QGpgMENewCryptoConfig::reloadConfiguration(bool)
90 {
91     clear();
92
93     Error error;
94     const std::vector<Component> components = Component::load(error);
95 #ifndef NDEBUG
96     {
97         std::stringstream ss;
98         ss << "error: " << error
99            << "components:\n";
100         std::copy(components.begin(), components.end(),
101                   std::ostream_iterator<Component>(ss, "\n"));
102         qCDebug(GPGPME_BACKEND_LOG) << ss.str().c_str();
103     }
104 #endif
105 #if 0
106     TODO port?
107     if (error && showErrors) {
108         const QString wmsg = i18n("<qt>Failed to execute gpgconf:<p>%1</p></qt>", QString::fromLocal8Bit(error.asString()));
109         qCWarning(GPGPME_BACKEND_LOG) << wmsg; // to see it from test_cryptoconfig.cpp
110         KMessageBox::error(0, wmsg);
111     }
112 #endif
113     Q_FOREACH(const Component & c, components) {
114         const std::shared_ptr<QGpgMENewCryptoConfigComponent> comp(new QGpgMENewCryptoConfigComponent);
115         comp->setComponent(c);
116         m_componentsByName[ comp->name() ] = comp;
117     }
118     m_parsed = true;
119 }
120
121 QStringList QGpgMENewCryptoConfig::componentList() const
122 {
123     if (!m_parsed) {
124         const_cast<QGpgMENewCryptoConfig *>(this)->reloadConfiguration(true);
125     }
126     QStringList result;
127     std::transform(m_componentsByName.begin(), m_componentsByName.end(),
128                    std::back_inserter(result),
129                    mem_fn(&QGpgMENewCryptoConfigComponent::name));
130     return result;
131 }
132
133 QGpgMENewCryptoConfigComponent *QGpgMENewCryptoConfig::component(const QString &name) const
134 {
135     if (!m_parsed) {
136         const_cast<QGpgMENewCryptoConfig *>(this)->reloadConfiguration(false);
137     }
138     return m_componentsByName.value(name).get();
139 }
140
141 void QGpgMENewCryptoConfig::sync(bool runtime)
142 {
143     Q_FOREACH(const std::shared_ptr<QGpgMENewCryptoConfigComponent> &c, m_componentsByName)
144     c->sync(runtime);
145 }
146
147 void QGpgMENewCryptoConfig::clear()
148 {
149     s_duringClear = true;
150     m_componentsByName.clear();
151     s_duringClear = false;
152     m_parsed = false; // next call to componentList/component will need to run gpgconf again
153 }
154
155 ////
156
157 QGpgMENewCryptoConfigComponent::QGpgMENewCryptoConfigComponent()
158     : CryptoConfigComponent(),
159       m_component()
160 {
161
162 }
163
164 void QGpgMENewCryptoConfigComponent::setComponent(const Component &component)
165 {
166     m_component = component;
167     m_groupsByName.clear();
168
169     std::shared_ptr<QGpgMENewCryptoConfigGroup> group;
170
171     const std::vector<Option> options = m_component.options();
172     Q_FOREACH(const Option & o, options)
173     if (o.flags() & Group) {
174         if (group) {
175             m_groupsByName[group->name()] = group;
176         }
177         group.reset(new QGpgMENewCryptoConfigGroup(shared_from_this(), o));
178     } else if (group) {
179         const std::shared_ptr<QGpgMENewCryptoConfigEntry> entry(new QGpgMENewCryptoConfigEntry(group, o));
180         const QString name = entry->name();
181         group->m_entryNames.push_back(name);
182         group->m_entriesByName[name] = entry;
183     } else {
184         qCWarning(GPGPME_BACKEND_LOG) << "found no group for entry" << o.name() << "of component" << name();
185     }
186     if (group) {
187         m_groupsByName[group->name()] = group;
188     }
189
190 }
191
192 QGpgMENewCryptoConfigComponent::~QGpgMENewCryptoConfigComponent() {}
193
194 QString QGpgMENewCryptoConfigComponent::name() const
195 {
196     return QString::fromUtf8(m_component.name());
197 }
198
199 QString QGpgMENewCryptoConfigComponent::description() const
200 {
201     return QString::fromUtf8(m_component.description());
202 }
203
204 QStringList QGpgMENewCryptoConfigComponent::groupList() const
205 {
206     QStringList result;
207     result.reserve(m_groupsByName.size());
208     std::transform(m_groupsByName.begin(), m_groupsByName.end(),
209                    std::back_inserter(result),
210                    std::mem_fn(&QGpgMENewCryptoConfigGroup::name));
211     return result;
212 }
213
214 QGpgMENewCryptoConfigGroup *QGpgMENewCryptoConfigComponent::group(const QString &name) const
215 {
216     return m_groupsByName.value(name).get();
217 }
218
219 void QGpgMENewCryptoConfigComponent::sync(bool runtime)
220 {
221     Q_UNUSED(runtime) // runtime is always set by engine_gpgconf
222     if (const Error err = m_component.save()) {
223         qCWarning(GPGPME_BACKEND_LOG) << ":"
224             << "Error from gpgconf while saving configuration: %1"
225             << QString::fromLocal8Bit(err.asString());
226     }
227 }
228
229 ////
230
231 QGpgMENewCryptoConfigGroup::QGpgMENewCryptoConfigGroup(const std::shared_ptr<QGpgMENewCryptoConfigComponent> &comp, const Option &option)
232     : CryptoConfigGroup(),
233       m_component(comp),
234       m_option(option)
235 {
236 }
237
238 QGpgMENewCryptoConfigGroup::~QGpgMENewCryptoConfigGroup() {}
239
240 QString QGpgMENewCryptoConfigGroup::name() const
241 {
242     return QString::fromUtf8(m_option.name());
243 }
244
245 QString QGpgMENewCryptoConfigGroup::description() const
246 {
247     return QString::fromUtf8(m_option.description());
248 }
249
250 QString QGpgMENewCryptoConfigGroup::path() const
251 {
252     if (const std::shared_ptr<QGpgMENewCryptoConfigComponent> c = m_component.lock()) {
253         return c->name() + QLatin1Char('/') + name();
254     } else {
255         return QString();
256     }
257 }
258
259 CryptoConfigEntry::Level QGpgMENewCryptoConfigGroup::level() const
260 {
261     // two casts to make SunCC happy:
262     return static_cast<CryptoConfigEntry::Level>(static_cast<unsigned int>(m_option.level()));
263 }
264
265 QStringList QGpgMENewCryptoConfigGroup::entryList() const
266 {
267     return m_entryNames;
268 }
269
270 QGpgMENewCryptoConfigEntry *QGpgMENewCryptoConfigGroup::entry(const QString &name) const
271 {
272     return m_entriesByName.value(name).get();
273 }
274
275 static QString urlpart_encode(const QString &str)
276 {
277     QString enc(str);
278     enc.replace(QLatin1Char('%'), QStringLiteral("%25"));   // first!
279     enc.replace(QLatin1Char(':'), QStringLiteral("%3a"));
280     //qCDebug(GPGPME_BACKEND_LOG) <<"  urlpart_encode:" << str <<" ->" << enc;
281     return enc;
282 }
283
284 static QString urlpart_decode(const QString &str)
285 {
286     return QUrl::fromPercentEncoding(str.toLatin1());
287 }
288
289 // gpgconf arg type number -> NewCryptoConfigEntry arg type enum mapping
290 static QGpgME::CryptoConfigEntry::ArgType knownArgType(int argType, bool &ok)
291 {
292     ok = true;
293     switch (argType) {
294     case 0: // none
295         return QGpgME::CryptoConfigEntry::ArgType_None;
296     case 1: // string
297         return QGpgME::CryptoConfigEntry::ArgType_String;
298     case 2: // int32
299         return QGpgME::CryptoConfigEntry::ArgType_Int;
300     case 3: // uint32
301         return QGpgME::CryptoConfigEntry::ArgType_UInt;
302     case 32: // pathname
303         return QGpgME::CryptoConfigEntry::ArgType_Path;
304     case 33: // ldap server
305         return QGpgME::CryptoConfigEntry::ArgType_LDAPURL;
306     default:
307         ok = false;
308         return QGpgME::CryptoConfigEntry::ArgType_None;
309     }
310 }
311
312 QGpgMENewCryptoConfigEntry::QGpgMENewCryptoConfigEntry(const std::shared_ptr<QGpgMENewCryptoConfigGroup> &group, const Option &option)
313     : m_group(group), m_option(option)
314 {
315 }
316
317 #if 0
318 QVariant QGpgMENewCryptoConfigEntry::stringToValue(const QString &str, bool unescape) const
319 {
320     const bool isString = isStringType();
321
322     if (isList()) {
323         if (argType() == ArgType_None) {
324             bool ok = true;
325             const QVariant v = str.isEmpty() ? 0U : str.toUInt(&ok);
326             if (!ok) {
327                 qCWarning(GPGPME_BACKEND_LOG) << "list-of-none should have an unsigned int as value:" << str;
328             }
329             return v;
330         }
331         QList<QVariant> lst;
332         QStringList items = str.split(',', QString::SkipEmptyParts);
333         for (QStringList::const_iterator valit = items.constBegin(); valit != items.constEnd(); ++valit) {
334             QString val = *valit;
335             if (isString) {
336                 if (val.isEmpty()) {
337                     lst << QVariant(QString());
338                     continue;
339                 } else if (unescape) {
340                     if (val[0] != '"') { // see README.gpgconf
341                         qCWarning(GPGPME_BACKEND_LOG) << "String value should start with '\"' :" << val;
342                     }
343                     val = val.mid(1);
344                 }
345             }
346             lst << QVariant(unescape ? gpgconf_unescape(val) : val);
347         }
348         return lst;
349     } else { // not a list
350         QString val(str);
351         if (isString) {
352             if (val.isEmpty()) {
353                 return QVariant(QString());    // not set  [ok with lists too?]
354             } else if (unescape) {
355                 if (val[0] != '"') { // see README.gpgconf
356                     qCWarning(GPGPME_BACKEND_LOG) << "String value should start with '\"' :" << val;
357                 }
358                 val = val.mid(1);
359             }
360         }
361         return QVariant(unescape ? gpgconf_unescape(val) : val);
362     }
363 }
364 #endif
365
366 QGpgMENewCryptoConfigEntry::~QGpgMENewCryptoConfigEntry()
367 {
368 #ifndef NDEBUG
369     if (!s_duringClear && m_option.dirty())
370         qCWarning(GPGPME_BACKEND_LOG) << "Deleting a QGpgMENewCryptoConfigEntry that was modified (" << m_option.description() << ")"
371                                       << "You forgot to call sync() (to commit) or clear() (to discard)";
372 #endif
373 }
374
375 QString QGpgMENewCryptoConfigEntry::name() const
376 {
377     return QString::fromUtf8(m_option.name());
378 }
379
380 QString QGpgMENewCryptoConfigEntry::description() const
381 {
382     return QString::fromUtf8(m_option.description());
383 }
384
385 QString QGpgMENewCryptoConfigEntry::path() const
386 {
387     if (const std::shared_ptr<QGpgMENewCryptoConfigGroup> g = m_group.lock()) {
388         return g->path() + QLatin1Char('/') + name();
389     } else {
390         return QString();
391     }
392 }
393
394 bool QGpgMENewCryptoConfigEntry::isOptional() const
395 {
396     return m_option.flags() & Optional;
397 }
398
399 bool QGpgMENewCryptoConfigEntry::isReadOnly() const
400 {
401     return m_option.flags() & NoChange;
402 }
403
404 bool QGpgMENewCryptoConfigEntry::isList() const
405 {
406     return m_option.flags() & List;
407 }
408
409 bool QGpgMENewCryptoConfigEntry::isRuntime() const
410 {
411     return m_option.flags() & Runtime;
412 }
413
414 CryptoConfigEntry::Level QGpgMENewCryptoConfigEntry::level() const
415 {
416     // two casts to make SunCC happy:
417     return static_cast<Level>(static_cast<unsigned int>(m_option.level()));
418 }
419
420 CryptoConfigEntry::ArgType QGpgMENewCryptoConfigEntry::argType() const
421 {
422     bool ok = false;
423     const ArgType type = knownArgType(m_option.type(), ok);
424     if (ok) {
425         return type;
426     } else {
427         return knownArgType(m_option.alternateType(), ok);
428     }
429 }
430
431 bool QGpgMENewCryptoConfigEntry::isSet() const
432 {
433     return m_option.set();
434 }
435
436 bool QGpgMENewCryptoConfigEntry::boolValue() const
437 {
438     Q_ASSERT(m_option.alternateType() == NoType);
439     Q_ASSERT(!isList());
440     return m_option.currentValue().boolValue();
441 }
442
443 QString QGpgMENewCryptoConfigEntry::stringValue() const
444 {
445     //return toString( false );
446     Q_ASSERT(m_option.alternateType() == StringType);
447     Q_ASSERT(!isList());
448     return QString::fromUtf8(m_option.currentValue().stringValue());
449 }
450
451 int QGpgMENewCryptoConfigEntry::intValue() const
452 {
453     Q_ASSERT(m_option.alternateType() == IntegerType);
454     Q_ASSERT(!isList());
455     return m_option.currentValue().intValue();
456 }
457
458 unsigned int QGpgMENewCryptoConfigEntry::uintValue() const
459 {
460     Q_ASSERT(m_option.alternateType() == UnsignedIntegerType);
461     Q_ASSERT(!isList());
462     return m_option.currentValue().uintValue();
463 }
464
465 static QUrl parseURL(int mRealArgType, const QString &str)
466 {
467     if (mRealArgType == 33) {   // LDAP server
468         // The format is HOSTNAME:PORT:USERNAME:PASSWORD:BASE_DN
469         QStringList items = str.split(QLatin1Char(':'));
470         if (items.count() == 5) {
471             QStringList::const_iterator it = items.constBegin();
472             QUrl url;
473             url.setScheme(QStringLiteral("ldap"));
474             url.setHost(urlpart_decode(*it++));
475
476             bool ok;
477             const int port = (*it++).toInt(&ok);
478             if (ok) {
479                 url.setPort(port);
480             } else if (!it->isEmpty()) {
481                 qCWarning(GPGPME_BACKEND_LOG) << "parseURL: malformed LDAP server port, ignoring: \"" << *it << "\"";
482             }
483
484             const QString userName = urlpart_decode(*it++);
485             if (!userName.isEmpty()) {
486                 url.setUserName(userName);
487             }
488             const QString passWord = urlpart_decode(*it++);
489             if (!passWord.isEmpty()) {
490                 url.setPassword(passWord);
491             }
492             url.setQuery(urlpart_decode(*it));
493             return url;
494         } else {
495             qCWarning(GPGPME_BACKEND_LOG) << "parseURL: malformed LDAP server:" << str;
496         }
497     }
498     // other URLs : assume wellformed URL syntax.
499     return QUrl(str);
500 }
501
502 // The opposite of parseURL
503 static QString splitURL(int mRealArgType, const QUrl &url)
504 {
505     if (mRealArgType == 33) {   // LDAP server
506         // The format is HOSTNAME:PORT:USERNAME:PASSWORD:BASE_DN
507         Q_ASSERT(url.scheme() == QLatin1String("ldap"));
508         return urlpart_encode(url.host()) + QLatin1Char(':') +
509                (url.port() != -1 ? QString::number(url.port()) : QString()) + QLatin1Char(':') +     // -1 is used for default ports, omit
510                urlpart_encode(url.userName()) + QLatin1Char(':') +
511                urlpart_encode(url.password()) + QLatin1Char(':') +
512                urlpart_encode(url.query());
513     }
514     return url.path();
515 }
516
517 QUrl QGpgMENewCryptoConfigEntry::urlValue() const
518 {
519     const Type type = m_option.type();
520     Q_ASSERT(type == FilenameType || type == LdapServerType);
521     Q_ASSERT(!isList());
522     if (type == FilenameType) {
523         QUrl url;
524         url.setPath(QFile::decodeName(m_option.currentValue().stringValue()));
525         return url;
526     }
527     return parseURL(type, stringValue());
528 }
529
530 unsigned int QGpgMENewCryptoConfigEntry::numberOfTimesSet() const
531 {
532     Q_ASSERT(m_option.alternateType() == NoType);
533     Q_ASSERT(isList());
534     return m_option.currentValue().uintValue();
535 }
536
537 std::vector<int> QGpgMENewCryptoConfigEntry::intValueList() const
538 {
539     Q_ASSERT(m_option.alternateType() == IntegerType);
540     Q_ASSERT(isList());
541     return m_option.currentValue().intValues();
542 }
543
544 std::vector<unsigned int> QGpgMENewCryptoConfigEntry::uintValueList() const
545 {
546     Q_ASSERT(m_option.alternateType() == UnsignedIntegerType);
547     Q_ASSERT(isList());
548     return m_option.currentValue().uintValues();
549 }
550
551 QStringList QGpgMENewCryptoConfigEntry::stringValueList() const
552 {
553     Q_ASSERT(isList());
554     const Argument arg = m_option.currentValue();
555     const std::vector<const char *> values = arg.stringValues();
556     QStringList ret;
557     for(const char *value: values) {
558         ret << QString::fromUtf8(value);
559     }
560     return ret;
561 }
562
563 QList<QUrl> QGpgMENewCryptoConfigEntry::urlValueList() const
564 {
565     const Type type = m_option.type();
566     Q_ASSERT(type == FilenameType || type == LdapServerType);
567     Q_ASSERT(isList());
568     const Argument arg = m_option.currentValue();
569     const std::vector<const char *> values = arg.stringValues();
570     QList<QUrl> ret;
571     Q_FOREACH(const char *value, values)
572     if (type == FilenameType) {
573         QUrl url;
574         url.setPath(QFile::decodeName(value));
575         ret << url;
576     } else {
577         ret << parseURL(type, QString::fromUtf8(value));
578     }
579     return ret;
580 }
581
582 void QGpgMENewCryptoConfigEntry::resetToDefault()
583 {
584     m_option.resetToDefaultValue();
585 }
586
587 void QGpgMENewCryptoConfigEntry::setBoolValue(bool b)
588 {
589     Q_ASSERT(m_option.alternateType() == NoType);
590     Q_ASSERT(!isList());
591     // A "no arg" option is either set or not set.
592     // Being set means createNoneArgument(), being unset means resetToDefault()
593     m_option.setNewValue(m_option.createNoneArgument(b));
594 }
595
596 void QGpgMENewCryptoConfigEntry::setStringValue(const QString &str)
597 {
598     Q_ASSERT(m_option.alternateType() == StringType);
599     Q_ASSERT(!isList());
600     const Type type = m_option.type();
601     // When setting a string to empty (and there's no default), we need to act like resetToDefault
602     // Otherwise we try e.g. "ocsp-responder:0:" and gpgconf answers:
603     // "gpgconf: argument required for option ocsp-responder"
604     if (str.isEmpty() && !isOptional()) {
605         m_option.resetToDefaultValue();
606     } else if (type == FilenameType) {
607         m_option.setNewValue(m_option.createStringArgument(QFile::encodeName(str).constData()));
608     } else {
609         m_option.setNewValue(m_option.createStringArgument(str.toUtf8().constData()));
610     }
611 }
612
613 void QGpgMENewCryptoConfigEntry::setIntValue(int i)
614 {
615     Q_ASSERT(m_option.alternateType() == IntegerType);
616     Q_ASSERT(!isList());
617     m_option.setNewValue(m_option.createIntArgument(i));
618 }
619
620 void QGpgMENewCryptoConfigEntry::setUIntValue(unsigned int i)
621 {
622     Q_ASSERT(m_option.alternateType() == UnsignedIntegerType);
623     Q_ASSERT(!isList());
624     m_option.setNewValue(m_option.createUIntArgument(i));
625 }
626
627 void QGpgMENewCryptoConfigEntry::setURLValue(const QUrl &url)
628 {
629     const Type type = m_option.type();
630     Q_ASSERT(type == FilenameType || type == LdapServerType);
631     Q_ASSERT(!isList());
632     const QString str = splitURL(type, url);
633     // cf. setStringValue()
634     if (str.isEmpty() && !isOptional()) {
635         m_option.resetToDefaultValue();
636     } else if (type == FilenameType) {
637         m_option.setNewValue(m_option.createStringArgument(QFile::encodeName(str).constData()));
638     } else {
639         m_option.setNewValue(m_option.createStringArgument(str.toUtf8().constData()));
640     }
641 }
642
643 void QGpgMENewCryptoConfigEntry::setNumberOfTimesSet(unsigned int i)
644 {
645     Q_ASSERT(m_option.alternateType() == NoType);
646     Q_ASSERT(isList());
647     m_option.setNewValue(m_option.createNoneListArgument(i));
648 }
649
650 void QGpgMENewCryptoConfigEntry::setIntValueList(const std::vector<int> &lst)
651 {
652     Q_ASSERT(m_option.alternateType() == IntegerType);
653     Q_ASSERT(isList());
654     m_option.setNewValue(m_option.createIntListArgument(lst));
655 }
656
657 void QGpgMENewCryptoConfigEntry::setUIntValueList(const std::vector<unsigned int> &lst)
658 {
659     Q_ASSERT(m_option.alternateType() == UnsignedIntegerType);
660     Q_ASSERT(isList());
661     m_option.setNewValue(m_option.createUIntListArgument(lst));
662 }
663
664 void QGpgMENewCryptoConfigEntry::setURLValueList(const QList<QUrl> &urls)
665 {
666     const Type type = m_option.type();
667     Q_ASSERT(m_option.alternateType() == StringType);
668     Q_ASSERT(isList());
669     std::vector<std::string> values;
670     values.reserve(urls.size());
671     Q_FOREACH (const QUrl &url, urls)
672         if (type == FilenameType) {
673             values.push_back(QFile::encodeName(url.path()).constData());
674         } else {
675             values.push_back(splitURL(type, url).toUtf8().constData());
676         }
677     m_option.setNewValue(m_option.createStringListArgument(values));
678 }
679
680 bool QGpgMENewCryptoConfigEntry::isDirty() const
681 {
682     return m_option.dirty();
683 }
684
685 #if 0
686 QString QGpgMENewCryptoConfigEntry::toString(bool escape) const
687 {
688     // Basically the opposite of stringToValue
689     if (isStringType()) {
690         if (mValue.isNull()) {
691             return QString();
692         } else if (isList()) { // string list
693             QStringList lst = mValue.toStringList();
694             if (escape) {
695                 for (QStringList::iterator it = lst.begin(); it != lst.end(); ++it) {
696                     if (!(*it).isNull()) {
697                         *it = gpgconf_escape(*it).prepend("\"");
698                     }
699                 }
700             }
701             QString res = lst.join(",");
702             //qCDebug(GPGPME_BACKEND_LOG) <<"toString:" << res;
703             return res;
704         } else { // normal string
705             QString res = mValue.toString();
706             if (escape) {
707                 res = gpgconf_escape(res).prepend("\"");
708             }
709             return res;
710         }
711     }
712     if (!isList()) { // non-list non-string
713         if (mArgType == ArgType_None) {
714             return mValue.toBool() ? QString::fromLatin1("1") : QString();
715         } else { // some int
716             Q_ASSERT(mArgType == ArgType_Int || mArgType == ArgType_UInt);
717             return mValue.toString(); // int to string conversion
718         }
719     }
720
721     // Lists (of other types than strings)
722     if (mArgType == ArgType_None) {
723         return QString::number(numberOfTimesSet());
724     }
725     QStringList ret;
726     QList<QVariant> lst = mValue.toList();
727     for (QList<QVariant>::const_iterator it = lst.constBegin(); it != lst.constEnd(); ++it) {
728         ret << (*it).toString(); // QVariant does the conversion
729     }
730     return ret.join(",");
731 }
732
733 QString QGpgMENewCryptoConfigEntry::outputString() const
734 {
735     Q_ASSERT(mSet);
736     return toString(true);
737 }
738
739 bool QGpgMENewCryptoConfigEntry::isStringType() const
740 {
741     return (mArgType == QGpgME::NewCryptoConfigEntry::ArgType_String
742             || mArgType == QGpgME::NewCryptoConfigEntry::ArgType_Path
743             || mArgType == QGpgME::NewCryptoConfigEntry::ArgType_URL
744             || mArgType == QGpgME::NewCryptoConfigEntry::ArgType_LDAPURL);
745 }
746
747 void QGpgMENewCryptoConfigEntry::setDirty(bool b)
748 {
749     mDirty = b;
750 }
751 #endif