Prepare for changing return types in KMime.
[kdepim.git] / libkpgp / kpgpbaseG.cpp
blob59d0d50433455e45680d65deaffbccd3028f2169
1 /*
2 kpgpbaseG.cpp
4 Copyright (C) 2001,2002 the KPGP authors
5 See file AUTHORS.kpgp for details
7 This file is part of KPGP, the KDE PGP/GnuPG support library.
9 KPGP is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software Foundation,
16 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
19 #include "kpgpbase.h"
20 #include "kpgp.h"
21 #include "kpgp_debug.h"
22 #include <KLocalizedString>
24 #include <kshell.h>
25 #include <qdebug.h>
27 #include <QTextCodec>
28 #include <QByteArray>
30 #include <algorithm>
31 #include <string.h> /* strncmp */
33 namespace Kpgp
36 BaseG::BaseG()
37 : Base()
39 // determine the version of gpg (the method is equivalent to gpgme's method)
40 runGpg("--version", 0);
41 int eol = output.indexOf('\n');
42 if (eol > 0) {
43 int pos = output.lastIndexOf(' ', eol - 1);
44 if (pos != -1) {
45 mVersion = output.mid(pos + 1, eol - pos - 1);
46 qCDebug(KPGP_LOG) << "found GnuPG" << mVersion;
51 BaseG::~BaseG()
55 int
56 BaseG::encrypt(Block &block, const KeyIDList &recipients)
58 return encsign(block, recipients, 0);
61 int
62 BaseG::clearsign(Block &block, const char *passphrase)
64 return encsign(block, KeyIDList(), passphrase);
67 int
68 BaseG::encsign(Block &block, const KeyIDList &recipients,
69 const char *passphrase)
71 QByteArray cmd;
72 int exitStatus = 0;
74 if (!recipients.isEmpty() && passphrase != 0) {
75 cmd = "--batch --armor --sign --encrypt --textmode";
76 } else if (!recipients.isEmpty()) {
77 cmd = "--batch --armor --encrypt --textmode";
78 } else if (passphrase != 0) {
79 cmd = "--batch --escape-from --clearsign";
80 } else {
81 qCDebug(KPGP_LOG) << "kpgpbase: Neither recipients nor passphrase specified.";
82 return OK;
85 if (passphrase != 0) {
86 cmd += addUserId();
89 if (!recipients.isEmpty()) {
90 cmd += " --set-filename stdin";
92 QByteArray pgpUser = Module::getKpgp()->user();
93 if (Module::getKpgp()->encryptToSelf() && !pgpUser.isEmpty()) {
94 cmd += " -r 0x";
95 cmd += pgpUser;
98 for (KeyIDList::ConstIterator it = recipients.begin();
99 it != recipients.end(); ++it) {
100 cmd += " -r 0x";
101 cmd += (*it);
105 clear();
106 input = block.text();
107 exitStatus = runGpg(cmd.data(), passphrase);
108 if (!output.isEmpty()) {
109 block.setProcessedText(output);
111 block.setError(error);
113 if (exitStatus != 0) {
114 // this error message is later hopefully overwritten
115 errMsg = i18n("Unknown error.");
116 status = ERROR;
119 #if 0
120 // #### FIXME: As we check the keys ourselves the following problems
121 // shouldn't occur. Therefore I don't handle them for now.
122 // IK 01/2002
123 if (!recipients.isEmpty()) {
124 int index = 0;
125 bool bad = false;
126 unsigned int num = 0;
127 QByteArray badkeys = "";
128 // Examples:
129 // gpg: 0x12345678: skipped: public key not found
130 // gpg: 0x12345678: skipped: public key is disabled
131 // gpg: 0x12345678: skipped: unusable public key
132 // (expired or revoked key)
133 // gpg: 23456789: no info to calculate a trust probability
134 // (untrusted key, 23456789 is the key Id of the encryption sub key)
135 while ((index = error.indexOf("skipped: ", index)) != -1) {
136 bad = true;
137 index = error.indexOf('\'', index);
138 int index2 = error.indexOf('\'', index + 1);
139 badkeys += error.mid(index, index2 - index + 1) + ", ";
140 num++;
142 if (bad) {
143 badkeys.trimmed();
144 if (num == recipients.count())
145 errMsg = i18n("Could not find public keys matching the userid(s)\n"
146 "%1;\n"
147 "the message is not encrypted.",
148 badkeys.data());
149 else
150 errMsg = i18n("Could not find public keys matching the userid(s)\n"
151 "%1;\n"
152 "these persons will not be able to read the message.",
153 badkeys.data());
154 status |= MISSINGKEY;
155 status |= ERROR;
158 #endif
159 if (passphrase != 0) {
160 // Example 1 (bad passphrase, clearsign only):
161 // gpg: skipped `0x12345678': bad passphrase
162 // gpg: [stdin]: clearsign failed: bad passphrase
163 // Example 2 (bad passphrase, sign & encrypt):
164 // gpg: skipped `0x12345678': bad passphrase
165 // gpg: [stdin]: sign+encrypt failed: bad passphrase
166 // Example 3 (unusable secret key, clearsign only):
167 // gpg: skipped `0x12345678': unusable secret key
168 // gpg: [stdin]: clearsign failed: unusable secret key
169 // Example 4 (unusable secret key, sign & encrypt):
170 // gpg: skipped `0xAC0EB35D': unusable secret key
171 // gpg: [stdin]: sign+encrypt failed: unusable secret key
172 if (error.contains("bad passphrase")) {
173 errMsg = i18n("Signing failed because the passphrase is wrong.");
174 status |= BADPHRASE;
175 status |= ERR_SIGNING;
176 status |= ERROR;
177 } else if (error.contains("unusable secret key")) {
178 errMsg = i18n("Signing failed because your secret key is unusable.");
179 status |= ERR_SIGNING;
180 status |= ERROR;
181 } else if (!(status & ERROR)) {
182 //qCDebug(KPGP_LOG) <<"Base: Good Passphrase!";
183 status |= SIGNED;
187 //qCDebug(KPGP_LOG) <<"status =" << status;
188 block.setStatus(status);
189 return status;
193 BaseG::decrypt(Block &block, const char *passphrase)
195 int index, index2;
196 int exitStatus = 0;
198 clear();
199 input = block.text();
200 exitStatus = runGpg("--batch --decrypt", passphrase);
201 if (!output.isEmpty() && (!error.contains("gpg: quoted printable"))) {
202 block.setProcessedText(output);
204 block.setError(error);
206 if (exitStatus == -1) {
207 errMsg = i18n("Error running gpg");
208 status = ERROR;
209 block.setStatus(status);
210 return status;
213 // Example 1 (good passphrase, decryption successful):
214 // gpg: encrypted with 2048-bit ELG-E key, ID 12345678, created 2000-11-11
215 // "Foo Bar <foo@bar.xyz>"
217 // Example 2 (bad passphrase):
218 // gpg: encrypted with 1024-bit RSA key, ID 12345678, created 1991-01-01
219 // "Foo Bar <foo@bar.xyz>"
220 // gpg: public key decryption failed: bad passphrase
221 // gpg: decryption failed: secret key not available
223 // Example 3 (no secret key available):
224 // gpg: encrypted with RSA key, ID 12345678
225 // gpg: decryption failed: secret key not available
227 // Example 4 (good passphrase for second key, decryption successful):
228 // gpg: encrypted with 2048-bit ELG-E key, ID 12345678, created 2000-01-01
229 // "Foo Bar (work) <foo@bar.xyz>"
230 // gpg: public key decryption failed: bad passphrase
231 // gpg: encrypted with 2048-bit ELG-E key, ID 23456789, created 2000-02-02
232 // "Foo Bar (home) <foo@bar.xyz>"
234 // Example 5: passphrase dialog cancelled by user
235 // gpg: cancelled by user
236 // gpg: encrypted with 2048-bit ELG key, ID XXXXXXXX, created 2006-11-16
237 // "Foo Bar <foobar@foo.org>"
238 // gpg: public key decryption failed: General error [..]
239 if (error.contains("gpg: encrypted with")) {
240 //qCDebug(KPGP_LOG) <<"kpgpbase: message is encrypted";
241 status |= ENCRYPTED;
242 if (error.contains("\ngpg: decryption failed")) {
243 if ((index = error.indexOf("bad passphrase")) != -1) {
244 if (passphrase != 0) {
245 errMsg = i18n("Bad passphrase; could not decrypt.");
246 qCDebug(KPGP_LOG) << "Base: passphrase is bad";
247 status |= BADPHRASE;
248 status |= ERROR;
249 } else {
250 // Search backwards the user ID of the needed key
251 index2 = error.lastIndexOf('"', index) - 1;
252 index = error.lastIndexOf(" \"", index2) + 7;
253 // The conversion from UTF8 is necessary because gpg stores and
254 // prints user IDs in UTF8
255 block.setRequiredUserId(QString::fromUtf8(error.mid(index, index2 - index + 1)));
256 qCDebug(KPGP_LOG) << "Base: key needed is \"" << block.requiredUserId() << "\"!";
258 } else if (error.contains("secret key not available")) {
259 // no secret key fitting this message
260 status |= NO_SEC_KEY;
261 status |= ERROR;
262 errMsg = i18n("You do not have the secret key needed to decrypt this message.");
263 qCDebug(KPGP_LOG) << "Base: no secret key for this message";
264 } else if (error.contains("cancelled by user")) {
265 status |= CANCEL;
266 status |= ERROR;
267 errMsg = i18n("The passphrase dialog was cancelled.");
268 qCDebug(KPGP_LOG) << errMsg;
271 // check for persons
272 #if 0
273 // ##### FIXME: This information is anyway currently not used
274 // I'll change it to always determine the recipients.
275 index = error.indexOf("can only be read by:");
276 if (index != -1) {
277 index = error.indexOf('\n', index);
278 int end = error.indexOf("\n\n", index);
280 mRecipients.clear();
281 while ((index2 = error.indexOf('\n', index + 1)) <= end) {
282 QByteArray item = error.mid(index + 1, index2 - index - 1);
283 item.trimmed();
284 mRecipients.append(item);
285 index = index2;
288 #endif
291 // Example 1 (unknown signature key):
292 // gpg: Signature made Wed 02 Jan 2002 11:26:33 AM CET using DSA key ID 2E250C64
293 // gpg: Can't check signature: public key not found
294 if ((index = error.indexOf("Signature made")) != -1) {
295 //qCDebug(KPGP_LOG) <<"Base: message is signed";
296 status |= SIGNED;
297 // get signature date and signature key ID
298 // Example: Signature made Sun 06 May 2001 03:49:27 PM CEST using DSA key ID 12345678
299 index2 = error.indexOf("using", index + 15);
300 block.setSignatureDate(error.mid(index + 15, index2 - (index + 15) - 1));
301 qCDebug(KPGP_LOG) << "Message was signed on '" << block.signatureDate() << "'";
302 index2 = error.indexOf("key ID ", index2) + 7;
303 block.setSignatureKeyId(error.mid(index2, 8));
304 qCDebug(KPGP_LOG) << "Message was signed with key '" << block.signatureKeyId() << "'";
305 // move index to start of next line
306 index = error.indexOf('\n', index2) + 1;
308 if ((error.indexOf("Key matching expected", index) != -1)
309 || (error.indexOf("Can't check signature", index) != -1)) {
310 status |= UNKNOWN_SIG;
311 status |= GOODSIG;
312 block.setSignatureUserId(QString());
313 } else if (error.indexOf("Good signature", index) != -1) {
314 status |= GOODSIG;
315 // get the primary user ID of the signer
316 index = error.indexOf('"', index);
317 index2 = error.indexOf('\n', index + 1);
318 index2 = error.lastIndexOf('"', index2 - 1);
319 block.setSignatureUserId(QLatin1String(error.mid(index + 1, index2 - index - 1)));
320 } else if (error.indexOf("BAD signature", index) != -1) {
321 //qCDebug(KPGP_LOG) <<"BAD signature";
322 status |= ERROR;
323 // get the primary user ID of the signer
324 index = error.indexOf('"', index);
325 index2 = error.indexOf('\n', index + 1);
326 index2 = error.lastIndexOf('"', index2 - 1);
327 block.setSignatureUserId(QLatin1String(error.mid(index + 1, index2 - index - 1)));
328 } else if (error.indexOf("Can't find the right public key", index) != -1) {
329 // #### fix this hack
330 // I think this can't happen anymore because if the pubring is missing
331 // the current GnuPG creates a new empty one.
332 status |= UNKNOWN_SIG;
333 status |= GOODSIG; // this is a hack...
334 block.setSignatureUserId(i18n("??? (file ~/.gnupg/pubring.gpg not found)"));
335 } else {
336 status |= ERROR;
337 block.setSignatureUserId(QString());
340 //qCDebug(KPGP_LOG) <<"status =" << status;
341 block.setStatus(status);
342 return status;
345 Key *
346 BaseG::readPublicKey(const KeyID &keyID,
347 const bool readTrust /* = false */,
348 Key *key /* = 0 */)
350 int exitStatus = 0;
352 status = 0;
353 if (readTrust) {
354 exitStatus = runGpg(QByteArray(QByteArray("--batch --list-public-keys --with-fingerprint --with-colons --fixed-list-mode 0x") + keyID), 0, true);
355 } else {
356 exitStatus = runGpg(QByteArray(QByteArray("--batch --list-public-keys --with-fingerprint --with-colons --fixed-list-mode --no-expensive-trust-checks 0x") + keyID), 0, true);
359 if (exitStatus != 0) {
360 status = ERROR;
361 return 0;
364 int offset;
365 // search start of key data
366 if (!strncmp(output.data(), "pub:", 4)) {
367 offset = 0;
368 } else {
369 offset = output.indexOf("\npub:");
370 if (offset == -1) {
371 return 0;
372 } else {
373 offset++;
377 key = parseKeyData(output, offset, key);
379 return key;
382 KeyList
383 BaseG::publicKeys(const QStringList &patterns)
385 int exitStatus = 0;
387 // the option --with-colons should be used for interprocess communication
388 // with gpg (according to Werner Koch)
389 QByteArray cmd = "--batch --list-public-keys --with-fingerprint --with-colons "
390 "--fixed-list-mode --no-expensive-trust-checks";
391 for (QStringList::ConstIterator it = patterns.begin();
392 it != patterns.end(); ++it) {
393 cmd += ' ';
394 cmd += KShell::quoteArg(*it).toLocal8Bit();
396 status = 0;
397 exitStatus = runGpg(cmd, 0, true);
399 if (exitStatus != 0) {
400 status = ERROR;
401 return KeyList();
404 // now we need to parse the output for public keys
405 KeyList publicKeys = parseKeyList(output, false);
407 // sort the list of public keys
408 std::sort(publicKeys.begin(), publicKeys.end(), KeyCompare);
410 return publicKeys;
413 KeyList
414 BaseG::secretKeys(const QStringList &patterns)
416 int exitStatus = 0;
418 // the option --with-colons should be used for interprocess communication
419 // with gpg (according to Werner Koch)
420 QByteArray cmd = "--batch --list-secret-keys --with-fingerprint --with-colons "
421 "--fixed-list-mode";
422 for (QStringList::ConstIterator it = patterns.begin();
423 it != patterns.end(); ++it) {
424 cmd += ' ';
425 cmd += KShell::quoteArg(*it).toLocal8Bit();
427 status = 0;
428 exitStatus = runGpg(cmd, 0, true);
430 if (exitStatus != 0) {
431 status = ERROR;
432 return KeyList();
435 // now we need to parse the output for secret keys
436 KeyList secretKeys = parseKeyList(output, true);
438 // sort the list of secret keys
439 std::sort(secretKeys.begin(), secretKeys.end(), KeyCompare);
441 return secretKeys;
445 BaseG::signKey(const KeyID &keyID, const char *passphrase)
447 QByteArray cmd;
448 int exitStatus = 0;
450 cmd = "--batch";
451 cmd += addUserId();
452 cmd += " --sign-key 0x";
453 cmd += keyID;
455 status = 0;
456 exitStatus = runGpg(cmd.data(), passphrase);
458 if (exitStatus != 0) {
459 status = ERROR;
462 return status;
465 QByteArray
466 BaseG::getAsciiPublicKey(const KeyID &keyID)
468 int exitStatus = 0;
470 if (keyID.isEmpty()) {
471 return QByteArray();
474 status = 0;
475 exitStatus = runGpg(QByteArray(QByteArray("--batch --armor --export 0x") + keyID), 0, true);
477 if (exitStatus != 0) {
478 status = ERROR;
479 return QByteArray();
482 return output;
485 Key *
486 BaseG::parseKeyData(const QByteArray &output, int &offset, Key *key /* = 0 */)
487 // This function parses the data for a single key which is output by GnuPG
488 // with the following command line arguments:
489 // --batch --list-public-keys --with-fingerprint --with-colons
490 // --fixed-list-mode [--no-expensive-trust-checks]
491 // It expects the key data to start at offset and returns the start of
492 // the next key's data in offset.
493 // Subkeys are currently ignored.
495 int index = offset;
497 if ((strncmp(output.data() + offset, "pub:", 4) != 0)
498 && (strncmp(output.data() + offset, "sec:", 4) != 0)) {
499 return 0;
502 if (key == 0) {
503 key = new Key();
504 } else {
505 key->clear();
508 QByteArray keyID;
509 bool firstKey = true;
511 while (true) {
512 int eol;
513 // search the end of the current line
514 if ((eol = output.indexOf('\n', index)) == -1) {
515 break;
518 bool bIsPublicKey = false;
519 if ((bIsPublicKey = !strncmp(output.data() + index, "pub:", 4))
520 || !strncmp(output.data() + index, "sec:", 4)) {
521 // line contains primary key data
522 // Example: pub:f:1024:17:63CB691DFAEBD5FC:860451781::379:-:::scESC:
524 // abort parsing if we found the start of the next key
525 if (!firstKey) {
526 break;
528 firstKey = false;
530 key->setSecret(!bIsPublicKey);
532 Subkey *subkey = new Subkey(QByteArray(), !bIsPublicKey);
534 int pos = index + 4; // begin of 2nd field
535 int pos2 = output.indexOf(':', pos);
536 for (int field = 2; field <= 12; field++) {
537 switch (field) {
538 case 2: // the calculated trust
539 if (pos2 > pos) {
540 switch (output[pos]) {
541 case 'o': // unknown (this key is new to the system)
542 break;
543 case 'i': // the key is invalid, e.g. missing self-signature
544 subkey->setInvalid(true);
545 key->setInvalid(true);
546 break;
547 case 'd': // the key has been disabled
548 subkey->setDisabled(true);
549 key->setDisabled(true);
550 break;
551 case 'r': // the key has been revoked
552 subkey->setRevoked(true);
553 key->setRevoked(true);
554 break;
555 case 'e': // the key has expired
556 subkey->setExpired(true);
557 key->setExpired(true);
558 break;
559 case '-': // undefined (no path leads to the key)
560 case 'q': // undefined (no trusted path leads to the key)
561 case 'n': // don't trust this key at all
562 case 'm': // the key is marginally trusted
563 case 'f': // the key is fully trusted
564 case 'u': // the key is ultimately trusted (secret key available)
565 // These values are ignored since we determine the key trust
566 // from the trust values of the user ids.
567 break;
568 default:
569 qCDebug(KPGP_LOG) << "Unknown trust value";
572 break;
573 case 3: // length of key in bits
574 if (pos2 > pos) {
575 subkey->setKeyLength(output.mid(pos, pos2 - pos).toUInt());
577 break;
578 case 4: // the key algorithm
579 if (pos2 > pos) {
580 subkey->setKeyAlgorithm(output.mid(pos, pos2 - pos).toUInt());
582 break;
583 case 5: // the long key id
584 keyID = output.mid(pos, pos2 - pos);
585 subkey->setKeyID(keyID);
586 break;
587 case 6: // the creation date (in seconds since 1970-01-01 00:00:00)
588 if (pos2 > pos) {
589 subkey->setCreationDate(QString(QLatin1String(output.mid(pos, pos2 - pos))).toLong());
591 break;
592 case 7: // the expiration date (in seconds since 1970-01-01 00:00:00)
593 if (pos2 > pos) {
594 subkey->setExpirationDate(QString(QLatin1String(output.mid(pos, pos2 - pos))).toLong());
595 } else {
596 subkey->setExpirationDate(-1); // key expires never
598 break;
599 case 8: // local ID (ignored)
600 case 9: // Ownertrust (ignored for now)
601 case 10: // User-ID (always empty in --fixed-list-mode)
602 case 11: // signature class (always empty except for key signatures)
603 break;
604 case 12: // key capabilities
605 for (int i = pos; i < pos2; ++i)
606 switch (output[i]) {
607 case 'e':
608 subkey->setCanEncrypt(true);
609 break;
610 case 's':
611 subkey->setCanSign(true);
612 break;
613 case 'c':
614 subkey->setCanCertify(true);
615 break;
616 case 'E':
617 key->setCanEncrypt(true);
618 break;
619 case 'S':
620 key->setCanSign(true);
621 break;
622 case 'C':
623 key->setCanCertify(true);
624 break;
625 default:
626 qCDebug(KPGP_LOG) << "Unknown key capability";
628 break;
630 pos = pos2 + 1;
631 pos2 = output.indexOf(':', pos);
633 key->addSubkey(subkey);
634 } else if (!strncmp(output.data() + index, "uid:", 4)) {
635 // line contains a user id
636 // Example: uid:f::::::::Philip R. Zimmermann <prz@pgp.com>:
638 UserID *userID = new UserID(QLatin1String(""));
640 int pos = index + 4; // begin of 2nd field
641 int pos2 = output.indexOf(':', pos);
642 for (int field = 2; field <= 10; field++) {
643 switch (field) {
644 case 2: // the calculated trust
645 if (pos2 > pos) {
646 switch (output[pos]) {
647 case 'i': // the user id is invalid, e.g. missing self-signature
648 userID->setInvalid(true);
649 break;
650 case 'r': // the user id has been revoked
651 userID->setRevoked(true);
652 break;
653 case '-': // undefined (no path leads to the key)
654 case 'q': // undefined (no trusted path leads to the key)
655 userID->setValidity(KPGP_VALIDITY_UNDEFINED);
656 break;
657 case 'n': // don't trust this key at all
658 userID->setValidity(KPGP_VALIDITY_NEVER);
659 break;
660 case 'm': // the key is marginally trusted
661 userID->setValidity(KPGP_VALIDITY_MARGINAL);
662 break;
663 case 'f': // the key is fully trusted
664 userID->setValidity(KPGP_VALIDITY_FULL);
665 break;
666 case 'u': // the key is ultimately trusted (secret key available)
667 userID->setValidity(KPGP_VALIDITY_ULTIMATE);
668 break;
669 default:
670 qCDebug(KPGP_LOG) << "Unknown trust value";
673 break;
674 case 3: // these fields are empty
675 case 4:
676 case 5:
677 case 6:
678 case 7:
679 case 8:
680 case 9:
681 break;
682 case 10: // User-ID
683 QByteArray uid = output.mid(pos, pos2 - pos);
684 // replace "\xXX" with the corresponding character;
685 // other escaped characters, i.e. \n, \r etc., are ignored
686 // because they shouldn't appear in user IDs
687 for (int idx = 0 ; (idx = uid.indexOf("\\x", idx) != -1) ; ++idx) {
688 char str[2] = "x";
689 str[0] = (char) QString(QLatin1String(uid.mid(idx + 2, 2))).toShort(0, 16);
690 uid.replace(idx, 4, str);
692 QString uidString = QString::fromUtf8(uid.data());
693 // check whether uid was utf-8 encoded
694 bool isUtf8 = true;
695 for (int i = 0; i + 1 < uidString.length(); ++i) {
696 if (uidString[i].unicode() == 0xdbff &&
697 uidString[i + 1].row() == 0xde) {
698 // we found a non-Unicode character (see QString::fromUtf8())
699 isUtf8 = false;
700 break;
703 if (!isUtf8) {
704 // The user id isn't utf-8 encoded. It was most likely
705 // created with PGP which either used latin1 or koi8-r.
706 qCDebug(KPGP_LOG) << "User Id '" << uid
707 << "' doesn't seem to be utf-8 encoded.";
709 // We determine the ratio between non-ASCII and ASCII chars.
710 // A koi8-r user id should have lots of non-ASCII chars.
711 int nonAsciiCount = 0, asciiCount = 0;
713 // We only look at the first part of the user id (i. e. everything
714 // before the email address resp. before a comment)
715 for (signed char *ch = (signed char *)uid.data();
716 *ch && (*ch != '(') && (*ch != '<');
717 ++ch) {
718 if (((*ch >= 'A') && (*ch <= 'Z'))
719 || ((*ch >= 'a') && (*ch <= 'z'))) {
720 ++asciiCount;
721 } else if (*ch < 0) {
722 ++nonAsciiCount;
725 qCDebug(KPGP_LOG) << "ascii-nonAscii ratio :" << asciiCount
726 << ":" << nonAsciiCount;
727 if (nonAsciiCount > asciiCount) {
728 // assume koi8-r encoding
729 qCDebug(KPGP_LOG) << "Assume koi8-r encoding.";
730 QTextCodec *codec = QTextCodec::codecForName("KOI8-R");
731 uidString = codec->toUnicode(uid.data());
732 // check the case of the first two characters to find out
733 // whether the user id is probably CP1251 encoded (for some
734 // reason in CP1251 the lower case characters have smaller
735 // codes than the upper case characters, so if the first char
736 // of the koi8-r decoded user id is lower case and the second
737 // char is upper case then it's likely that the user id is
738 // CP1251 encoded)
739 if ((uidString.length() >= 2)
740 && (uidString[0].toLower() == uidString[0])
741 && (uidString[1].toUpper() == uidString[1])) {
742 // koi8-r decoded user id has inverted case, so assume
743 // CP1251 encoding
744 qCDebug(KPGP_LOG) << "No, it doesn't seem to be koi8-r."
745 "Use CP 1251 instead.";
746 QTextCodec *codec = QTextCodec::codecForName("CP1251");
747 uidString = codec->toUnicode(uid.data());
749 } else {
750 // assume latin1 encoding
751 qCDebug(KPGP_LOG) << "Assume latin1 encoding.";
752 uidString = QString::fromLatin1(uid.data());
755 userID->setText(uidString);
756 break;
758 pos = pos2 + 1;
759 pos2 = output.indexOf(':', pos);
762 // user IDs are printed in UTF-8 by gpg (if one uses --with-colons)
763 key->addUserID(userID);
764 } else if (!strncmp(output.data() + index, "fpr:", 4)) {
765 // line contains a fingerprint
766 // Example: fpr:::::::::17AFBAAF21064E513F037E6E63CB691DFAEBD5FC:
768 if (key == 0) { // invalid key data
769 break;
772 // search the fingerprint (it's in the 10th field)
773 int pos = index + 4;
774 for (int i = 0; i < 8; ++i) {
775 pos = output.indexOf(':', pos) + 1;
777 int pos2 = output.indexOf(':', pos);
779 key->setFingerprint(keyID, output.mid(pos, pos2 - pos));
781 index = eol + 1;
784 //qCDebug(KPGP_LOG) <<"finished parsing key data";
786 offset = index;
788 return key;
791 KeyList
792 BaseG::parseKeyList(const QByteArray &output, bool secretKeys)
794 KeyList keys;
795 Key *key = 0;
796 int offset;
798 // search start of key data
799 if (!strncmp(output.data(), "pub:", 4)
800 || !strncmp(output.data(), "sec:", 4)) {
801 offset = 0;
802 } else {
803 if (secretKeys) {
804 offset = output.indexOf("\nsec:");
805 } else {
806 offset = output.indexOf("\npub:");
808 if (offset == -1) {
809 return keys;
810 } else {
811 offset++;
815 do {
816 key = parseKeyData(output, offset);
818 if (key != 0) {
819 keys.append(key);
821 } while (key != 0);
823 //qCDebug(KPGP_LOG) <<"finished parsing keys";
825 return keys;
828 } // namespace Kpgp