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 <kprocess.h>
28 #include <kdebug.h>
29 
30 #include <tqtextcodec.h>
31 
32 #include <string.h> /* strncmp */
33 
34 namespace Kpgp {
35 
36 BaseG::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 
52 BaseG::~BaseG()
53 {
54 }
55 
56 
57 int
58 BaseG::encrypt( Block& block, const KeyIDList& recipients )
59 {
60  return encsign( block, recipients, 0 );
61 }
62 
63 
64 int
65 BaseG::clearsign( Block& block, const TQString &passphrase )
66 {
67  return encsign( block, KeyIDList(), passphrase );
68 }
69 
70 
71 int
72 BaseG::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 
207 int
208 BaseG::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 
385 Key*
386 BaseG::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 
421 KeyList
422 BaseG::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 
453 KeyList
454 BaseG::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 
485 int
486 BaseG::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 
506 TQCString
507 BaseG::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 
526 Key*
527 BaseG::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 
836 KeyList
837 BaseG::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