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