qt: Fix filename handling in cryptoconfig
[gpgme.git] / lang / qt / src / dn.cpp
1 /*
2     dn.cpp
3
4     This file is part of qgpgme, the Qt API binding for gpgme
5     Copyright (c) 2004 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 "dn.h"
40
41 #include <gpg-error.h>
42
43 static const struct {
44     const char *name;
45     const char *oid;
46 } oidmap[] = {
47     // keep them ordered by oid:
48     { "SP", "ST" }, // hack to show the Sphinx-required/desired SP for
49     // StateOrProvince, otherwise known as ST or even S
50     { "NameDistinguisher", "0.2.262.1.10.7.20" },
51     { "EMAIL", "1.2.840.113549.1.9.1" },
52     { "SN", "2.5.4.4" },
53     { "SerialNumber", "2.5.4.5" },
54     { "T", "2.5.4.12" },
55     { "D", "2.5.4.13" },
56     { "BC", "2.5.4.15" },
57     { "ADDR", "2.5.4.16" },
58     { "PC", "2.5.4.17" },
59     { "GN", "2.5.4.42" },
60     { "Pseudo", "2.5.4.65" },
61 };
62 static const unsigned int numOidMaps = sizeof oidmap / sizeof * oidmap;
63
64 class QGpgME::DN::Private
65 {
66 public:
67     Private() : mRefCount(0) {}
68     Private(const Private &other)
69         : attributes(other.attributes),
70           reorderedAttributes(other.reorderedAttributes),
71           order{"CN", "L", "_X_", "OU", "O", "C"},
72           mRefCount(0)
73     {
74     }
75
76     int ref()
77     {
78         return ++mRefCount;
79     }
80
81     int unref()
82     {
83         if (--mRefCount <= 0) {
84             delete this;
85             return 0;
86         } else {
87             return mRefCount;
88         }
89     }
90
91     int refCount() const
92     {
93         return mRefCount;
94     }
95
96     DN::Attribute::List attributes;
97     DN::Attribute::List reorderedAttributes;
98     QStringList order;
99 private:
100     int mRefCount;
101 };
102
103 namespace
104 {
105 struct DnPair {
106     char *key;
107     char *value;
108 };
109 }
110
111 // copied from CryptPlug and adapted to work on DN::Attribute::List:
112
113 #define digitp(p)   (*(p) >= '0' && *(p) <= '9')
114 #define hexdigitp(a) (digitp (a)                     \
115                       || (*(a) >= 'A' && *(a) <= 'F')  \
116                       || (*(a) >= 'a' && *(a) <= 'f'))
117 #define xtoi_1(p)   (*(p) <= '9'? (*(p)- '0'): \
118                      *(p) <= 'F'? (*(p)-'A'+10):(*(p)-'a'+10))
119 #define xtoi_2(p)   ((xtoi_1(p) * 16) + xtoi_1((p)+1))
120
121 static char *
122 trim_trailing_spaces(char *string)
123 {
124     char *p, *mark;
125
126     for (mark = NULL, p = string; *p; p++) {
127         if (isspace(*p)) {
128             if (!mark) {
129                 mark = p;
130             }
131         } else {
132             mark = NULL;
133         }
134     }
135     if (mark) {
136         *mark = '\0';
137     }
138
139     return string;
140 }
141
142 /* Parse a DN and return an array-ized one.  This is not a validating
143    parser and it does not support any old-stylish syntax; gpgme is
144    expected to return only rfc2253 compatible strings. */
145 static const unsigned char *
146 parse_dn_part(DnPair *array, const unsigned char *string)
147 {
148     const unsigned char *s, *s1;
149     size_t n;
150     char *p;
151
152     /* parse attributeType */
153     for (s = string + 1; *s && *s != '='; s++)
154         ;
155     if (!*s) {
156         return NULL;    /* error */
157     }
158     n = s - string;
159     if (!n) {
160         return NULL;    /* empty key */
161     }
162     p = (char *)malloc(n + 1);
163
164     memcpy(p, string, n);
165     p[n] = 0;
166     trim_trailing_spaces((char *)p);
167     // map OIDs to their names:
168     for (unsigned int i = 0; i < numOidMaps; ++i)
169         if (!strcasecmp((char *)p, oidmap[i].oid)) {
170             free(p);
171             gpgrt_asprintf(&p, "%s", oidmap[i].name);
172             break;
173         }
174     array->key = p;
175     string = s + 1;
176
177     if (*string == '#') {
178         /* hexstring */
179         string++;
180         for (s = string; hexdigitp(s); s++) {
181             s++;
182         }
183         n = s - string;
184         if (!n || (n & 1)) {
185             return NULL;    /* empty or odd number of digits */
186         }
187         n /= 2;
188         array->value = p = (char *)malloc(n + 1);
189
190         for (s1 = string; n; s1 += 2, n--) {
191             *p++ = xtoi_2(s1);
192         }
193         *p = 0;
194     } else {
195         /* regular v3 quoted string */
196         for (n = 0, s = string; *s; s++) {
197             if (*s == '\\') {
198                 /* pair */
199                 s++;
200                 if (*s == ',' || *s == '=' || *s == '+'
201                         || *s == '<' || *s == '>' || *s == '#' || *s == ';'
202                         || *s == '\\' || *s == '\"' || *s == ' ') {
203                     n++;
204                 } else if (hexdigitp(s) && hexdigitp(s + 1)) {
205                     s++;
206                     n++;
207                 } else {
208                     return NULL;    /* invalid escape sequence */
209                 }
210             } else if (*s == '\"') {
211                 return NULL;    /* invalid encoding */
212             } else if (*s == ',' || *s == '=' || *s == '+'
213                        || *s == '<' || *s == '>' || *s == '#' || *s == ';') {
214                 break;
215             } else {
216                 n++;
217             }
218         }
219
220         array->value = p = (char *)malloc(n + 1);
221
222         for (s = string; n; s++, n--) {
223             if (*s == '\\') {
224                 s++;
225                 if (hexdigitp(s)) {
226                     *p++ = xtoi_2(s);
227                     s++;
228                 } else {
229                     *p++ = *s;
230                 }
231             } else {
232                 *p++ = *s;
233             }
234         }
235         *p = 0;
236     }
237     return s;
238 }
239
240 /* Parse a DN and return an array-ized one.  This is not a validating
241    parser and it does not support any old-stylish syntax; gpgme is
242    expected to return only rfc2253 compatible strings. */
243 static QGpgME::DN::Attribute::List
244 parse_dn(const unsigned char *string)
245 {
246     if (!string) {
247         return QVector<QGpgME::DN::Attribute>();
248     }
249
250     QVector<QGpgME::DN::Attribute> result;
251     while (*string) {
252         while (*string == ' ') {
253             string++;
254         }
255         if (!*string) {
256             break;    /* ready */
257         }
258
259         DnPair pair = { 0, 0 };
260         string = parse_dn_part(&pair, string);
261         if (!string) {
262             goto failure;
263         }
264         if (pair.key && pair.value)
265             result.push_back(QGpgME::DN::Attribute(QString::fromUtf8(pair.key),
266                                                  QString::fromUtf8(pair.value)));
267         free(pair.key);
268         free(pair.value);
269
270         while (*string == ' ') {
271             string++;
272         }
273         if (*string && *string != ',' && *string != ';' && *string != '+') {
274             goto failure;    /* invalid delimiter */
275         }
276         if (*string) {
277             string++;
278         }
279     }
280     return result;
281
282 failure:
283     return QVector<QGpgME::DN::Attribute>();
284 }
285
286 static QVector<QGpgME::DN::Attribute>
287 parse_dn(const QString &dn)
288 {
289     return parse_dn((const unsigned char *)dn.toUtf8().data());
290 }
291
292 static QString dn_escape(const QString &s)
293 {
294     QString result;
295     for (unsigned int i = 0, end = s.length(); i != end; ++i) {
296         const QChar ch = s[i];
297         switch (ch.unicode()) {
298         case ',':
299         case '+':
300         case '"':
301         case '\\':
302         case '<':
303         case '>':
304         case ';':
305             result += QLatin1Char('\\');
306         // fall through
307         default:
308             result += ch;
309         }
310     }
311     return result;
312 }
313
314 static QString
315 serialise(const QVector<QGpgME::DN::Attribute> &dn, const QString &sep)
316 {
317     QStringList result;
318     for (QVector<QGpgME::DN::Attribute>::const_iterator it = dn.begin(); it != dn.end(); ++it)
319         if (!(*it).name().isEmpty() && !(*it).value().isEmpty()) {
320             result.push_back((*it).name().trimmed() + QLatin1Char('=') + dn_escape((*it).value().trimmed()));
321         }
322     return result.join(sep);
323 }
324
325 static QGpgME::DN::Attribute::List
326 reorder_dn(const QGpgME::DN::Attribute::List &dn, const QStringList &attrOrder)
327 {
328     QGpgME::DN::Attribute::List unknownEntries;
329     QGpgME::DN::Attribute::List result;
330     unknownEntries.reserve(dn.size());
331     result.reserve(dn.size());
332
333     // find all unknown entries in their order of appearance
334     for (QGpgME::DN::const_iterator it = dn.begin(); it != dn.end(); ++it)
335         if (!attrOrder.contains((*it).name())) {
336             unknownEntries.push_back(*it);
337         }
338
339     // process the known attrs in the desired order
340     for (QStringList::const_iterator oit = attrOrder.begin(); oit != attrOrder.end(); ++oit)
341         if (*oit == QLatin1String("_X_")) {
342             // insert the unknown attrs
343             std::copy(unknownEntries.begin(), unknownEntries.end(),
344                       std::back_inserter(result));
345             unknownEntries.clear(); // don't produce dup's
346         } else {
347             for (QGpgME::DN::const_iterator dnit = dn.begin(); dnit != dn.end(); ++dnit)
348                 if ((*dnit).name() == *oit) {
349                     result.push_back(*dnit);
350                 }
351         }
352
353     return result;
354 }
355
356 //
357 //
358 // class DN
359 //
360 //
361
362 QGpgME::DN::DN()
363 {
364     d = new Private();
365     d->ref();
366 }
367
368 QGpgME::DN::DN(const QString &dn)
369 {
370     d = new Private();
371     d->ref();
372     d->attributes = parse_dn(dn);
373 }
374
375 QGpgME::DN::DN(const char *utf8DN)
376 {
377     d = new Private();
378     d->ref();
379     if (utf8DN) {
380         d->attributes = parse_dn((const unsigned char *)utf8DN);
381     }
382 }
383
384 QGpgME::DN::DN(const DN &other)
385     : d(other.d)
386 {
387     if (d) {
388         d->ref();
389     }
390 }
391
392 QGpgME::DN::~DN()
393 {
394     if (d) {
395         d->unref();
396     }
397 }
398
399 const QGpgME::DN &QGpgME::DN::operator=(const DN &that)
400 {
401     if (this->d == that.d) {
402         return *this;
403     }
404
405     if (that.d) {
406         that.d->ref();
407     }
408     if (this->d) {
409         this->d->unref();
410     }
411
412     this->d = that.d;
413
414     return *this;
415 }
416
417 QString QGpgME::DN::prettyDN() const
418 {
419     if (!d) {
420         return QString();
421     }
422     if (d->reorderedAttributes.empty()) {
423         d->reorderedAttributes = reorder_dn(d->attributes, d->order);
424     }
425     return serialise(d->reorderedAttributes, QStringLiteral(","));
426 }
427
428 QString QGpgME::DN::dn() const
429 {
430     return d ? serialise(d->attributes, QStringLiteral(",")) : QString();
431 }
432
433 QString QGpgME::DN::dn(const QString &sep) const
434 {
435     return d ? serialise(d->attributes, sep) : QString();
436 }
437
438 // static
439 QString QGpgME::DN::escape(const QString &value)
440 {
441     return dn_escape(value);
442 }
443
444 void QGpgME::DN::detach()
445 {
446     if (!d) {
447         d = new QGpgME::DN::Private();
448         d->ref();
449     } else if (d->refCount() > 1) {
450         QGpgME::DN::Private *d_save = d;
451         d = new QGpgME::DN::Private(*d);
452         d->ref();
453         d_save->unref();
454     }
455 }
456
457 void QGpgME::DN::append(const Attribute &attr)
458 {
459     detach();
460     d->attributes.push_back(attr);
461     d->reorderedAttributes.clear();
462 }
463
464 QString QGpgME::DN::operator[](const QString &attr) const
465 {
466     if (!d) {
467         return QString();
468     }
469     const QString attrUpper = attr.toUpper();
470     for (QVector<Attribute>::const_iterator it = d->attributes.constBegin();
471             it != d->attributes.constEnd(); ++it)
472         if ((*it).name() == attrUpper) {
473             return (*it).value();
474         }
475     return QString();
476 }
477
478 static QVector<QGpgME::DN::Attribute> empty;
479
480 QGpgME::DN::const_iterator QGpgME::DN::begin() const
481 {
482     return d ? d->attributes.constBegin() : empty.constBegin();
483 }
484
485 QGpgME::DN::const_iterator QGpgME::DN::end() const
486 {
487     return d ? d->attributes.constEnd() : empty.constEnd();
488 }
489
490 void QGpgME::DN::setAttributeOrder (const QStringList &order) const
491 {
492     d->order = order;
493 }
494
495 const QStringList & QGpgME::DN::attributeOrder () const
496 {
497     return d->order;
498 }