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