Example #1
0
Key *
BaseG::parseKeyData(const QCString &output, int &offset, Key *key /* = 0 */)
// This function parses the data for a single key which is output by GnuPG
// with the following command line arguments:
//   --batch --list-public-keys --with-fingerprint --with-colons
//   --fixed-list-mode [--no-expensive-trust-checks]
// It expects the key data to start at offset and returns the start of
// the next key's data in offset.
// Subkeys are currently ignored.
{
    int index = offset;

    if((strncmp(output.data() + offset, "pub:", 4) != 0)
            && (strncmp(output.data() + offset, "sec:", 4) != 0))
    {
        return 0;
    }

    if(key == 0)
        key = new Key();
    else
        key->clear();

    QCString keyID;
    bool firstKey = true;

    while(true)
    {
        int eol;
        // search the end of the current line
        if((eol = output.find('\n', index)) == -1)
            break;

        bool bIsPublicKey = false;
        if((bIsPublicKey = !strncmp(output.data() + index, "pub:", 4))
                || !strncmp(output.data() + index, "sec:", 4))
        {
            // line contains primary key data
            // Example: pub:f:1024:17:63CB691DFAEBD5FC:860451781::379:-:::scESC:

            // abort parsing if we found the start of the next key
            if(!firstKey)
                break;
            firstKey = false;

            key->setSecret(!bIsPublicKey);

            Subkey *subkey = new Subkey(QCString(), !bIsPublicKey);

            int pos = index + 4; // begin of 2nd field
            int pos2 = output.find(':', pos);
            for(int field = 2; field <= 12; field++)
            {
                switch(field)
                {
                    case 2: // the calculated trust
                        if(pos2 > pos)
                        {
                            switch(output[pos])
                            {
                                case 'o': // unknown (this key is new to the system)
                                    break;
                                case 'i': // the key is invalid, e.g. missing self-signature
                                    subkey->setInvalid(true);
                                    key->setInvalid(true);
                                    break;
                                case 'd': // the key has been disabled
                                    subkey->setDisabled(true);
                                    key->setDisabled(true);
                                    break;
                                case 'r': // the key has been revoked
                                    subkey->setRevoked(true);
                                    key->setRevoked(true);
                                    break;
                                case 'e': // the key has expired
                                    subkey->setExpired(true);
                                    key->setExpired(true);
                                    break;
                                case '-': // undefined (no path leads to the key)
                                case 'q': // undefined (no trusted path leads to the key)
                                case 'n': // don't trust this key at all
                                case 'm': // the key is marginally trusted
                                case 'f': // the key is fully trusted
                                case 'u': // the key is ultimately trusted (secret key available)
                                    // These values are ignored since we determine the key trust
                                    // from the trust values of the user ids.
                                    break;
                                default:
                                    kdDebug(5100) << "Unknown trust value\n";
                            }
                        }
                        break;
                    case 3: // length of key in bits
                        if(pos2 > pos)
                            subkey->setKeyLength(output.mid(pos, pos2 - pos).toUInt());
                        break;
                    case 4: //  the key algorithm
                        if(pos2 > pos)
                            subkey->setKeyAlgorithm(output.mid(pos, pos2 - pos).toUInt());
                        break;
                    case 5: // the long key id
                        keyID = output.mid(pos, pos2 - pos);
                        subkey->setKeyID(keyID);
                        break;
                    case 6: // the creation date (in seconds since 1970-01-01 00:00:00)
                        if(pos2 > pos)
                            subkey->setCreationDate(output.mid(pos, pos2 - pos).toLong());
                        break;
                    case 7: // the expiration date (in seconds since 1970-01-01 00:00:00)
                        if(pos2 > pos)
                            subkey->setExpirationDate(output.mid(pos, pos2 - pos).toLong());
                        else
                            subkey->setExpirationDate(-1);   // key expires never
                        break;
                    case 8: // local ID (ignored)
                    case 9: // Ownertrust (ignored for now)
                    case 10: // User-ID (always empty in --fixed-list-mode)
                    case 11: // signature class (always empty except for key signatures)
                        break;
                    case 12: // key capabilities
                        for(int i = pos; i < pos2; i++)
                            switch(output[i])
                            {
                                case 'e':
                                    subkey->setCanEncrypt(true);
                                    break;
                                case 's':
                                    subkey->setCanSign(true);
                                    break;
                                case 'c':
                                    subkey->setCanCertify(true);
                                    break;
                                case 'E':
                                    key->setCanEncrypt(true);
                                    break;
                                case 'S':
                                    key->setCanSign(true);
                                    break;
                                case 'C':
                                    key->setCanCertify(true);
                                    break;
                                default:
                                    kdDebug(5100) << "Unknown key capability\n";
                            }
                        break;
                }
                pos = pos2 + 1;
                pos2 = output.find(':', pos);
            }
            key->addSubkey(subkey);
        }
        else if(!strncmp(output.data() + index, "uid:", 4))
        {
            // line contains a user id
            // Example: uid:f::::::::Philip R. Zimmermann <*****@*****.**>:

            UserID *userID = new UserID("");

            int pos = index + 4; // begin of 2nd field
            int pos2 = output.find(':', pos);
            for(int field = 2; field <= 10; field++)
            {
                switch(field)
                {
                    case 2: // the calculated trust
                        if(pos2 > pos)
                        {
                            switch(output[pos])
                            {
                                case 'i': // the user id is invalid, e.g. missing self-signature
                                    userID->setInvalid(true);
                                    break;
                                case 'r': // the user id has been revoked
                                    userID->setRevoked(true);
                                    break;
                                case '-': // undefined (no path leads to the key)
                                case 'q': // undefined (no trusted path leads to the key)
                                    userID->setValidity(KPGP_VALIDITY_UNDEFINED);
                                    break;
                                case 'n': // don't trust this key at all
                                    userID->setValidity(KPGP_VALIDITY_NEVER);
                                    break;
                                case 'm': // the key is marginally trusted
                                    userID->setValidity(KPGP_VALIDITY_MARGINAL);
                                    break;
                                case 'f': // the key is fully trusted
                                    userID->setValidity(KPGP_VALIDITY_FULL);
                                    break;
                                case 'u': // the key is ultimately trusted (secret key available)
                                    userID->setValidity(KPGP_VALIDITY_ULTIMATE);
                                    break;
                                default:
                                    kdDebug(5100) << "Unknown trust value\n";
                            }
                        }
                        break;
                    case 3: // these fields are empty
                    case 4:
                    case 5:
                    case 6:
                    case 7:
                    case 8:
                    case 9:
                        break;
                    case 10: // User-ID
                        QCString uid = output.mid(pos, pos2 - pos);
                        // replace "\xXX" with the corresponding character;
                        // other escaped characters, i.e. \n, \r etc., are ignored
                        // because they shouldn't appear in user IDs
                        for(int idx = 0 ; (idx = uid.find("\\x", idx)) >= 0 ; ++idx)
                        {
                            char str[2] = "x";
                            str[0] = (char) QString(uid.mid(idx + 2, 2)).toShort(0, 16);
                            uid.replace(idx, 4, str);
                        }
                        QString uidString = QString::fromUtf8(uid.data());
                        // check whether uid was utf-8 encoded
                        bool isUtf8 = true;
                        for(unsigned int i = 0; i + 1 < uidString.length(); ++i)
                        {
                            if(uidString[i].unicode() == 0xdbff &&
                                    uidString[i + 1].row() == 0xde)
                            {
                                // we found a non-Unicode character (see QString::fromUtf8())
                                isUtf8 = false;
                                break;
                            }
                        }
                        if(!isUtf8)
                        {
                            // The user id isn't utf-8 encoded. It was most likely
                            // created with PGP which either used latin1 or koi8-r.
                            kdDebug(5100) << "User Id '" << uid
                                          << "' doesn't seem to be utf-8 encoded." << endl;

                            // We determine the ratio between non-ASCII and ASCII chars.
                            // A koi8-r user id should have lots of non-ASCII chars.
                            int nonAsciiCount = 0, asciiCount = 0;

                            // We only look at the first part of the user id (i. e. everything
                            // before the email address resp. before a comment)
                            for(signed char *ch = (signed char *)uid.data();
                                    *ch && (*ch != '(') && (*ch != '<');
                                    ++ch)
                            {
                                if(((*ch >= 'A') && (*ch <= 'Z'))
                                        || ((*ch >= 'a') && (*ch <= 'z')))
                                    ++asciiCount;
                                else if(*ch < 0)
                                    ++nonAsciiCount;
                            }
                            kdDebug(5100) << "ascii-nonAscii ratio : " << asciiCount
                                          << ":" << nonAsciiCount << endl;
                            if(nonAsciiCount > asciiCount)
                            {
                                // assume koi8-r encoding
                                kdDebug(5100) << "Assume koi8-r encoding." << endl;
                                QTextCodec *codec = QTextCodec::codecForName("KOI8-R");
                                uidString = codec->toUnicode(uid.data());
                                // check the case of the first two characters to find out
                                // whether the user id is probably CP1251 encoded (for some
                                // reason in CP1251 the lower case characters have smaller
                                // codes than the upper case characters, so if the first char
                                // of the koi8-r decoded user id is lower case and the second
                                // char is upper case then it's likely that the user id is
                                // CP1251 encoded)
                                if((uidString.length() >= 2)
                                        && (uidString[0].lower() == uidString[0])
                                        && (uidString[1].upper() == uidString[1]))
                                {
                                    // koi8-r decoded user id has inverted case, so assume
                                    // CP1251 encoding
                                    kdDebug(5100) << "No, it doesn't seem to be koi8-r. "
                                                  "Use CP 1251 instead." << endl;
                                    QTextCodec *codec = QTextCodec::codecForName("CP1251");
                                    uidString = codec->toUnicode(uid.data());
                                }
                            }
                            else
                            {
                                // assume latin1 encoding
                                kdDebug(5100) << "Assume latin1 encoding." << endl;
                                uidString = QString::fromLatin1(uid.data());
                            }
                        }
                        userID->setText(uidString);
                        break;
                }
                pos = pos2 + 1;
                pos2 = output.find(':', pos);
            }

            // user IDs are printed in UTF-8 by gpg (if one uses --with-colons)
            key->addUserID(userID);
        }
        else if(!strncmp(output.data() + index, "fpr:", 4))
        {
            // line contains a fingerprint
            // Example: fpr:::::::::17AFBAAF21064E513F037E6E63CB691DFAEBD5FC:

            if(key == 0)  // invalid key data
                break;

            // search the fingerprint (it's in the 10th field)
            int pos = index + 4;
            for(int i = 0; i < 8; i++)
                pos = output.find(':', pos) + 1;
            int pos2 = output.find(':', pos);

            key->setFingerprint(keyID, output.mid(pos, pos2 - pos));
        }
        index = eol + 1;
    }

    //kdDebug(5100) << "finished parsing key data\n";

    offset = index;

    return key;
}