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