Blame lang/qt/src/dn.cpp

Packit d7e8d0
/*
Packit d7e8d0
    dn.cpp
Packit d7e8d0
Packit d7e8d0
    This file is part of qgpgme, the Qt API binding for gpgme
Packit d7e8d0
    Copyright (c) 2004 Klarälvdalens Datakonsult AB
Packit d7e8d0
    Copyright (c) 2016 by Bundesamt für Sicherheit in der Informationstechnik
Packit d7e8d0
    Software engineering by Intevation GmbH
Packit d7e8d0
Packit d7e8d0
    QGpgME is free software; you can redistribute it and/or
Packit d7e8d0
    modify it under the terms of the GNU General Public License as
Packit d7e8d0
    published by the Free Software Foundation; either version 2 of the
Packit d7e8d0
    License, or (at your option) any later version.
Packit d7e8d0
Packit d7e8d0
    QGpgME is distributed in the hope that it will be useful,
Packit d7e8d0
    but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit d7e8d0
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit d7e8d0
    General Public License for more details.
Packit d7e8d0
Packit d7e8d0
    You should have received a copy of the GNU General Public License
Packit d7e8d0
    along with this program; if not, write to the Free Software
Packit d7e8d0
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
Packit d7e8d0
Packit d7e8d0
    In addition, as a special exception, the copyright holders give
Packit d7e8d0
    permission to link the code of this program with any edition of
Packit d7e8d0
    the Qt library by Trolltech AS, Norway (or with modified versions
Packit d7e8d0
    of Qt that use the same license as Qt), and distribute linked
Packit d7e8d0
    combinations including the two.  You must obey the GNU General
Packit d7e8d0
    Public License in all respects for all of the code used other than
Packit d7e8d0
    Qt.  If you modify this file, you may extend this exception to
Packit d7e8d0
    your version of the file, but you are not obligated to do so.  If
Packit d7e8d0
    you do not wish to do so, delete this exception statement from
Packit d7e8d0
    your version.
Packit d7e8d0
*/
Packit d7e8d0
Packit d7e8d0
#ifdef HAVE_CONFIG_H
Packit d7e8d0
 #include "config.h"
Packit d7e8d0
#endif
Packit d7e8d0
Packit d7e8d0
#include "dn.h"
Packit d7e8d0
Packit d7e8d0
#include <gpg-error.h>
Packit d7e8d0
Packit d7e8d0
static const struct {
Packit d7e8d0
    const char *name;
Packit d7e8d0
    const char *oid;
Packit d7e8d0
} oidmap[] = {
Packit d7e8d0
    // keep them ordered by oid:
Packit d7e8d0
    { "SP", "ST" }, // hack to show the Sphinx-required/desired SP for
Packit d7e8d0
    // StateOrProvince, otherwise known as ST or even S
Packit d7e8d0
    { "NameDistinguisher", "0.2.262.1.10.7.20" },
Packit d7e8d0
    { "EMAIL", "1.2.840.113549.1.9.1" },
Packit d7e8d0
    { "SN", "2.5.4.4" },
Packit d7e8d0
    { "SerialNumber", "2.5.4.5" },
Packit d7e8d0
    { "T", "2.5.4.12" },
Packit d7e8d0
    { "D", "2.5.4.13" },
Packit d7e8d0
    { "BC", "2.5.4.15" },
Packit d7e8d0
    { "ADDR", "2.5.4.16" },
Packit d7e8d0
    { "PC", "2.5.4.17" },
Packit d7e8d0
    { "GN", "2.5.4.42" },
Packit d7e8d0
    { "Pseudo", "2.5.4.65" },
Packit d7e8d0
};
Packit d7e8d0
static const unsigned int numOidMaps = sizeof oidmap / sizeof * oidmap;
Packit d7e8d0
Packit d7e8d0
class QGpgME::DN::Private
Packit d7e8d0
{
Packit d7e8d0
public:
Packit d7e8d0
    Private() : mRefCount(0) {}
Packit d7e8d0
    Private(const Private &other)
Packit d7e8d0
        : attributes(other.attributes),
Packit d7e8d0
          reorderedAttributes(other.reorderedAttributes),
Packit d7e8d0
          order{"CN", "L", "_X_", "OU", "O", "C"},
Packit d7e8d0
          mRefCount(0)
Packit d7e8d0
    {
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
    int ref()
Packit d7e8d0
    {
Packit d7e8d0
        return ++mRefCount;
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
    int unref()
Packit d7e8d0
    {
Packit d7e8d0
        if (--mRefCount <= 0) {
Packit d7e8d0
            delete this;
Packit d7e8d0
            return 0;
Packit d7e8d0
        } else {
Packit d7e8d0
            return mRefCount;
Packit d7e8d0
        }
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
    int refCount() const
Packit d7e8d0
    {
Packit d7e8d0
        return mRefCount;
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
    DN::Attribute::List attributes;
Packit d7e8d0
    DN::Attribute::List reorderedAttributes;
Packit d7e8d0
    QStringList order;
Packit d7e8d0
private:
Packit d7e8d0
    int mRefCount;
Packit d7e8d0
};
Packit d7e8d0
Packit d7e8d0
namespace
Packit d7e8d0
{
Packit d7e8d0
struct DnPair {
Packit d7e8d0
    char *key;
Packit d7e8d0
    char *value;
Packit d7e8d0
};
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
// copied from CryptPlug and adapted to work on DN::Attribute::List:
Packit d7e8d0
Packit d7e8d0
#define digitp(p)   (*(p) >= '0' && *(p) <= '9')
Packit d7e8d0
#define hexdigitp(a) (digitp (a)                     \
Packit d7e8d0
                      || (*(a) >= 'A' && *(a) <= 'F')  \
Packit d7e8d0
                      || (*(a) >= 'a' && *(a) <= 'f'))
Packit d7e8d0
#define xtoi_1(p)   (*(p) <= '9'? (*(p)- '0'): \
Packit d7e8d0
                     *(p) <= 'F'? (*(p)-'A'+10):(*(p)-'a'+10))
Packit d7e8d0
#define xtoi_2(p)   ((xtoi_1(p) * 16) + xtoi_1((p)+1))
Packit d7e8d0
Packit d7e8d0
static char *
Packit d7e8d0
trim_trailing_spaces(char *string)
Packit d7e8d0
{
Packit d7e8d0
    char *p, *mark;
Packit d7e8d0
Packit d7e8d0
    for (mark = NULL, p = string; *p; p++) {
Packit d7e8d0
        if (isspace(*p)) {
Packit d7e8d0
            if (!mark) {
Packit d7e8d0
                mark = p;
Packit d7e8d0
            }
Packit d7e8d0
        } else {
Packit d7e8d0
            mark = NULL;
Packit d7e8d0
        }
Packit d7e8d0
    }
Packit d7e8d0
    if (mark) {
Packit d7e8d0
        *mark = '\0';
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
    return string;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
/* Parse a DN and return an array-ized one.  This is not a validating
Packit d7e8d0
   parser and it does not support any old-stylish syntax; gpgme is
Packit d7e8d0
   expected to return only rfc2253 compatible strings. */
Packit d7e8d0
static const unsigned char *
Packit d7e8d0
parse_dn_part(DnPair *array, const unsigned char *string)
Packit d7e8d0
{
Packit d7e8d0
    const unsigned char *s, *s1;
Packit d7e8d0
    size_t n;
Packit d7e8d0
    char *p;
Packit d7e8d0
Packit d7e8d0
    /* parse attributeType */
Packit d7e8d0
    for (s = string + 1; *s && *s != '='; s++)
Packit d7e8d0
        ;
Packit d7e8d0
    if (!*s) {
Packit d7e8d0
        return NULL;    /* error */
Packit d7e8d0
    }
Packit d7e8d0
    n = s - string;
Packit d7e8d0
    if (!n) {
Packit d7e8d0
        return NULL;    /* empty key */
Packit d7e8d0
    }
Packit d7e8d0
    p = (char *)malloc(n + 1);
Packit d7e8d0
Packit d7e8d0
    memcpy(p, string, n);
Packit d7e8d0
    p[n] = 0;
Packit d7e8d0
    trim_trailing_spaces((char *)p);
Packit d7e8d0
    // map OIDs to their names:
Packit d7e8d0
    for (unsigned int i = 0; i < numOidMaps; ++i)
Packit d7e8d0
        if (!strcasecmp((char *)p, oidmap[i].oid)) {
Packit d7e8d0
            free(p);
Packit d7e8d0
            gpgrt_asprintf(&p, "%s", oidmap[i].name);
Packit d7e8d0
            break;
Packit d7e8d0
        }
Packit d7e8d0
    array->key = p;
Packit d7e8d0
    string = s + 1;
Packit d7e8d0
Packit d7e8d0
    if (*string == '#') {
Packit d7e8d0
        /* hexstring */
Packit d7e8d0
        string++;
Packit d7e8d0
        for (s = string; hexdigitp(s); s++) {
Packit d7e8d0
            s++;
Packit d7e8d0
        }
Packit d7e8d0
        n = s - string;
Packit d7e8d0
        if (!n || (n & 1)) {
Packit d7e8d0
            return NULL;    /* empty or odd number of digits */
Packit d7e8d0
        }
Packit d7e8d0
        n /= 2;
Packit d7e8d0
        array->value = p = (char *)malloc(n + 1);
Packit d7e8d0
Packit d7e8d0
        for (s1 = string; n; s1 += 2, n--) {
Packit d7e8d0
            *p++ = xtoi_2(s1);
Packit d7e8d0
        }
Packit d7e8d0
        *p = 0;
Packit d7e8d0
    } else {
Packit d7e8d0
        /* regular v3 quoted string */
Packit d7e8d0
        for (n = 0, s = string; *s; s++) {
Packit d7e8d0
            if (*s == '\\') {
Packit d7e8d0
                /* pair */
Packit d7e8d0
                s++;
Packit d7e8d0
                if (*s == ',' || *s == '=' || *s == '+'
Packit d7e8d0
                        || *s == '<' || *s == '>' || *s == '#' || *s == ';'
Packit d7e8d0
                        || *s == '\\' || *s == '\"' || *s == ' ') {
Packit d7e8d0
                    n++;
Packit d7e8d0
                } else if (hexdigitp(s) && hexdigitp(s + 1)) {
Packit d7e8d0
                    s++;
Packit d7e8d0
                    n++;
Packit d7e8d0
                } else {
Packit d7e8d0
                    return NULL;    /* invalid escape sequence */
Packit d7e8d0
                }
Packit d7e8d0
            } else if (*s == '\"') {
Packit d7e8d0
                return NULL;    /* invalid encoding */
Packit d7e8d0
            } else if (*s == ',' || *s == '=' || *s == '+'
Packit d7e8d0
                       || *s == '<' || *s == '>' || *s == '#' || *s == ';') {
Packit d7e8d0
                break;
Packit d7e8d0
            } else {
Packit d7e8d0
                n++;
Packit d7e8d0
            }
Packit d7e8d0
        }
Packit d7e8d0
Packit d7e8d0
        array->value = p = (char *)malloc(n + 1);
Packit d7e8d0
Packit d7e8d0
        for (s = string; n; s++, n--) {
Packit d7e8d0
            if (*s == '\\') {
Packit d7e8d0
                s++;
Packit d7e8d0
                if (hexdigitp(s)) {
Packit d7e8d0
                    *p++ = xtoi_2(s);
Packit d7e8d0
                    s++;
Packit d7e8d0
                } else {
Packit d7e8d0
                    *p++ = *s;
Packit d7e8d0
                }
Packit d7e8d0
            } else {
Packit d7e8d0
                *p++ = *s;
Packit d7e8d0
            }
Packit d7e8d0
        }
Packit d7e8d0
        *p = 0;
Packit d7e8d0
    }
Packit d7e8d0
    return s;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
/* Parse a DN and return an array-ized one.  This is not a validating
Packit d7e8d0
   parser and it does not support any old-stylish syntax; gpgme is
Packit d7e8d0
   expected to return only rfc2253 compatible strings. */
Packit d7e8d0
static QGpgME::DN::Attribute::List
Packit d7e8d0
parse_dn(const unsigned char *string)
Packit d7e8d0
{
Packit d7e8d0
    if (!string) {
Packit d7e8d0
        return QVector<QGpgME::DN::Attribute>();
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
    QVector<QGpgME::DN::Attribute> result;
Packit d7e8d0
    while (*string) {
Packit d7e8d0
        while (*string == ' ') {
Packit d7e8d0
            string++;
Packit d7e8d0
        }
Packit d7e8d0
        if (!*string) {
Packit d7e8d0
            break;    /* ready */
Packit d7e8d0
        }
Packit d7e8d0
Packit Service 30b792
        DnPair pair = { nullptr, nullptr };
Packit d7e8d0
        string = parse_dn_part(&pair, string);
Packit d7e8d0
        if (!string) {
Packit d7e8d0
            goto failure;
Packit d7e8d0
        }
Packit d7e8d0
        if (pair.key && pair.value)
Packit d7e8d0
            result.push_back(QGpgME::DN::Attribute(QString::fromUtf8(pair.key),
Packit d7e8d0
                                                 QString::fromUtf8(pair.value)));
Packit d7e8d0
        free(pair.key);
Packit d7e8d0
        free(pair.value);
Packit d7e8d0
Packit d7e8d0
        while (*string == ' ') {
Packit d7e8d0
            string++;
Packit d7e8d0
        }
Packit d7e8d0
        if (*string && *string != ',' && *string != ';' && *string != '+') {
Packit d7e8d0
            goto failure;    /* invalid delimiter */
Packit d7e8d0
        }
Packit d7e8d0
        if (*string) {
Packit d7e8d0
            string++;
Packit d7e8d0
        }
Packit d7e8d0
    }
Packit d7e8d0
    return result;
Packit d7e8d0
Packit d7e8d0
failure:
Packit d7e8d0
    return QVector<QGpgME::DN::Attribute>();
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
static QVector<QGpgME::DN::Attribute>
Packit d7e8d0
parse_dn(const QString &dn)
Packit d7e8d0
{
Packit d7e8d0
    return parse_dn((const unsigned char *)dn.toUtf8().data());
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
static QString dn_escape(const QString &s)
Packit d7e8d0
{
Packit d7e8d0
    QString result;
Packit d7e8d0
    for (unsigned int i = 0, end = s.length(); i != end; ++i) {
Packit d7e8d0
        const QChar ch = s[i];
Packit d7e8d0
        switch (ch.unicode()) {
Packit d7e8d0
        case ',':
Packit d7e8d0
        case '+':
Packit d7e8d0
        case '"':
Packit d7e8d0
        case '\\':
Packit d7e8d0
        case '<':
Packit d7e8d0
        case '>':
Packit d7e8d0
        case ';':
Packit d7e8d0
            result += QLatin1Char('\\');
Packit d7e8d0
        // fall through
Packit d7e8d0
        default:
Packit d7e8d0
            result += ch;
Packit d7e8d0
        }
Packit d7e8d0
    }
Packit d7e8d0
    return result;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
static QString
Packit d7e8d0
serialise(const QVector<QGpgME::DN::Attribute> &dn, const QString &sep)
Packit d7e8d0
{
Packit d7e8d0
    QStringList result;
Packit d7e8d0
    for (QVector<QGpgME::DN::Attribute>::const_iterator it = dn.begin(); it != dn.end(); ++it)
Packit d7e8d0
        if (!(*it).name().isEmpty() && !(*it).value().isEmpty()) {
Packit d7e8d0
            result.push_back((*it).name().trimmed() + QLatin1Char('=') + dn_escape((*it).value().trimmed()));
Packit d7e8d0
        }
Packit d7e8d0
    return result.join(sep);
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
static QGpgME::DN::Attribute::List
Packit d7e8d0
reorder_dn(const QGpgME::DN::Attribute::List &dn, const QStringList &attrOrder)
Packit d7e8d0
{
Packit d7e8d0
    QGpgME::DN::Attribute::List unknownEntries;
Packit d7e8d0
    QGpgME::DN::Attribute::List result;
Packit d7e8d0
    unknownEntries.reserve(dn.size());
Packit d7e8d0
    result.reserve(dn.size());
Packit d7e8d0
Packit d7e8d0
    // find all unknown entries in their order of appearance
Packit d7e8d0
    for (QGpgME::DN::const_iterator it = dn.begin(); it != dn.end(); ++it)
Packit d7e8d0
        if (!attrOrder.contains((*it).name())) {
Packit d7e8d0
            unknownEntries.push_back(*it);
Packit d7e8d0
        }
Packit d7e8d0
Packit d7e8d0
    // process the known attrs in the desired order
Packit d7e8d0
    for (QStringList::const_iterator oit = attrOrder.begin(); oit != attrOrder.end(); ++oit)
Packit d7e8d0
        if (*oit == QLatin1String("_X_")) {
Packit d7e8d0
            // insert the unknown attrs
Packit d7e8d0
            std::copy(unknownEntries.begin(), unknownEntries.end(),
Packit d7e8d0
                      std::back_inserter(result));
Packit d7e8d0
            unknownEntries.clear(); // don't produce dup's
Packit d7e8d0
        } else {
Packit d7e8d0
            for (QGpgME::DN::const_iterator dnit = dn.begin(); dnit != dn.end(); ++dnit)
Packit d7e8d0
                if ((*dnit).name() == *oit) {
Packit d7e8d0
                    result.push_back(*dnit);
Packit d7e8d0
                }
Packit d7e8d0
        }
Packit d7e8d0
Packit d7e8d0
    return result;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
//
Packit d7e8d0
//
Packit d7e8d0
// class DN
Packit d7e8d0
//
Packit d7e8d0
//
Packit d7e8d0
Packit d7e8d0
QGpgME::DN::DN()
Packit d7e8d0
{
Packit d7e8d0
    d = new Private();
Packit d7e8d0
    d->ref();
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
QGpgME::DN::DN(const QString &dn)
Packit d7e8d0
{
Packit d7e8d0
    d = new Private();
Packit d7e8d0
    d->ref();
Packit d7e8d0
    d->attributes = parse_dn(dn);
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
QGpgME::DN::DN(const char *utf8DN)
Packit d7e8d0
{
Packit d7e8d0
    d = new Private();
Packit d7e8d0
    d->ref();
Packit d7e8d0
    if (utf8DN) {
Packit d7e8d0
        d->attributes = parse_dn((const unsigned char *)utf8DN);
Packit d7e8d0
    }
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
QGpgME::DN::DN(const DN &other)
Packit d7e8d0
    : d(other.d)
Packit d7e8d0
{
Packit d7e8d0
    if (d) {
Packit d7e8d0
        d->ref();
Packit d7e8d0
    }
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
QGpgME::DN::~DN()
Packit d7e8d0
{
Packit d7e8d0
    if (d) {
Packit d7e8d0
        d->unref();
Packit d7e8d0
    }
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
const QGpgME::DN &QGpgME::DN::operator=(const DN &that)
Packit d7e8d0
{
Packit d7e8d0
    if (this->d == that.d) {
Packit d7e8d0
        return *this;
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
    if (that.d) {
Packit d7e8d0
        that.d->ref();
Packit d7e8d0
    }
Packit d7e8d0
    if (this->d) {
Packit d7e8d0
        this->d->unref();
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
    this->d = that.d;
Packit d7e8d0
Packit d7e8d0
    return *this;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
QString QGpgME::DN::prettyDN() const
Packit d7e8d0
{
Packit d7e8d0
    if (!d) {
Packit d7e8d0
        return QString();
Packit d7e8d0
    }
Packit d7e8d0
    if (d->reorderedAttributes.empty()) {
Packit d7e8d0
        d->reorderedAttributes = reorder_dn(d->attributes, d->order);
Packit d7e8d0
    }
Packit d7e8d0
    return serialise(d->reorderedAttributes, QStringLiteral(","));
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
QString QGpgME::DN::dn() const
Packit d7e8d0
{
Packit d7e8d0
    return d ? serialise(d->attributes, QStringLiteral(",")) : QString();
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
QString QGpgME::DN::dn(const QString &sep) const
Packit d7e8d0
{
Packit d7e8d0
    return d ? serialise(d->attributes, sep) : QString();
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
// static
Packit d7e8d0
QString QGpgME::DN::escape(const QString &value)
Packit d7e8d0
{
Packit d7e8d0
    return dn_escape(value);
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
void QGpgME::DN::detach()
Packit d7e8d0
{
Packit d7e8d0
    if (!d) {
Packit d7e8d0
        d = new QGpgME::DN::Private();
Packit d7e8d0
        d->ref();
Packit d7e8d0
    } else if (d->refCount() > 1) {
Packit d7e8d0
        QGpgME::DN::Private *d_save = d;
Packit d7e8d0
        d = new QGpgME::DN::Private(*d);
Packit d7e8d0
        d->ref();
Packit d7e8d0
        d_save->unref();
Packit d7e8d0
    }
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
void QGpgME::DN::append(const Attribute &attr)
Packit d7e8d0
{
Packit d7e8d0
    detach();
Packit d7e8d0
    d->attributes.push_back(attr);
Packit d7e8d0
    d->reorderedAttributes.clear();
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
QString QGpgME::DN::operator[](const QString &attr) const
Packit d7e8d0
{
Packit d7e8d0
    if (!d) {
Packit d7e8d0
        return QString();
Packit d7e8d0
    }
Packit d7e8d0
    const QString attrUpper = attr.toUpper();
Packit d7e8d0
    for (QVector<Attribute>::const_iterator it = d->attributes.constBegin();
Packit d7e8d0
            it != d->attributes.constEnd(); ++it)
Packit d7e8d0
        if ((*it).name() == attrUpper) {
Packit d7e8d0
            return (*it).value();
Packit d7e8d0
        }
Packit d7e8d0
    return QString();
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
static QVector<QGpgME::DN::Attribute> empty;
Packit d7e8d0
Packit d7e8d0
QGpgME::DN::const_iterator QGpgME::DN::begin() const
Packit d7e8d0
{
Packit d7e8d0
    return d ? d->attributes.constBegin() : empty.constBegin();
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
QGpgME::DN::const_iterator QGpgME::DN::end() const
Packit d7e8d0
{
Packit d7e8d0
    return d ? d->attributes.constEnd() : empty.constEnd();
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
void QGpgME::DN::setAttributeOrder (const QStringList &order) const
Packit d7e8d0
{
Packit d7e8d0
    d->order = order;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
const QStringList & QGpgME::DN::attributeOrder () const
Packit d7e8d0
{
Packit d7e8d0
    return d->order;
Packit d7e8d0
}