one entry for korganizer in khelpcenters navigation tree is enough
[kdepim.git] / libkpgp / kpgpbaseG.cpp
blob94fdaa76bc9b8d9d77d05a93ec786833ee9a0c27
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"
22 #include <klocale.h>
23 #include <kshell.h>
24 #include <kdebug.h>
26 #include <QTextCodec>
27 #include <QByteArray>
29 #include <algorithm>
30 #include <string.h> /* strncmp */
32 namespace Kpgp {
34 BaseG::BaseG()
35 : Base()
37 // determine the version of gpg (the method is equivalent to gpgme's method)
38 runGpg( "--version", 0 );
39 int eol = output.indexOf( '\n' );
40 if( eol > 0 ) {
41 int pos = output.lastIndexOf( ' ', eol - 1 );
42 if( pos != -1 ) {
43 mVersion = output.mid( pos + 1, eol - pos - 1 );
44 kDebug( 5326 ) <<"found GnuPG" << mVersion;
50 BaseG::~BaseG()
55 int
56 BaseG::encrypt( Block& block, const KeyIDList& recipients )
58 return encsign( block, recipients, 0 );
62 int
63 BaseG::clearsign( Block& block, const char *passphrase )
65 return encsign( block, KeyIDList(), passphrase );
69 int
70 BaseG::encsign( Block& block, const KeyIDList& recipients,
71 const char *passphrase )
73 QByteArray cmd;
74 int exitStatus = 0;
76 if(!recipients.isEmpty() && passphrase != 0)
77 cmd = "--batch --armor --sign --encrypt --textmode";
78 else if(!recipients.isEmpty())
79 cmd = "--batch --armor --encrypt --textmode";
80 else if(passphrase != 0)
81 cmd = "--batch --escape-from --clearsign";
82 else
84 kDebug( 5326 ) <<"kpgpbase: Neither recipients nor passphrase specified.";
85 return OK;
88 if(passphrase != 0)
89 cmd += addUserId();
91 if(!recipients.isEmpty())
93 cmd += " --set-filename stdin";
95 QByteArray pgpUser = Module::getKpgp()->user();
96 if(Module::getKpgp()->encryptToSelf() && !pgpUser.isEmpty()) {
97 cmd += " -r 0x";
98 cmd += pgpUser;
101 for( KeyIDList::ConstIterator it = recipients.begin();
102 it != recipients.end(); ++it ) {
103 cmd += " -r 0x";
104 cmd += (*it);
108 clear();
109 input = block.text();
110 exitStatus = runGpg(cmd.data(), passphrase);
111 if( !output.isEmpty() )
112 block.setProcessedText( output );
113 block.setError( error );
115 if( exitStatus != 0 )
117 // this error message is later hopefully overwritten
118 errMsg = i18n( "Unknown error." );
119 status = ERROR;
122 #if 0
123 // #### FIXME: As we check the keys ourselves the following problems
124 // shouldn't occur. Therefore I don't handle them for now.
125 // IK 01/2002
126 if(!recipients.isEmpty())
128 int index = 0;
129 bool bad = false;
130 unsigned int num = 0;
131 QByteArray badkeys = "";
132 // Examples:
133 // gpg: 0x12345678: skipped: public key not found
134 // gpg: 0x12345678: skipped: public key is disabled
135 // gpg: 0x12345678: skipped: unusable public key
136 // (expired or revoked key)
137 // gpg: 23456789: no info to calculate a trust probability
138 // (untrusted key, 23456789 is the key Id of the encryption sub key)
139 while((index = error.indexOf("skipped: ",index) ) != -1 )
141 bad = true;
142 index = error.indexOf('\'',index);
143 int index2 = error.indexOf('\'',index+1);
144 badkeys += error.mid(index, index2-index+1) + ", ";
145 num++;
147 if(bad)
149 badkeys.trimmed();
150 if(num == recipients.count())
151 errMsg = i18n("Could not find public keys matching the userid(s)\n"
152 "%1;\n"
153 "the message is not encrypted.",
154 badkeys.data() );
155 else
156 errMsg = i18n("Could not find public keys matching the userid(s)\n"
157 "%1;\n"
158 "these persons will not be able to read the message.",
159 badkeys.data() );
160 status |= MISSINGKEY;
161 status |= ERROR;
164 #endif
165 if( passphrase != 0 )
167 // Example 1 (bad passphrase, clearsign only):
168 // gpg: skipped `0x12345678': bad passphrase
169 // gpg: [stdin]: clearsign failed: bad passphrase
170 // Example 2 (bad passphrase, sign & encrypt):
171 // gpg: skipped `0x12345678': bad passphrase
172 // gpg: [stdin]: sign+encrypt failed: bad passphrase
173 // Example 3 (unusable secret key, clearsign only):
174 // gpg: skipped `0x12345678': unusable secret key
175 // gpg: [stdin]: clearsign failed: unusable secret key
176 // Example 4 (unusable secret key, sign & encrypt):
177 // gpg: skipped `0xAC0EB35D': unusable secret key
178 // gpg: [stdin]: sign+encrypt failed: unusable secret key
179 if( error.contains("bad passphrase") )
181 errMsg = i18n("Signing failed because the passphrase is wrong.");
182 status |= BADPHRASE;
183 status |= ERR_SIGNING;
184 status |= ERROR;
186 else if( error.contains("unusable secret key") )
188 errMsg = i18n("Signing failed because your secret key is unusable.");
189 status |= ERR_SIGNING;
190 status |= ERROR;
192 else if( !( status & ERROR ) )
194 //kDebug( 5326 ) <<"Base: Good Passphrase!";
195 status |= SIGNED;
199 //kDebug( 5326 ) <<"status =" << status;
200 block.setStatus( status );
201 return status;
206 BaseG::decrypt( Block& block, const char *passphrase )
208 int index, index2;
209 int exitStatus = 0;
211 clear();
212 input = block.text();
213 exitStatus = runGpg("--batch --decrypt", passphrase);
214 if( !output.isEmpty() && ( !error.contains( "gpg: quoted printable" ) ) )
215 block.setProcessedText( output );
216 block.setError( error );
218 if(exitStatus == -1) {
219 errMsg = i18n("Error running gpg");
220 status = ERROR;
221 block.setStatus( status );
222 return status;
225 // Example 1 (good passphrase, decryption successful):
226 // gpg: encrypted with 2048-bit ELG-E key, ID 12345678, created 2000-11-11
227 // "Foo Bar <foo@bar.xyz>"
229 // Example 2 (bad passphrase):
230 // gpg: encrypted with 1024-bit RSA key, ID 12345678, created 1991-01-01
231 // "Foo Bar <foo@bar.xyz>"
232 // gpg: public key decryption failed: bad passphrase
233 // gpg: decryption failed: secret key not available
235 // Example 3 (no secret key available):
236 // gpg: encrypted with RSA key, ID 12345678
237 // gpg: decryption failed: secret key not available
239 // Example 4 (good passphrase for second key, decryption successful):
240 // gpg: encrypted with 2048-bit ELG-E key, ID 12345678, created 2000-01-01
241 // "Foo Bar (work) <foo@bar.xyz>"
242 // gpg: public key decryption failed: bad passphrase
243 // gpg: encrypted with 2048-bit ELG-E key, ID 23456789, created 2000-02-02
244 // "Foo Bar (home) <foo@bar.xyz>"
246 // Example 5: passphrase dialog cancelled by user
247 // gpg: cancelled by user
248 // gpg: encrypted with 2048-bit ELG key, ID XXXXXXXX, created 2006-11-16
249 // "Foo Bar <foobar@foo.org>"
250 // gpg: public key decryption failed: General error [..]
251 if( error.contains( "gpg: encrypted with" ) )
253 //kDebug( 5326 ) <<"kpgpbase: message is encrypted";
254 status |= ENCRYPTED;
255 if( error.contains( "\ngpg: decryption failed" ) )
257 if( ( index = error.indexOf( "bad passphrase" ) ) != -1 )
259 if( passphrase != 0 )
261 errMsg = i18n( "Bad passphrase; could not decrypt." );
262 kDebug( 5326 ) <<"Base: passphrase is bad";
263 status |= BADPHRASE;
264 status |= ERROR;
266 else
268 // Search backwards the user ID of the needed key
269 index2 = error.lastIndexOf('"', index) - 1;
270 index = error.lastIndexOf(" \"", index2) + 7;
271 // The conversion from UTF8 is necessary because gpg stores and
272 // prints user IDs in UTF8
273 block.setRequiredUserId( QString::fromUtf8( error.mid( index, index2 - index + 1 ) ) );
274 kDebug( 5326 ) <<"Base: key needed is \"" << block.requiredUserId() <<"\"!";
277 else if( error.contains( "secret key not available" ) )
279 // no secret key fitting this message
280 status |= NO_SEC_KEY;
281 status |= ERROR;
282 errMsg = i18n("You do not have the secret key needed to decrypt this message.");
283 kDebug( 5326 ) <<"Base: no secret key for this message";
285 else if( error.contains( "cancelled by user" ) )
287 status |= CANCEL;
288 status |= ERROR;
289 errMsg = i18n("The passphrase dialog was cancelled.");
290 kDebug( 5326 ) << errMsg;
293 // check for persons
294 #if 0
295 // ##### FIXME: This information is anyway currently not used
296 // I'll change it to always determine the recipients.
297 index = error.indexOf("can only be read by:");
298 if(index != -1)
300 index = error.indexOf('\n',index);
301 int end = error.indexOf("\n\n",index);
303 mRecipients.clear();
304 while( (index2 = error.indexOf('\n',index+1)) <= end )
306 QByteArray item = error.mid(index+1,index2-index-1);
307 item.trimmed();
308 mRecipients.append(item);
309 index = index2;
312 #endif
315 // Example 1 (unknown signature key):
316 // gpg: Signature made Wed 02 Jan 2002 11:26:33 AM CET using DSA key ID 2E250C64
317 // gpg: Can't check signature: public key not found
318 if( ( index = error.indexOf("Signature made") ) != -1 )
320 //kDebug( 5326 ) <<"Base: message is signed";
321 status |= SIGNED;
322 // get signature date and signature key ID
323 // Example: Signature made Sun 06 May 2001 03:49:27 PM CEST using DSA key ID 12345678
324 index2 = error.indexOf("using", index+15);
325 block.setSignatureDate( error.mid(index+15, index2-(index+15)-1) );
326 kDebug( 5326 ) <<"Message was signed on '" << block.signatureDate() <<"'";
327 index2 = error.indexOf("key ID ", index2) + 7;
328 block.setSignatureKeyId( error.mid(index2,8) );
329 kDebug( 5326 ) <<"Message was signed with key '" << block.signatureKeyId() <<"'";
330 // move index to start of next line
331 index = error.indexOf('\n', index2)+1;
333 if ((error.indexOf("Key matching expected", index) != -1 )
334 || (error.indexOf("Can't check signature", index) != -1 ))
336 status |= UNKNOWN_SIG;
337 status |= GOODSIG;
338 block.setSignatureUserId( QString() );
340 else if( error.indexOf("Good signature", index) != -1 )
342 status |= GOODSIG;
343 // get the primary user ID of the signer
344 index = error.indexOf('"',index);
345 index2 = error.indexOf('\n',index+1);
346 index2 = error.lastIndexOf('"', index2-1);
347 block.setSignatureUserId( error.mid( index+1, index2-index-1 ) );
349 else if( error.indexOf("BAD signature", index) != -1 )
351 //kDebug( 5326 ) <<"BAD signature";
352 status |= ERROR;
353 // get the primary user ID of the signer
354 index = error.indexOf('"',index);
355 index2 = error.indexOf('\n',index+1);
356 index2 = error.lastIndexOf('"', index2-1);
357 block.setSignatureUserId( error.mid( index+1, index2-index-1 ) );
359 else if( error.indexOf("Can't find the right public key", index) != -1 )
361 // #### fix this hack
362 // I think this can't happen anymore because if the pubring is missing
363 // the current GnuPG creates a new empty one.
364 status |= UNKNOWN_SIG;
365 status |= GOODSIG; // this is a hack...
366 block.setSignatureUserId( i18n("??? (file ~/.gnupg/pubring.gpg not found)") );
368 else
370 status |= ERROR;
371 block.setSignatureUserId( QString() );
374 //kDebug( 5326 ) <<"status =" << status;
375 block.setStatus( status );
376 return status;
380 Key*
381 BaseG::readPublicKey( const KeyID& keyID,
382 const bool readTrust /* = false */,
383 Key* key /* = 0 */ )
385 int exitStatus = 0;
387 status = 0;
388 if( readTrust )
389 exitStatus = runGpg( "--batch --list-public-keys --with-fingerprint --with-colons --fixed-list-mode 0x" + keyID, 0, true );
390 else
391 exitStatus = runGpg( "--batch --list-public-keys --with-fingerprint --with-colons --fixed-list-mode --no-expensive-trust-checks 0x" + keyID, 0, true );
393 if(exitStatus != 0) {
394 status = ERROR;
395 return 0;
398 int offset;
399 // search start of key data
400 if( !strncmp( output.data(), "pub:", 4 ) )
401 offset = 0;
402 else {
403 offset = output.indexOf( "\npub:" );
404 if( offset == -1 )
405 return 0;
406 else
407 offset++;
410 key = parseKeyData( output, offset, key );
412 return key;
416 KeyList
417 BaseG::publicKeys( const QStringList & patterns )
419 int exitStatus = 0;
421 // the option --with-colons should be used for interprocess communication
422 // with gpg (according to Werner Koch)
423 QByteArray cmd = "--batch --list-public-keys --with-fingerprint --with-colons "
424 "--fixed-list-mode --no-expensive-trust-checks";
425 for ( QStringList::ConstIterator it = patterns.begin();
426 it != patterns.end(); ++it ) {
427 cmd += ' ';
428 cmd += KShell::quoteArg( *it ).toLocal8Bit();
430 status = 0;
431 exitStatus = runGpg( cmd, 0, true );
433 if(exitStatus != 0) {
434 status = ERROR;
435 return KeyList();
438 // now we need to parse the output for public keys
439 KeyList publicKeys = parseKeyList(output, false);
441 // sort the list of public keys
442 std::sort( publicKeys.begin(), publicKeys.end(), KeyCompare );
444 return publicKeys;
448 KeyList
449 BaseG::secretKeys( const QStringList & patterns )
451 int exitStatus = 0;
453 // the option --with-colons should be used for interprocess communication
454 // with gpg (according to Werner Koch)
455 QByteArray cmd = "--batch --list-secret-keys --with-fingerprint --with-colons "
456 "--fixed-list-mode";
457 for ( QStringList::ConstIterator it = patterns.begin();
458 it != patterns.end(); ++it ) {
459 cmd += ' ';
460 cmd += KShell::quoteArg( *it ).toLocal8Bit();
462 status = 0;
463 exitStatus = runGpg( cmd, 0, true );
465 if(exitStatus != 0) {
466 status = ERROR;
467 return KeyList();
470 // now we need to parse the output for secret keys
471 KeyList secretKeys = parseKeyList(output, true);
473 // sort the list of secret keys
474 std::sort( secretKeys.begin(), secretKeys.end(), KeyCompare );
476 return secretKeys;
481 BaseG::signKey(const KeyID& keyID, const char *passphrase)
483 QByteArray cmd;
484 int exitStatus = 0;
486 cmd = "--batch";
487 cmd += addUserId();
488 cmd += " --sign-key 0x";
489 cmd += keyID;
491 status = 0;
492 exitStatus = runGpg(cmd.data(), passphrase);
494 if (exitStatus != 0)
495 status = ERROR;
497 return status;
501 QByteArray
502 BaseG::getAsciiPublicKey(const KeyID& keyID)
504 int exitStatus = 0;
506 if (keyID.isEmpty())
507 return QByteArray();
509 status = 0;
510 exitStatus = runGpg("--batch --armor --export 0x" + keyID, 0, true);
512 if(exitStatus != 0) {
513 status = ERROR;
514 return QByteArray();
517 return output;
521 Key*
522 BaseG::parseKeyData( const QByteArray& output, int& offset, Key* key /* = 0 */ )
523 // This function parses the data for a single key which is output by GnuPG
524 // with the following command line arguments:
525 // --batch --list-public-keys --with-fingerprint --with-colons
526 // --fixed-list-mode [--no-expensive-trust-checks]
527 // It expects the key data to start at offset and returns the start of
528 // the next key's data in offset.
529 // Subkeys are currently ignored.
531 int index = offset;
533 if( ( strncmp( output.data() + offset, "pub:", 4 ) != 0 )
534 && ( strncmp( output.data() + offset, "sec:", 4 ) != 0 ) ) {
535 return 0;
538 if( key == 0 )
539 key = new Key();
540 else
541 key->clear();
543 QByteArray keyID;
544 bool firstKey = true;
546 while( true )
548 int eol;
549 // search the end of the current line
550 if( ( eol = output.indexOf( '\n', index ) ) == -1 )
551 break;
553 bool bIsPublicKey = false;
554 if( ( bIsPublicKey = !strncmp( output.data() + index, "pub:", 4 ) )
555 || !strncmp( output.data() + index, "sec:", 4 ) )
556 { // line contains primary key data
557 // Example: pub:f:1024:17:63CB691DFAEBD5FC:860451781::379:-:::scESC:
559 // abort parsing if we found the start of the next key
560 if( !firstKey )
561 break;
562 firstKey = false;
564 key->setSecret( !bIsPublicKey );
566 Subkey *subkey = new Subkey( QByteArray(), !bIsPublicKey );
568 int pos = index + 4; // begin of 2nd field
569 int pos2 = output.indexOf( ':', pos );
570 for( int field = 2; field <= 12; field++ )
572 switch( field )
574 case 2: // the calculated trust
575 if( pos2 > pos )
577 switch( output[pos] )
579 case 'o': // unknown (this key is new to the system)
580 break;
581 case 'i': // the key is invalid, e.g. missing self-signature
582 subkey->setInvalid( true );
583 key->setInvalid( true );
584 break;
585 case 'd': // the key has been disabled
586 subkey->setDisabled( true );
587 key->setDisabled( true );
588 break;
589 case 'r': // the key has been revoked
590 subkey->setRevoked( true );
591 key->setRevoked( true );
592 break;
593 case 'e': // the key has expired
594 subkey->setExpired( true );
595 key->setExpired( true );
596 break;
597 case '-': // undefined (no path leads to the key)
598 case 'q': // undefined (no trusted path leads to the key)
599 case 'n': // don't trust this key at all
600 case 'm': // the key is marginally trusted
601 case 'f': // the key is fully trusted
602 case 'u': // the key is ultimately trusted (secret key available)
603 // These values are ignored since we determine the key trust
604 // from the trust values of the user ids.
605 break;
606 default:
607 kDebug( 5326 ) <<"Unknown trust value";
610 break;
611 case 3: // length of key in bits
612 if( pos2 > pos )
613 subkey->setKeyLength( output.mid( pos, pos2-pos ).toUInt() );
614 break;
615 case 4: // the key algorithm
616 if( pos2 > pos )
617 subkey->setKeyAlgorithm( output.mid( pos, pos2-pos ).toUInt() );
618 break;
619 case 5: // the long key id
620 keyID = output.mid( pos, pos2-pos );
621 subkey->setKeyID( keyID );
622 break;
623 case 6: // the creation date (in seconds since 1970-01-01 00:00:00)
624 if( pos2 > pos )
625 subkey->setCreationDate( QString(output.mid( pos, pos2-pos )).toLong() );
626 break;
627 case 7: // the expiration date (in seconds since 1970-01-01 00:00:00)
628 if( pos2 > pos )
629 subkey->setExpirationDate( QString(output.mid( pos, pos2-pos )).toLong() );
630 else
631 subkey->setExpirationDate( -1 ); // key expires never
632 break;
633 case 8: // local ID (ignored)
634 case 9: // Ownertrust (ignored for now)
635 case 10: // User-ID (always empty in --fixed-list-mode)
636 case 11: // signature class (always empty except for key signatures)
637 break;
638 case 12: // key capabilities
639 for( int i=pos; i<pos2; i++ )
640 switch( output[i] )
642 case 'e':
643 subkey->setCanEncrypt( true );
644 break;
645 case 's':
646 subkey->setCanSign( true );
647 break;
648 case 'c':
649 subkey->setCanCertify( true );
650 break;
651 case 'E':
652 key->setCanEncrypt( true );
653 break;
654 case 'S':
655 key->setCanSign( true );
656 break;
657 case 'C':
658 key->setCanCertify( true );
659 break;
660 default:
661 kDebug( 5326 ) <<"Unknown key capability";
663 break;
665 pos = pos2 + 1;
666 pos2 = output.indexOf( ':', pos );
668 key->addSubkey( subkey );
670 else if( !strncmp( output.data() + index, "uid:", 4 ) )
671 { // line contains a user id
672 // Example: uid:f::::::::Philip R. Zimmermann <prz@pgp.com>:
674 UserID *userID = new UserID( "" );
676 int pos = index + 4; // begin of 2nd field
677 int pos2 = output.indexOf( ':', pos );
678 for( int field=2; field <= 10; field++ )
680 switch( field )
682 case 2: // the calculated trust
683 if( pos2 > pos )
685 switch( output[pos] )
687 case 'i': // the user id is invalid, e.g. missing self-signature
688 userID->setInvalid( true );
689 break;
690 case 'r': // the user id has been revoked
691 userID->setRevoked( true );
692 break;
693 case '-': // undefined (no path leads to the key)
694 case 'q': // undefined (no trusted path leads to the key)
695 userID->setValidity( KPGP_VALIDITY_UNDEFINED );
696 break;
697 case 'n': // don't trust this key at all
698 userID->setValidity( KPGP_VALIDITY_NEVER );
699 break;
700 case 'm': // the key is marginally trusted
701 userID->setValidity( KPGP_VALIDITY_MARGINAL );
702 break;
703 case 'f': // the key is fully trusted
704 userID->setValidity( KPGP_VALIDITY_FULL );
705 break;
706 case 'u': // the key is ultimately trusted (secret key available)
707 userID->setValidity( KPGP_VALIDITY_ULTIMATE );
708 break;
709 default:
710 kDebug( 5326 ) <<"Unknown trust value";
713 break;
714 case 3: // these fields are empty
715 case 4:
716 case 5:
717 case 6:
718 case 7:
719 case 8:
720 case 9:
721 break;
722 case 10: // User-ID
723 QByteArray uid = output.mid( pos, pos2-pos );
724 // replace "\xXX" with the corresponding character;
725 // other escaped characters, i.e. \n, \r etc., are ignored
726 // because they shouldn't appear in user IDs
727 for ( int idx = 0 ; (idx = uid.indexOf( "\\x", idx ) != -1) ; ++idx ) {
728 char str[2] = "x";
729 str[0] = (char) QString( uid.mid( idx + 2, 2 ) ).toShort( 0, 16 );
730 uid.replace( idx, 4, str );
732 QString uidString = QString::fromUtf8( uid.data() );
733 // check whether uid was utf-8 encoded
734 bool isUtf8 = true;
735 for ( int i = 0; i + 1 < uidString.length(); ++i ) {
736 if ( uidString[i].unicode() == 0xdbff &&
737 uidString[i+1].row() == 0xde ) {
738 // we found a non-Unicode character (see QString::fromUtf8())
739 isUtf8 = false;
740 break;
743 if( !isUtf8 ) {
744 // The user id isn't utf-8 encoded. It was most likely
745 // created with PGP which either used latin1 or koi8-r.
746 kDebug( 5326 ) <<"User Id '" << uid
747 << "' doesn't seem to be utf-8 encoded.";
749 // We determine the ratio between non-ASCII and ASCII chars.
750 // A koi8-r user id should have lots of non-ASCII chars.
751 int nonAsciiCount = 0, asciiCount = 0;
753 // We only look at the first part of the user id (i. e. everything
754 // before the email address resp. before a comment)
755 for( signed char* ch = (signed char*)uid.data();
756 *ch && ( *ch != '(' ) && ( *ch != '<' );
757 ++ch ) {
758 if( ( ( *ch >= 'A' ) && ( *ch <= 'Z' ) )
759 || ( ( *ch >= 'a' ) && ( *ch <= 'z' ) ) )
760 ++asciiCount;
761 else if( *ch < 0 )
762 ++nonAsciiCount;
764 kDebug( 5326 ) <<"ascii-nonAscii ratio :" << asciiCount
765 << ":" << nonAsciiCount;
766 if( nonAsciiCount > asciiCount ) {
767 // assume koi8-r encoding
768 kDebug( 5326 ) <<"Assume koi8-r encoding.";
769 QTextCodec *codec = QTextCodec::codecForName("KOI8-R");
770 uidString = codec->toUnicode( uid.data() );
771 // check the case of the first two characters to find out
772 // whether the user id is probably CP1251 encoded (for some
773 // reason in CP1251 the lower case characters have smaller
774 // codes than the upper case characters, so if the first char
775 // of the koi8-r decoded user id is lower case and the second
776 // char is upper case then it's likely that the user id is
777 // CP1251 encoded)
778 if( ( uidString.length() >= 2 )
779 && ( uidString[0].toLower() == uidString[0] )
780 && ( uidString[1].toUpper() == uidString[1] ) ) {
781 // koi8-r decoded user id has inverted case, so assume
782 // CP1251 encoding
783 kDebug( 5326 ) <<"No, it doesn't seem to be koi8-r."
784 "Use CP 1251 instead.";
785 QTextCodec *codec = QTextCodec::codecForName("CP1251");
786 uidString = codec->toUnicode( uid.data() );
789 else {
790 // assume latin1 encoding
791 kDebug( 5326 ) <<"Assume latin1 encoding.";
792 uidString = QString::fromLatin1( uid.data() );
795 userID->setText( uidString );
796 break;
798 pos = pos2 + 1;
799 pos2 = output.indexOf( ':', pos );
802 // user IDs are printed in UTF-8 by gpg (if one uses --with-colons)
803 key->addUserID( userID );
805 else if( !strncmp( output.data() + index, "fpr:", 4 ) )
806 { // line contains a fingerprint
807 // Example: fpr:::::::::17AFBAAF21064E513F037E6E63CB691DFAEBD5FC:
809 if (key == 0) // invalid key data
810 break;
812 // search the fingerprint (it's in the 10th field)
813 int pos = index + 4;
814 for( int i = 0; i < 8; i++ )
815 pos = output.indexOf( ':', pos ) + 1;
816 int pos2 = output.indexOf( ':', pos );
818 key->setFingerprint( keyID, output.mid( pos, pos2-pos ) );
820 index = eol + 1;
823 //kDebug( 5326 ) <<"finished parsing key data";
825 offset = index;
827 return key;
831 KeyList
832 BaseG::parseKeyList( const QByteArray& output, bool secretKeys )
834 KeyList keys;
835 Key *key = 0;
836 int offset;
838 // search start of key data
839 if( !strncmp( output.data(), "pub:", 4 )
840 || !strncmp( output.data(), "sec:", 4 ) )
841 offset = 0;
842 else {
843 if( secretKeys )
844 offset = output.indexOf( "\nsec:" );
845 else
846 offset = output.indexOf( "\npub:" );
847 if( offset == -1 )
848 return keys;
849 else
850 offset++;
853 do {
854 key = parseKeyData( output, offset );
856 if( key != 0 )
857 keys.append( key );
859 while( key != 0 );
861 //kDebug( 5326 ) <<"finished parsing keys";
863 return keys;
867 } // namespace Kpgp