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; }