libkpgp

kpgpbaseG.cpp
1/*
2 kpgpbaseG.cpp
3
4 Copyright (C) 2001,2002 the KPGP authors
5 See file AUTHORS.kpgp for details
6
7 This file is part of KPGP, the KDE PGP/GnuPG support library.
8
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.
13
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
17 */
18
19#ifdef HAVE_CONFIG_H
20#include <config.h>
21#endif
22
23#include "kpgpbase.h"
24#include "kpgp.h"
25
26#include <tdelocale.h>
27#include <tdeprocess.h>
28#include <kdebug.h>
29
30#include <tqtextcodec.h>
31
32#include <string.h> /* strncmp */
33
34namespace Kpgp {
35
36BaseG::BaseG()
37 : Base()
38{
39 // determine the version of gpg (the method is equivalent to gpgme's method)
40 runGpg( "--version", 0 );
41 int eol = output.find( '\n' );
42 if( eol > 0 ) {
43 int pos = output.findRev( ' ', eol - 1 );
44 if( pos != -1 ) {
45 mVersion = output.mid( pos + 1, eol - pos - 1 );
46 kdDebug(5100) << "found GnuPG " << mVersion << endl;
47 }
48 }
49}
50
51
52BaseG::~BaseG()
53{
54}
55
56
57int
58BaseG::encrypt( Block& block, const KeyIDList& recipients )
59{
60 return encsign( block, recipients, 0 );
61}
62
63
64int
65BaseG::clearsign( Block& block, const TQString &passphrase )
66{
67 return encsign( block, KeyIDList(), passphrase );
68}
69
70
71int
72BaseG::encsign( Block& block, const KeyIDList& recipients,
73 const TQString &passphrase )
74{
75 TQCString cmd;
76 int exitStatus = 0;
77
78 if (!recipients.isEmpty() && !passphrase.isEmpty())
79 cmd = "--batch --armor --sign --encrypt --textmode";
80 else if(!recipients.isEmpty())
81 cmd = "--batch --armor --encrypt --textmode";
82 else if (!passphrase.isEmpty())
83 cmd = "--batch --escape-from --clearsign";
84 else
85 {
86 kdDebug(5100) << "kpgpbase: Neither recipients nor passphrase specified." << endl;
87 return OK;
88 }
89
90 if (!passphrase.isEmpty())
91 cmd += addUserId();
92
93 if(!recipients.isEmpty())
94 {
95 cmd += " --set-filename stdin";
96
97 TQCString pgpUser = Module::getKpgp()->user();
98 if(Module::getKpgp()->encryptToSelf() && !pgpUser.isEmpty()) {
99 cmd += " -r 0x";
100 cmd += pgpUser;
101 }
102
103 for( KeyIDList::ConstIterator it = recipients.begin();
104 it != recipients.end(); ++it ) {
105 cmd += " -r 0x";
106 cmd += (*it);
107 }
108 }
109
110 clear();
111 input = block.text();
112 exitStatus = runGpg(cmd.data(), passphrase);
113 if( !output.isEmpty() )
114 block.setProcessedText( output );
115 block.setError( error );
116
117 if( exitStatus != 0 )
118 {
119 // this error message is later hopefully overwritten
120 errMsg = i18n( "Unknown error." );
121 status = ERROR;
122 }
123
124#if 0
125 // #### FIXME: As we check the keys ourselves the following problems
126 // shouldn't occur. Therefore I don't handle them for now.
127 // IK 01/2002
128 if(!recipients.isEmpty())
129 {
130 int index = 0;
131 bool bad = FALSE;
132 unsigned int num = 0;
133 TQCString badkeys = "";
134 // Examples:
135 // gpg: 0x12345678: skipped: public key not found
136 // gpg: 0x12345678: skipped: public key is disabled
137 // gpg: 0x12345678: skipped: unusable public key
138 // (expired or revoked key)
139 // gpg: 23456789: no info to calculate a trust probability
140 // (untrusted key, 23456789 is the key Id of the encryption sub key)
141 while((index = error.find("skipped: ",index)) != -1)
142 {
143 bad = TRUE;
144 index = error.find('\'',index);
145 int index2 = error.find('\'',index+1);
146 badkeys += error.mid(index, index2-index+1) + ", ";
147 num++;
148 }
149 if(bad)
150 {
151 badkeys.stripWhiteSpace();
152 if(num == recipients.count())
153 errMsg = i18n("Could not find public keys matching the userid(s)\n"
154 "%1;\n"
155 "the message is not encrypted.")
156 .arg( badkeys.data() );
157 else
158 errMsg = i18n("Could not find public keys matching the userid(s)\n"
159 "%1;\n"
160 "these persons will not be able to read the message.")
161 .arg( badkeys.data() );
162 status |= MISSINGKEY;
163 status |= ERROR;
164 }
165 }
166#endif
167 if (!passphrase.isEmpty())
168 {
169 // Example 1 (bad passphrase, clearsign only):
170 // gpg: skipped `0x12345678': bad passphrase
171 // gpg: [stdin]: clearsign failed: bad passphrase
172 // Example 2 (bad passphrase, sign & encrypt):
173 // gpg: skipped `0x12345678': bad passphrase
174 // gpg: [stdin]: sign+encrypt failed: bad passphrase
175 // Example 3 (unusable secret key, clearsign only):
176 // gpg: skipped `0x12345678': unusable secret key
177 // gpg: [stdin]: clearsign failed: unusable secret key
178 // Example 4 (unusable secret key, sign & encrypt):
179 // gpg: skipped `0xAC0EB35D': unusable secret key
180 // gpg: [stdin]: sign+encrypt failed: unusable secret key
181 if( error.find("bad passphrase") != -1 )
182 {
183 errMsg = i18n("Signing failed because the passphrase is wrong.");
184 status |= BADPHRASE;
185 status |= ERR_SIGNING;
186 status |= ERROR;
187 }
188 else if( error.find("unusable secret key") != -1 )
189 {
190 errMsg = i18n("Signing failed because your secret key is unusable.");
191 status |= ERR_SIGNING;
192 status |= ERROR;
193 }
194 else if( !( status & ERROR ) )
195 {
196 //kdDebug(5100) << "Base: Good Passphrase!" << endl;
197 status |= SIGNED;
198 }
199 }
200
201 //kdDebug(5100) << "status = " << status << endl;
202 block.setStatus( status );
203 return status;
204}
205
206
207int
208BaseG::decrypt( Block& block, const TQString &passphrase )
209{
210 int index, index2;
211 int exitStatus = 0;
212
213 clear();
214 input = block.text();
215 exitStatus = runGpg("--batch --decrypt", passphrase);
216 if( !output.isEmpty() && ( error.find( "gpg: quoted printable" ) == -1 ) )
217 block.setProcessedText( output );
218 block.setError( error );
219
220 if(exitStatus == -1) {
221 errMsg = i18n("Error running gpg");
222 status = RUN_ERR;
223 block.setStatus( status );
224 return status;
225 }
226
227 // Example 1 (good passphrase, decryption successful):
228 // gpg: encrypted with 2048-bit ELG-E key, ID 12345678, created 2000-11-11
229 // "Foo Bar <foo@bar.xyz>"
230 //
231 // Example 2 (bad passphrase):
232 // gpg: encrypted with 1024-bit RSA key, ID 12345678, created 1991-01-01
233 // "Foo Bar <foo@bar.xyz>"
234 // gpg: public key decryption failed: bad passphrase
235 // gpg: decryption failed: secret key not available
236 //
237 // Example 3 (no secret key available):
238 // gpg: encrypted with RSA key, ID 12345678
239 // gpg: decryption failed: secret key not available
240 //
241 // Example 4 (good passphrase for second key, decryption successful):
242 // gpg: encrypted with 2048-bit ELG-E key, ID 12345678, created 2000-01-01
243 // "Foo Bar (work) <foo@bar.xyz>"
244 // gpg: public key decryption failed: bad passphrase
245 // gpg: encrypted with 2048-bit ELG-E key, ID 23456789, created 2000-02-02
246 // "Foo Bar (home) <foo@bar.xyz>"
247 if( error.find( "gpg: encrypted with" ) != -1 )
248 {
249 //kdDebug(5100) << "kpgpbase: message is encrypted" << endl;
250 status |= ENCRYPTED;
251 if( error.find( "\ngpg: decryption failed" ) != -1 )
252 {
253 if( ( index = error.find( "bad passphrase" ) ) != -1 )
254 {
255 if (!passphrase.isEmpty())
256 {
257 errMsg = i18n( "Bad passphrase; could not decrypt." );
258 kdDebug(5100) << "Base: passphrase is bad" << endl;
259 status |= BADPHRASE;
260 status |= ERROR;
261 }
262 else
263 {
264 // Search backwards the user ID of the needed key
265 index2 = error.findRev('"', index) - 1;
266 index = error.findRev(" \"", index2) + 7;
267 // The conversion from UTF8 is necessary because gpg stores and
268 // prints user IDs in UTF8
269 block.setRequiredUserId( TQString::fromUtf8( error.mid( index, index2 - index + 1 ) ) );
270 kdDebug(5100) << "Base: key needed is \"" << block.requiredUserId() << "\"!" << endl;
271 }
272 }
273 else if( error.find( "secret key not available" ) != -1 )
274 {
275 // no secret key fitting this message
276 status |= NO_SEC_KEY;
277 status |= ERROR;
278 errMsg = i18n("You do not have the secret key needed to decrypt this message.");
279 kdDebug(5100) << "Base: no secret key for this message" << endl;
280 }
281 }
282 // check for persons
283#if 0
284 // ##### FIXME: This information is anyway currently not used
285 // I'll change it to always determine the recipients.
286 index = error.find("can only be read by:");
287 if(index != -1)
288 {
289 index = error.find('\n',index);
290 int end = error.find("\n\n",index);
291
292 mRecipients.clear();
293 while( (index2 = error.find('\n',index+1)) <= end )
294 {
295 TQCString item = error.mid(index+1,index2-index-1);
296 item.stripWhiteSpace();
297 mRecipients.append(item);
298 index = index2;
299 }
300 }
301#endif
302 }
303
304 // Example 1 (unknown signature key):
305 // gpg: Signature made Wed 02 Jan 2002 11:26:33 AM CET using DSA key ID 2E250C64
306 // gpg: Can't check signature: public key not found
307 if((index = error.find("Signature made")) != -1)
308 {
309 //kdDebug(5100) << "Base: message is signed" << endl;
310 status |= SIGNED;
311 // get signature date and signature key ID
312 // Example: Signature made Sun 06 May 2001 03:49:27 PM CEST using DSA key ID 12345678
313 index2 = error.find("using", index+15);
314 block.setSignatureDate( error.mid(index+15, index2-(index+15)-1) );
315 kdDebug(5100) << "Message was signed on '" << block.signatureDate() << "'\n";
316 // To handle gnupg > 2.1
317 // gpg: Signature made Thu 05 Apr 2018 10:02:50 PM CEST
318 // gpg: using DSA key A0CF1DC09533E5E87F54DB40F1EEB8CD9FB16A50
319 // gpg: Good signature from "deloptes <deloptes@gmail.com>" [ultimate]
320 // so we need extra check
321 if (error.contains("key ID") > 0) {
322 index2 = error.find("key ID ", index2) + 7;
323 block.setSignatureKeyId( error.mid(index2,8) );
324 }
325 else {
326 index2 = error.find("key ", index2) + 4;
327 // handle variable key size
328 // gpg: Signature made Mon 02 Apr 2018 03:15:08 PM CEST
329 // gpg: using DSA key 05C82CF57AD1DA46
330 // gpg: Can't check signature: No public key
331 int end = error.find("\n", index2);
332 block.setSignatureKeyId( error.mid(index2,end-index2) );
333 }
334 kdDebug(5100) << "Message was signed with key '" << block.signatureKeyId() << "'\n";
335 // move index to start of next line
336 index = error.find('\n', index2)+1;
337
338 if ((error.find("Key matching expected", index) != -1)
339 || (error.find("Can't check signature", index) != -1))
340 {
341 status |= UNKNOWN_SIG;
342 status |= GOODSIG;
343 block.setSignatureUserId( TQString() );
344 }
345 else if( error.find("Good signature", index) != -1 )
346 {
347 status |= GOODSIG;
348 // get the primary user ID of the signer
349 index = error.find('"',index);
350 index2 = error.find('\n',index+1);
351 index2 = error.findRev('"', index2-1);
352 block.setSignatureUserId( TQString::fromLocal8Bit( error.mid( index+1, index2-index-1 ) ) );
353 }
354 else if( error.find("BAD signature", index) != -1 )
355 {
356 //kdDebug(5100) << "BAD signature" << endl;
357 status |= ERROR;
358 // get the primary user ID of the signer
359 index = error.find('"',index);
360 index2 = error.find('\n',index+1);
361 index2 = error.findRev('"', index2-1);
362 block.setSignatureUserId( TQString::fromLocal8Bit( error.mid( index+1, index2-index-1 ) ) );
363 }
364 else if( error.find("Can't find the right public key", index) != -1 )
365 {
366 // #### fix this hack
367 // I think this can't happen anymore because if the pubring is missing
368 // the current GnuPG creates a new empty one.
369 status |= UNKNOWN_SIG;
370 status |= GOODSIG; // this is a hack...
371 block.setSignatureUserId( i18n("??? (file ~/.gnupg/pubring.gpg not found)") );
372 }
373 else
374 {
375 status |= ERROR;
376 block.setSignatureUserId( TQString() );
377 }
378 }
379 //kdDebug(5100) << "status = " << status << endl;
380 block.setStatus( status );
381 return status;
382}
383
384
385Key*
386BaseG::readPublicKey( const KeyID& keyID,
387 const bool readTrust /* = false */,
388 Key* key /* = 0 */ )
389{
390 int exitStatus = 0;
391
392 status = 0;
393 if( readTrust )
394 exitStatus = runGpg( "--batch --list-public-keys --with-fingerprint --with-colons --fixed-list-mode 0x" + keyID, 0, true );
395 else
396 exitStatus = runGpg( "--batch --list-public-keys --with-fingerprint --with-colons --fixed-list-mode --no-expensive-trust-checks 0x" + keyID, 0, true );
397
398 if(exitStatus != 0) {
399 status = ERROR;
400 return 0;
401 }
402
403 int offset;
404 // search start of key data
405 if( !strncmp( output.data(), "pub:", 4 ) )
406 offset = 0;
407 else {
408 offset = output.find( "\npub:" );
409 if( offset == -1 )
410 return 0;
411 else
412 offset++;
413 }
414
415 key = parseKeyData( output, offset, key );
416
417 return key;
418}
419
420
421KeyList
422BaseG::publicKeys( const TQStringList & patterns )
423{
424 int exitStatus = 0;
425
426 // the option --with-colons should be used for interprocess communication
427 // with gpg (according to Werner Koch)
428 TQCString cmd = "--batch --list-public-keys --with-fingerprint --with-colons "
429 "--fixed-list-mode --no-expensive-trust-checks";
430 for ( TQStringList::ConstIterator it = patterns.begin();
431 it != patterns.end(); ++it ) {
432 cmd += " ";
433 cmd += TDEProcess::quote( *it ).local8Bit();
434 }
435 status = 0;
436 exitStatus = runGpg( cmd, 0, true );
437
438 if(exitStatus != 0) {
439 status = ERROR;
440 return KeyList();
441 }
442
443 // now we need to parse the output for public keys
444 KeyList publicKeys = parseKeyList(output, false);
445
446 // sort the list of public keys
447 publicKeys.sort();
448
449 return publicKeys;
450}
451
452
453KeyList
454BaseG::secretKeys( const TQStringList & patterns )
455{
456 int exitStatus = 0;
457
458 // the option --with-colons should be used for interprocess communication
459 // with gpg (according to Werner Koch)
460 TQCString cmd = "--batch --list-secret-keys --with-fingerprint --with-colons "
461 "--fixed-list-mode";
462 for ( TQStringList::ConstIterator it = patterns.begin();
463 it != patterns.end(); ++it ) {
464 cmd += " ";
465 cmd += TDEProcess::quote( *it ).local8Bit();
466 }
467 status = 0;
468 exitStatus = runGpg( cmd, 0, true );
469
470 if(exitStatus != 0) {
471 status = ERROR;
472 return KeyList();
473 }
474
475 // now we need to parse the output for secret keys
476 KeyList secretKeys = parseKeyList(output, true);
477
478 // sort the list of secret keys
479 secretKeys.sort();
480
481 return secretKeys;
482}
483
484
485int
486BaseG::signKey(const KeyID& keyID, const TQString &passphrase)
487{
488 TQCString cmd;
489 int exitStatus = 0;
490
491 cmd = "--batch";
492 cmd += addUserId();
493 cmd += " --sign-key 0x";
494 cmd += keyID;
495
496 status = 0;
497 exitStatus = runGpg(cmd.data(), passphrase);
498
499 if (exitStatus != 0)
500 status = ERROR;
501
502 return status;
503}
504
505
506TQCString
507BaseG::getAsciiPublicKey(const KeyID& keyID)
508{
509 int exitStatus = 0;
510
511 if (keyID.isEmpty())
512 return TQCString();
513
514 status = 0;
515 exitStatus = runGpg("--batch --armor --export 0x" + keyID, 0, true);
516
517 if(exitStatus != 0) {
518 status = ERROR;
519 return TQCString();
520 }
521
522 return output;
523}
524
525
526Key*
527BaseG::parseKeyData( const TQCString& output, int& offset, Key* key /* = 0 */ )
528// This function parses the data for a single key which is output by GnuPG
529// with the following command line arguments:
530// --batch --list-public-keys --with-fingerprint --with-colons
531// --fixed-list-mode [--no-expensive-trust-checks]
532// It expects the key data to start at offset and returns the start of
533// the next key's data in offset.
534// Subkeys are currently ignored.
535{
536 int index = offset;
537
538 if( ( strncmp( output.data() + offset, "pub:", 4 ) != 0 )
539 && ( strncmp( output.data() + offset, "sec:", 4 ) != 0 ) ) {
540 return 0;
541 }
542
543 if( key == 0 )
544 key = new Key();
545 else
546 key->clear();
547
548 TQCString keyID;
549 bool firstKey = true;
550
551 while( true )
552 {
553 int eol;
554 // search the end of the current line
555 if( ( eol = output.find( '\n', index ) ) == -1 )
556 break;
557
558 bool bIsPublicKey = false;
559 if( ( bIsPublicKey = !strncmp( output.data() + index, "pub:", 4 ) )
560 || !strncmp( output.data() + index, "sec:", 4 ) )
561 { // line contains primary key data
562 // Example: pub:f:1024:17:63CB691DFAEBD5FC:860451781::379:-:::scESC:
563
564 // abort parsing if we found the start of the next key
565 if( !firstKey )
566 break;
567 firstKey = false;
568
569 key->setSecret( !bIsPublicKey );
570
571 Subkey *subkey = new Subkey( TQCString(), !bIsPublicKey );
572
573 int pos = index + 4; // begin of 2nd field
574 int pos2 = output.find( ':', pos );
575 for( int field = 2; field <= 12; field++ )
576 {
577 switch( field )
578 {
579 case 2: // the calculated trust
580 if( pos2 > pos )
581 {
582 switch( output[pos] )
583 {
584 case 'o': // unknown (this key is new to the system)
585 break;
586 case 'i': // the key is invalid, e.g. missing self-signature
587 subkey->setInvalid( true );
588 key->setInvalid( true );
589 break;
590 case 'd': // the key has been disabled
591 subkey->setDisabled( true );
592 key->setDisabled( true );
593 break;
594 case 'r': // the key has been revoked
595 subkey->setRevoked( true );
596 key->setRevoked( true );
597 break;
598 case 'e': // the key has expired
599 subkey->setExpired( true );
600 key->setExpired( true );
601 break;
602 case '-': // undefined (no path leads to the key)
603 case 'q': // undefined (no trusted path leads to the key)
604 case 'n': // don't trust this key at all
605 case 'm': // the key is marginally trusted
606 case 'f': // the key is fully trusted
607 case 'u': // the key is ultimately trusted (secret key available)
608 // These values are ignored since we determine the key trust
609 // from the trust values of the user ids.
610 break;
611 default:
612 kdDebug(5100) << "Unknown trust value\n";
613 }
614 }
615 break;
616 case 3: // length of key in bits
617 if( pos2 > pos )
618 subkey->setKeyLength( output.mid( pos, pos2-pos ).toUInt() );
619 break;
620 case 4: // the key algorithm
621 if( pos2 > pos )
622 subkey->setKeyAlgorithm( output.mid( pos, pos2-pos ).toUInt() );
623 break;
624 case 5: // the long key id
625 keyID = output.mid( pos, pos2-pos );
626 subkey->setKeyID( keyID );
627 break;
628 case 6: // the creation date (in seconds since 1970-01-01 00:00:00)
629 if( pos2 > pos )
630 subkey->setCreationDate( output.mid( pos, pos2-pos ).toLong() );
631 break;
632 case 7: // the expiration date (in seconds since 1970-01-01 00:00:00)
633 if( pos2 > pos )
634 subkey->setExpirationDate( output.mid( pos, pos2-pos ).toLong() );
635 else
636 subkey->setExpirationDate( -1 ); // key expires never
637 break;
638 case 8: // local ID (ignored)
639 case 9: // Ownertrust (ignored for now)
640 case 10: // User-ID (always empty in --fixed-list-mode)
641 case 11: // signature class (always empty except for key signatures)
642 break;
643 case 12: // key capabilities
644 for( int i=pos; i<pos2; i++ )
645 switch( output[i] )
646 {
647 case 'e':
648 subkey->setCanEncrypt( true );
649 break;
650 case 's':
651 subkey->setCanSign( true );
652 break;
653 case 'c':
654 subkey->setCanCertify( true );
655 break;
656 case 'E':
657 key->setCanEncrypt( true );
658 break;
659 case 'S':
660 key->setCanSign( true );
661 break;
662 case 'C':
663 key->setCanCertify( true );
664 break;
665 default:
666 kdDebug(5100) << "Unknown key capability\n";
667 }
668 break;
669 }
670 pos = pos2 + 1;
671 pos2 = output.find( ':', pos );
672 }
673 key->addSubkey( subkey );
674 }
675 else if( !strncmp( output.data() + index, "uid:", 4 ) )
676 { // line contains a user id
677 // Example: uid:f::::::::Philip R. Zimmermann <prz@pgp.com>:
678
679 UserID *userID = new UserID( "" );
680
681 int pos = index + 4; // begin of 2nd field
682 int pos2 = output.find( ':', pos );
683 for( int field=2; field <= 10; field++ )
684 {
685 switch( field )
686 {
687 case 2: // the calculated trust
688 if( pos2 > pos )
689 {
690 switch( output[pos] )
691 {
692 case 'i': // the user id is invalid, e.g. missing self-signature
693 userID->setInvalid( true );
694 break;
695 case 'r': // the user id has been revoked
696 userID->setRevoked( true );
697 break;
698 case '-': // undefined (no path leads to the key)
699 case 'q': // undefined (no trusted path leads to the key)
700 userID->setValidity( KPGP_VALIDITY_UNDEFINED );
701 break;
702 case 'n': // don't trust this key at all
703 userID->setValidity( KPGP_VALIDITY_NEVER );
704 break;
705 case 'm': // the key is marginally trusted
706 userID->setValidity( KPGP_VALIDITY_MARGINAL );
707 break;
708 case 'f': // the key is fully trusted
709 userID->setValidity( KPGP_VALIDITY_FULL );
710 break;
711 case 'u': // the key is ultimately trusted (secret key available)
712 userID->setValidity( KPGP_VALIDITY_ULTIMATE );
713 break;
714 default:
715 kdDebug(5100) << "Unknown trust value\n";
716 }
717 }
718 break;
719 case 3: // these fields are empty
720 case 4:
721 case 5:
722 case 6:
723 case 7:
724 case 8:
725 case 9:
726 break;
727 case 10: // User-ID
728 TQCString uid = output.mid( pos, pos2-pos );
729 // replace "\xXX" with the corresponding character;
730 // other escaped characters, i.e. \n, \r etc., are ignored
731 // because they shouldn't appear in user IDs
732 for ( int idx = 0 ; (idx = uid.find( "\\x", idx )) >= 0 ; ++idx ) {
733 char str[2] = "x";
734 str[0] = (char) TQString( uid.mid( idx + 2, 2 ) ).toShort( 0, 16 );
735 uid.replace( idx, 4, str );
736 }
737 TQString uidString = TQString::fromUtf8( uid.data() );
738 // check whether uid was utf-8 encoded
739 bool isUtf8 = true;
740 for ( unsigned int i = 0; i + 1 < uidString.length(); ++i ) {
741 if ( uidString[i].unicode() == 0xdbff &&
742 uidString[i+1].row() == 0xde ) {
743 // we found a non-Unicode character (see TQString::fromUtf8())
744 isUtf8 = false;
745 break;
746 }
747 }
748 if( !isUtf8 ) {
749 // The user id isn't utf-8 encoded. It was most likely
750 // created with PGP which either used latin1 or koi8-r.
751 kdDebug(5100) << "User Id '" << uid
752 << "' doesn't seem to be utf-8 encoded." << endl;
753
754 // We determine the ratio between non-ASCII and ASCII chars.
755 // A koi8-r user id should have lots of non-ASCII chars.
756 int nonAsciiCount = 0, asciiCount = 0;
757
758 // We only look at the first part of the user id (i. e. everything
759 // before the email address resp. before a comment)
760 for( signed char* ch = (signed char*)uid.data();
761 *ch && ( *ch != '(' ) && ( *ch != '<' );
762 ++ch ) {
763 if( ( ( *ch >= 'A' ) && ( *ch <= 'Z' ) )
764 || ( ( *ch >= 'a' ) && ( *ch <= 'z' ) ) )
765 ++asciiCount;
766 else if( *ch < 0 )
767 ++nonAsciiCount;
768 }
769 kdDebug(5100) << "ascii-nonAscii ratio : " << asciiCount
770 << ":" << nonAsciiCount << endl;
771 if( nonAsciiCount > asciiCount ) {
772 // assume koi8-r encoding
773 kdDebug(5100) << "Assume koi8-r encoding." << endl;
774 TQTextCodec *codec = TQTextCodec::codecForName("KOI8-R");
775 uidString = codec->toUnicode( uid.data() );
776 // check the case of the first two characters to find out
777 // whether the user id is probably CP1251 encoded (for some
778 // reason in CP1251 the lower case characters have smaller
779 // codes than the upper case characters, so if the first char
780 // of the koi8-r decoded user id is lower case and the second
781 // char is upper case then it's likely that the user id is
782 // CP1251 encoded)
783 if( ( uidString.length() >= 2 )
784 && ( uidString[0].lower() == uidString[0] )
785 && ( uidString[1].upper() == uidString[1] ) ) {
786 // koi8-r decoded user id has inverted case, so assume
787 // CP1251 encoding
788 kdDebug(5100) << "No, it doesn't seem to be koi8-r. "
789 "Use CP 1251 instead." << endl;
790 TQTextCodec *codec = TQTextCodec::codecForName("CP1251");
791 uidString = codec->toUnicode( uid.data() );
792 }
793 }
794 else {
795 // assume latin1 encoding
796 kdDebug(5100) << "Assume latin1 encoding." << endl;
797 uidString = TQString::fromLatin1( uid.data() );
798 }
799 }
800 userID->setText( uidString );
801 break;
802 }
803 pos = pos2 + 1;
804 pos2 = output.find( ':', pos );
805 }
806
807 // user IDs are printed in UTF-8 by gpg (if one uses --with-colons)
808 key->addUserID( userID );
809 }
810 else if( !strncmp( output.data() + index, "fpr:", 4 ) )
811 { // line contains a fingerprint
812 // Example: fpr:::::::::17AFBAAF21064E513F037E6E63CB691DFAEBD5FC:
813
814 if (key == 0) // invalid key data
815 break;
816
817 // search the fingerprint (it's in the 10th field)
818 int pos = index + 4;
819 for( int i = 0; i < 8; i++ )
820 pos = output.find( ':', pos ) + 1;
821 int pos2 = output.find( ':', pos );
822
823 key->setFingerprint( keyID, output.mid( pos, pos2-pos ) );
824 }
825 index = eol + 1;
826 }
827
828 //kdDebug(5100) << "finished parsing key data\n";
829
830 offset = index;
831
832 return key;
833}
834
835
836KeyList
837BaseG::parseKeyList( const TQCString& output, bool secretKeys )
838{
839 KeyList keys;
840 Key *key = 0;
841 int offset;
842
843 // search start of key data
844 if( !strncmp( output.data(), "pub:", 4 )
845 || !strncmp( output.data(), "sec:", 4 ) )
846 offset = 0;
847 else {
848 if( secretKeys )
849 offset = output.find( "\nsec:" );
850 else
851 offset = output.find( "\npub:" );
852 if( offset == -1 )
853 return keys;
854 else
855 offset++;
856 }
857
858 do {
859 key = parseKeyData( output, offset );
860 if( key != 0 )
861 keys.append( key );
862 }
863 while( key != 0 );
864
865 //kdDebug(5100) << "finished parsing keys" << endl;
866
867 return keys;
868}
869
870
871} // namespace Kpgp