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