libkpgp

kpgpbase2.cpp
1 /*
2  kpgpbase2.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 <string.h> /* strncmp */
27 #include <assert.h>
28 
29 #include <tqdatetime.h>
30 
31 #include <tdelocale.h>
32 #include <tdeprocess.h>
33 #include <kdebug.h>
34 
35 #define PGP2 "pgp"
36 
37 namespace Kpgp {
38 
39 Base2::Base2()
40  : Base()
41 {
42 }
43 
44 
45 Base2::~Base2()
46 {
47 }
48 
49 
50 int
51 Base2::encrypt( Block& block, const KeyIDList& recipients )
52 {
53  return encsign( block, recipients, 0 );
54 }
55 
56 
57 int
58 Base2::clearsign( Block& block, const TQString &passphrase )
59 {
60  return encsign( block, KeyIDList(), passphrase );
61 }
62 
63 
64 int
65 Base2::encsign( Block& block, const KeyIDList& recipients,
66  const TQString &passphrase )
67 {
68  TQCString cmd;
69  int exitStatus = 0;
70 
71  if (!recipients.isEmpty() && !passphrase.isEmpty())
72  cmd = PGP2 " +batchmode +language=en +verbose=1 -seat";
73  else if(!recipients.isEmpty())
74  cmd = PGP2 " +batchmode +language=en +verbose=1 -eat";
75  else if (!passphrase.isEmpty())
76  cmd = PGP2 " +batchmode +language=en +verbose=1 -sat";
77  else
78  {
79  kdDebug(5100) << "kpgpbase: Neither recipients nor passphrase specified." << endl;
80  return OK;
81  }
82 
83  if (!passphrase.isEmpty())
84  cmd += addUserId();
85 
86  if(!recipients.isEmpty()) {
87  if(Module::getKpgp()->encryptToSelf())
88  {
89  cmd += " 0x";
90  cmd += Module::getKpgp()->user();
91  }
92 
93  for( KeyIDList::ConstIterator it = recipients.begin();
94  it != recipients.end(); ++it ) {
95  cmd += " 0x";
96  cmd += (*it);
97  }
98  }
99  cmd += " -f";
100 
101  clear();
102  input = block.text();
103  exitStatus = run(cmd.data(), passphrase);
104  if( !output.isEmpty() )
105  block.setProcessedText( output );
106  block.setError( error );
107 
108  if(exitStatus != 0)
109  status = ERROR;
110 
111 #if 0
112  // #### FIXME: As we check the keys ourselves the following problems
113  // shouldn't occur. Therefore I don't handle them for now.
114  // IK 01/2002
115  if(!recipients.isEmpty())
116  {
117  int index = 0;
118  bool bad = FALSE;
119  unsigned int num = 0;
120  TQCString badkeys = "";
121  if (error.find("Cannot find the public key") != -1)
122  {
123  index = 0;
124  num = 0;
125  while((index = error.find("Cannot find the public key",index))
126  != -1)
127  {
128  bad = TRUE;
129  index = error.find('\'',index);
130  int index2 = error.find('\'',index+1);
131  if (num++)
132  badkeys += ", ";
133  badkeys += error.mid(index, index2-index+1);
134  }
135  if(bad)
136  {
137  badkeys.stripWhiteSpace();
138  if(num == recipients.count())
139  errMsg = i18n("Could not find public keys matching the userid(s)\n"
140  "%1;\n"
141  "the message is not encrypted.")
142  .arg( badkeys.data() );
143  else
144  errMsg = i18n("Could not find public keys matching the userid(s)\n"
145  "%1;\n"
146  "these persons will not be able to read the message.")
147  .arg( badkeys.data() );
148  status |= MISSINGKEY;
149  status |= ERROR;
150  }
151  }
152  if (error.find("skipping userid") != -1)
153  {
154  index = 0;
155  num = 0;
156  while((index = error.find("skipping userid",index))
157  != -1)
158  {
159  bad = TRUE;
160  int index2 = error.find('\n',index+16);
161  if (num++)
162  badkeys += ", ";
163  badkeys += error.mid(index+16, index2-index-16);
164  index = index2;
165  }
166  if(bad)
167  {
168  badkeys.stripWhiteSpace();
169  if(num == recipients.count())
170  errMsg = i18n("Public keys not certified with trusted signature "
171  "for userid(s)\n"
172  "%1.\n"
173  "The message is not encrypted.")
174  .arg( badkeys.data() );
175  else
176  errMsg = i18n("Public keys not certified with trusted signature "
177  "for userid(s)\n"
178  "%1;\n"
179  "these persons will not be able to read the message.")
180  .arg( badkeys.data() );
181  status |= BADKEYS;
182  status |= ERROR;
183  return status;
184  }
185  }
186  }
187 #endif
188  if (!passphrase.isEmpty())
189  {
190  if(error.find("Pass phrase is good") != -1)
191  {
192  //kdDebug(5100) << "Base: Good Passphrase!" << endl;
193  status |= SIGNED;
194  }
195  if( error.find("Bad pass phrase") != -1)
196  {
197  errMsg = i18n("Bad passphrase; could not sign.");
198  status |= BADPHRASE;
199  status |= ERR_SIGNING;
200  status |= ERROR;
201  }
202  }
203  if (error.find("Signature error") != -1)
204  {
205  errMsg = i18n("Signing failed: please check your PGP User Identity, "
206  "the PGP setup, and the key rings.");
207  status |= NO_SEC_KEY;
208  status |= ERR_SIGNING;
209  status |= ERROR;
210  }
211  if (error.find("Encryption error") != -1)
212  {
213  errMsg = i18n("Encryption failed: please check your PGP setup "
214  "and the key rings.");
215  status |= NO_SEC_KEY;
216  status |= BADKEYS;
217  status |= ERROR;
218  }
219 
220  //kdDebug(5100) << "status = " << status << endl;
221  block.setStatus( status );
222  return status;
223 }
224 
225 
226 int
227 Base2::decrypt( Block& block, const TQString &passphrase )
228 {
229  int index, index2;
230  int exitStatus = 0;
231 
232  clear();
233  input = block.text();
234  exitStatus = run(PGP2 " +batchmode +language=en -f", passphrase);
235  if( !output.isEmpty() )
236  block.setProcessedText( output );
237  block.setError( error );
238 
239  // pgp2.6 has sometimes problems with the ascii armor pgp5.0 produces
240  // this hack can solve parts of the problem
241  if(error.find("ASCII armor corrupted.") != -1)
242  {
243  kdDebug(5100) << "removing ASCII armor header" << endl;
244  int index1 = input.find("-----BEGIN PGP SIGNED MESSAGE-----");
245  if(index1 != -1)
246  index1 = input.find("-----BEGIN PGP SIGNATURE-----", index1);
247  else
248  index1 = input.find("-----BEGIN PGP MESSAGE-----");
249  index1 = input.find('\n', index1);
250  index2 = input.find("\n\n", index1);
251  input.remove(index1, index2 - index1);
252  exitStatus = run(PGP2 " +batchmode +language=en -f", passphrase);
253  if( !output.isEmpty() )
254  block.setProcessedText( output );
255  block.setError( error );
256  }
257 
258  if(exitStatus == -1) {
259  errMsg = i18n("error running PGP");
260  status = RUN_ERR;
261  block.setStatus( status );
262  return status;
263  }
264 
265  /* Example No.1 (PGP 2.6.3in):
266  * File is encrypted. Secret key is required to read it.
267  * Key for user ID: Test Key (only for testing) <testkey@ingo-kloecker.de>
268  * 1024-bit key, key ID E2D074D3, created 2001/09/09
269  *
270  * Error: Bad pass phrase.
271  *
272  * This message can only be read by:
273  * Test key without secret key (for testing only) <nosectestkey@ingo-kloecker.de>
274  * Test Key (only for testing) <testkey@ingo-kloecker.de>
275  *
276  * You do not have the secret key needed to decrypt this file.
277  */
278  /* Example No.2 (PGP 2.6.3in):
279  * File is encrypted. Secret key is required to read it.
280  * This message can only be read by:
281  * Test key without secret key (for testing only) <nosectestkey@ingo-kloecker.de>
282  *
283  * You do not have the secret key needed to decrypt this file.
284  */
285  if(error.find("File is encrypted.") != -1)
286  {
287  //kdDebug(5100) << "kpgpbase: message is encrypted" << endl;
288  status |= ENCRYPTED;
289  if((index = error.find("Key for user ID:")) != -1)
290  {
291  // Find out the key for which the phrase is needed
292  index += 17;
293  index2 = error.find('\n', index);
294  block.setRequiredUserId( error.mid(index, index2 - index) );
295  //kdDebug(5100) << "Base: key needed is \"" << block.requiredUserId() << "\"!\n";
296 
297  if ((!passphrase.isEmpty()) && (error.find("Bad pass phrase") != -1))
298  {
299  errMsg = i18n("Bad passphrase; could not decrypt.");
300  kdDebug(5100) << "Base: passphrase is bad" << endl;
301  status |= BADPHRASE;
302  status |= ERROR;
303  }
304  }
305  else
306  {
307  // no secret key fitting this message
308  status |= NO_SEC_KEY;
309  status |= ERROR;
310  errMsg = i18n("You do not have the secret key needed to decrypt this message.");
311  kdDebug(5100) << "Base: no secret key for this message" << endl;
312  }
313  // check for persons
314 #if 0
315  // ##### FIXME: This information is anyway currently not used
316  // I'll change it to always determine the recipients.
317  index = error.find("can only be read by:");
318  if(index != -1)
319  {
320  index = error.find('\n',index);
321  int end = error.find("\n\n",index);
322 
323  mRecipients.clear();
324  while( (index2 = error.find('\n',index+1)) <= end )
325  {
326  TQCString item = error.mid(index+1,index2-index-1);
327  item.stripWhiteSpace();
328  mRecipients.append(item);
329  index = index2;
330  }
331  }
332 #endif
333  }
334 
335  // handle signed message
336 
337  // Examples (made with PGP 2.6.3in)
338  /* Example No. 1 (signed with unknown key):
339  * File has signature. Public key is required to check signature.
340  *
341  * Key matching expected Key ID 12345678 not found in file '/home/user/.pgp/pubring.pgp'.
342  *
343  * WARNING: Can't find the right public key-- can't check signature integrity.
344  */
345  /* Example No. 2 (bad signature):
346  * File has signature. Public key is required to check signature.
347  * ..
348  * WARNING: Bad signature, doesn't match file contents!
349  *
350  * Bad signature from user "Joe User <joe@foo.bar>".
351  * Signature made 2001/09/09 16:01 GMT using 1024-bit key, key ID 12345678
352  */
353  /* Example No. 3.1 (good signature with untrusted key):
354  * File has signature. Public key is required to check signature.
355  * .
356  * Good signature from user "Joe User <joe@foo.bar>".
357  * Signature made 2001/09/09 16:01 GMT using 1024-bit key, key ID 12345678
358  *
359  * WARNING: Because this public key is not certified with a trusted
360  * signature, it is not known with high confidence that this public key
361  * actually belongs to: "Joe User <joe@foo.bar>".
362  */
363  /* Example No. 3.2 (good signature with untrusted key):
364  * File has signature. Public key is required to check signature.
365  * .
366  * Good signature from user "Joe User <joe@foo.bar>".
367  * Signature made 2001/09/09 16:01 GMT using 1024-bit key, key ID 12345678
368  *
369  * WARNING: Because this public key is not certified with enough trusted
370  * signatures, it is not known with high confidence that this public key
371  * actually belongs to: "Joe User <joe@foo.bar>".
372  */
373  /* Example No. 4 (good signature with revoked key):
374  * File has signature. Public key is required to check signature.
375  * .
376  * Good signature from user "Joe User <joe@foo.bar>".
377  * Signature made 2001/09/09 16:01 GMT using 1024-bit key, key ID 12345678
378  *
379  *
380  * Key for user ID: Joe User <joe@foo.bar>
381  * 1024-bit key, key ID 12345678, created 2001/09/09
382  * Key has been revoked.
383  *
384  * WARNING: This key has been revoked by its owner,
385  * possibly because the secret key was compromised.
386  * This could mean that this signature is a forgery.
387  */
388  /* Example No. 5 (good signature with trusted key):
389  * File has signature. Public key is required to check signature.
390  * .
391  * Good signature from user "Joe User <joe@foo.bar>".
392  * Signature made 2001/09/09 16:01 GMT using 1024-bit key, key ID 12345678
393  */
394 
395  if((index = error.find("File has signature")) != -1)
396  {
397  // move index to start of next line
398  index = error.find('\n', index+18) + 1;
399  //kdDebug(5100) << "Base: message is signed" << endl;
400  status |= SIGNED;
401  // get signature date and signature key ID
402  if ((index2 = error.find("Signature made", index)) != -1) {
403  index2 += 15;
404  int index3 = error.find("using", index2);
405  block.setSignatureDate( error.mid(index2, index3-index2-1) );
406  kdDebug(5100) << "Message was signed on '" << block.signatureDate() << "'\n";
407  index3 = error.find("key ID ", index3) + 7;
408  block.setSignatureKeyId( error.mid(index3,8) );
409  kdDebug(5100) << "Message was signed with key '" << block.signatureKeyId() << "'\n";
410  }
411  else {
412  // if pgp can't find the keyring it unfortunately doesn't print
413  // the signature date and key ID
414  block.setSignatureDate( "" );
415  block.setSignatureKeyId( "" );
416  }
417 
418  if( ( index2 = error.find("Key matching expected", index) ) != -1)
419  {
420  status |= UNKNOWN_SIG;
421  status |= GOODSIG;
422  int index3 = error.find("Key ID ", index2) + 7;
423  block.setSignatureKeyId( error.mid(index3,8) );
424  block.setSignatureUserId( TQString() );
425  }
426  else if( (index2 = error.find("Good signature from", index)) != -1 )
427  {
428  status |= GOODSIG;
429  // get signer
430  index = error.find('"',index2+19);
431  index2 = error.find('"', index+1);
432  block.setSignatureUserId( error.mid(index+1, index2-index-1) );
433  }
434  else if( (index2 = error.find("Bad signature from", index)) != -1 )
435  {
436  status |= ERROR;
437  // get signer
438  index = error.find('"',index2+19);
439  index2 = error.find('"', index+1);
440  block.setSignatureUserId( error.mid(index+1, index2-index-1) );
441  }
442  else if( error.find("Keyring file", index) != -1 )
443  {
444  // #### fix this hack
445  status |= UNKNOWN_SIG;
446  status |= GOODSIG; // this is a hack...
447  // determine file name of missing keyring file
448  index = error.find('\'', index) + 1;
449  index2 = error.find('\'', index);
450  block.setSignatureUserId( i18n("The keyring file %1 does not exist.\n"
451  "Please check your PGP setup.").arg(TQString(error.mid(index, index2-index))) );
452  }
453  else
454  {
455  status |= ERROR;
456  block.setSignatureUserId( i18n("Unknown error") );
457  }
458  }
459  //kdDebug(5100) << "status = " << status << endl;
460  block.setStatus( status );
461  return status;
462 }
463 
464 
465 Key*
466 Base2::readPublicKey( const KeyID& keyID,
467  const bool readTrust /* = false */,
468  Key* key /* = 0 */ )
469 {
470  int exitStatus = 0;
471 
472  status = 0;
473  exitStatus = run( PGP2 " +batchmode +language=en +verbose=0 -kvc -f 0x" +
474  keyID, 0, true );
475 
476  if(exitStatus != 0) {
477  status = ERROR;
478  return 0;
479  }
480 
481  key = parsePublicKeyData( output, key );
482 
483  if( key == 0 )
484  {
485  return 0;
486  }
487 
488  if( readTrust )
489  {
490  exitStatus = run( PGP2 " +batchmode +language=en +verbose=0 -kc -f",
491  0, true );
492 
493  if(exitStatus != 0) {
494  status = ERROR;
495  return 0;
496  }
497 
498  parseTrustDataForKey( key, error );
499  }
500 
501  return key;
502 }
503 
504 
505 KeyList
506 Base2::publicKeys( const TQStringList & patterns )
507 {
508  return doGetPublicKeys( PGP2 " +batchmode +language=en +verbose=0 -kvc -f",
509  patterns );
510 }
511 
512 KeyList
513 Base2::doGetPublicKeys( const TQCString & cmd, const TQStringList & patterns )
514 {
515  int exitStatus = 0;
516  KeyList publicKeys;
517 
518  status = 0;
519  if ( patterns.isEmpty() ) {
520  exitStatus = run( cmd, 0, true );
521 
522  if ( exitStatus != 0 ) {
523  status = ERROR;
524  return KeyList();
525  }
526 
527  // now we need to parse the output for public keys
528  publicKeys = parseKeyList( output, false );
529  }
530  else {
531  typedef TQMap<TQCString, Key*> KeyMap;
532  KeyMap map;
533 
534  for ( TQStringList::ConstIterator it = patterns.begin();
535  it != patterns.end(); ++it ) {
536  exitStatus = run( cmd + " " + TDEProcess::quote( *it ).local8Bit(),
537  0, true );
538 
539  if ( exitStatus != 0 ) {
540  status = ERROR;
541  return KeyList();
542  }
543 
544  // now we need to parse the output for public keys
545  publicKeys = parseKeyList( output, false );
546 
547  // put all new keys into a map, remove duplicates
548  while ( !publicKeys.isEmpty() ) {
549  Key * key = publicKeys.take( 0 );
550  if ( !map.contains( key->primaryFingerprint() ) )
551  map.insert( key->primaryFingerprint(), key );
552  else
553  delete key;
554  }
555  }
556  // build list from the map
557  for ( KeyMap::ConstIterator it = map.begin(); it != map.end(); ++it ) {
558  publicKeys.append( it.data() );
559  }
560  }
561 
562  // sort the list of public keys
563  publicKeys.sort();
564 
565  return publicKeys;
566 }
567 
568 KeyList
569 Base2::secretKeys( const TQStringList & patterns )
570 {
571  return publicKeys( patterns );
572 }
573 
574 
575 int
576 Base2::signKey(const KeyID& keyID, const TQString &passphrase)
577 {
578  TQCString cmd;
579  int exitStatus = 0;
580 
581  cmd = PGP2 " +batchmode +language=en -ks -f ";
582  cmd += addUserId();
583  cmd += " 0x" + keyID;
584 
585  status = 0;
586  exitStatus = run(cmd.data(),passphrase);
587 
588  if (exitStatus != 0)
589  status = ERROR;
590 
591  return status;
592 }
593 
594 
595 TQCString Base2::getAsciiPublicKey(const KeyID& keyID)
596 {
597  int exitStatus = 0;
598 
599  if (keyID.isEmpty())
600  return TQCString();
601 
602  status = 0;
603  exitStatus = run( PGP2 " +batchmode +force +language=en -kxaf 0x" + keyID,
604  0, true );
605 
606  if(exitStatus != 0) {
607  status = ERROR;
608  return TQCString();
609  }
610 
611  return output;
612 }
613 
614 
615 Key*
616 Base2::parsePublicKeyData( const TQCString& output, Key* key /* = 0 */ )
617 {
618  Subkey *subkey = 0;
619  int index;
620 
621  // search start of key data
622  if( !strncmp( output.data(), "pub", 3 ) ||
623  !strncmp( output.data(), "sec", 3 ) )
624  index = 0;
625  else
626  {
627  /*
628  if( secretKeys )
629  index = output.find( "\nsec" );
630  else
631  */
632  index = output.find( "\npub" );
633  if( index == -1 )
634  return 0;
635  else
636  index++;
637  }
638 
639  while( true )
640  {
641  int index2;
642 
643  // search the end of the current line
644  if( ( index2 = output.find( '\n', index ) ) == -1 )
645  break;
646 
647  if( !strncmp( output.data() + index, "pub", 3 ) ||
648  !strncmp( output.data() + index, "sec", 3 ) )
649  { // line contains primary key data
650  // Example 1 (nothing special):
651  // pub 1024/E2D074D3 2001/09/09 Test Key <testkey@xyz>
652  // Example 2 (disabled key):
653  // pub- 1024/8CCB2C1B 2001/11/04 Disabled Test Key <disabled@xyz>
654  // Example 3 (expired key):
655  // pub> 1024/7B94827D 2001/09/09 Expired Test Key <expired@xyz> (EXPIRE:2001-09-10)
656  // Example 4 (revoked key):
657  // pub 1024/956721F9 2001/09/09 *** KEY REVOKED ***
658 
659  int pos, pos2;
660 
661  if( key == 0 )
662  key = new Key();
663  else
664  key->clear();
665  /*key->setSecret( secretKeys );*/
666  // set default key capabilities
667  key->setCanEncrypt( true );
668  key->setCanSign( true );
669  key->setCanCertify( true );
670 
671  /*subkey = new Subkey( "", secretKeys );*/
672  subkey = new Subkey( "", false );
673  key->addSubkey( subkey );
674  // set default key capabilities
675  subkey->setCanEncrypt( true );
676  subkey->setCanSign( true );
677  subkey->setCanCertify( true );
678  // expiration date defaults to never
679  subkey->setExpirationDate( -1 );
680 
681  // Key Flags
682  switch( output[index+3] )
683  {
684  case ' ': // nothing special
685  break;
686  case '-': // disabled key
687  subkey->setDisabled( true );
688  key->setDisabled( true );
689  break;
690  case '>': // expired key
691  subkey->setExpired( true );
692  key->setExpired( true );
693  break;
694  default:
695  kdDebug(5100) << "Unknown key flag.\n";
696  }
697 
698  // Key Length
699  pos = index + 4;
700  while( output[pos] == ' ' )
701  pos++;
702  pos2 = output.find( '/', pos );
703  subkey->setKeyLength( output.mid( pos, pos2-pos ).toUInt() );
704 
705  // Key ID
706  pos = pos2 + 1;
707  pos2 = output.find( ' ', pos );
708  subkey->setKeyID( output.mid( pos, pos2-pos ) );
709 
710  // Creation Date
711  pos = pos2 + 1;
712  while( output[pos] == ' ' )
713  pos++;
714  pos2 = output.find( ' ', pos );
715  int year = output.mid( pos, 4 ).toInt();
716  int month = output.mid( pos+5, 2 ).toInt();
717  int day = output.mid( pos+8, 2 ).toInt();
718  TQDateTime dt( TQDate( year, month, day ), TQTime( 00, 00 ) );
719  TQDateTime epoch( TQDate( 1970, 01, 01 ), TQTime( 00, 00 ) );
720  // The calculated creation date isn't exactly correct because TQDateTime
721  // doesn't know anything about timezones and always assumes local time
722  // although epoch is of course UTC. But as PGP 2 anyway doesn't print
723  // the time this doesn't matter too much.
724  subkey->setCreationDate( epoch.secsTo( dt ) );
725 
726  // User ID
727  pos = pos2 + 1;
728  while( output[pos] == ' ' )
729  pos++;
730  TQCString uid = output.mid( pos, index2-pos );
731  if( uid != "*** KEY REVOKED ***" )
732  key->addUserID( uid );
733  else
734  {
735  subkey->setRevoked( true );
736  key->setRevoked( true );
737  }
738  }
739  else if( output[index] == ' ' )
740  { // line contains additional key data
741 
742  if( key == 0 )
743  break;
744  assert( subkey != 0 );
745 
746  int pos = index + 1;
747  while( output[pos] == ' ' )
748  pos++;
749 
750  if( !strncmp( output.data() + pos, "Key fingerprint = ", 18 ) )
751  { // line contains a fingerprint
752  // Example:
753  // Key fingerprint = 47 30 7C 76 05 BF 5E FB 72 41 00 F2 7D 0B D0 49
754 
755  TQCString fingerprint = output.mid( pos, index2-pos );
756  // remove white space from the fingerprint
757  for ( int idx = 0 ; (idx = fingerprint.find(' ', idx)) >= 0 ; )
758  fingerprint.replace( idx, 1, "" );
759 
760  subkey->setFingerprint( fingerprint );
761  }
762  else if( !strncmp( output.data() + pos, "Expire: ", 8 ) ||
763  !strncmp( output.data() + pos, "no expire ", 10 ) )
764  { // line contains additional key properties
765  // Examples:
766  // Expire: 2001/09/10
767  // no expire ENCRyption only
768  // no expire SIGNature only
769 
770  if( output[pos] == 'E' )
771  {
772  // Expiration Date
773  pos += 8;
774  int year = output.mid( pos, 4 ).toInt();
775  int month = output.mid( pos+5, 2 ).toInt();
776  int day = output.mid( pos+8, 2 ).toInt();
777  TQDateTime dt( TQDate( year, month, day ), TQTime( 00, 00 ) );
778  TQDateTime epoch( TQDate( 1970, 01, 01 ), TQTime( 00, 00 ) );
779  // Here the same comments as for the creation date are valid.
780  subkey->setExpirationDate( epoch.secsTo( dt ) );
781  pos += 11; // note that there is always a blank after the expire date
782  }
783  else
784  pos += 10;
785 
786  // optional key capabilities (sign/encrypt only)
787  if( pos != index2 )
788  {
789  if( !strncmp( output.data() + pos, "SIGNature only", 14 ) )
790  {
791  subkey->setCanEncrypt( false );
792  key->setCanEncrypt( false );
793  }
794  else if( !strncmp( output.data() + pos, "ENCRyption only", 15 ) )
795  {
796  subkey->setCanSign( false );
797  key->setCanSign( false );
798  subkey->setCanCertify( false );
799  key->setCanCertify( false );
800  }
801  }
802  }
803  else
804  { // line contains an additional user id
805  // Example:
806  // Test key (2nd user ID) <abc@xyz>
807 
808  key->addUserID( output.mid( pos, index2-pos ) );
809  }
810  }
811  index = index2 + 1;
812  }
813 
814  //kdDebug(5100) << "finished parsing key data\n";
815 
816  return key;
817 }
818 
819 
820 void
821 Base2::parseTrustDataForKey( Key* key, const TQCString& str )
822 {
823  if( ( key == 0 ) || str.isEmpty() )
824  return;
825 
826  TQCString keyID = key->primaryKeyID();
827  UserIDList userIDs = key->userIDs();
828 
829  // search the trust data belonging to this key
830  int index = str.find( '\n' ) + 1;
831  while( ( index > 0 ) &&
832  ( strncmp( str.data() + index+2, keyID.data(), 8 ) != 0 ) )
833  index = str.find( '\n', index ) + 1;
834 
835  if( index == 0 )
836  return;
837 
838  bool ultimateTrust = false;
839  if( !strncmp( str.data() + index+11, "ultimate", 8 ) )
840  ultimateTrust = true;
841 
842  bool firstLine = true;
843 
844  while( true )
845  { // loop over all trust information about this key
846  int index2;
847 
848  // search the end of the current line
849  if( ( index2 = str.find( '\n', index ) ) == -1 )
850  break;
851 
852  // check if trust info for the next key starts
853  if( !firstLine && ( str[index+2] != ' ' ) )
854  break;
855 
856  if( str[index+21] != ' ' )
857  { // line contains a validity value for a user ID
858 
859  // determine the validity
860  Validity validity = KPGP_VALIDITY_UNKNOWN;
861  if( !strncmp( str.data() + index+21, "complete", 8 ) )
862  if( ultimateTrust )
863  validity = KPGP_VALIDITY_ULTIMATE;
864  else
865  validity = KPGP_VALIDITY_FULL;
866  else if( !strncmp( str.data() + index+21, "marginal", 8 ) )
867  validity = KPGP_VALIDITY_MARGINAL;
868  else if( !strncmp( str.data() + index+21, "never", 5 ) )
869  validity = KPGP_VALIDITY_NEVER;
870  else if( !strncmp( str.data() + index+21, "undefined", 9 ) )
871  validity = KPGP_VALIDITY_UNDEFINED;
872 
873  // determine the user ID
874  int pos = index + 31;
875  if( str[index+2] == ' ' )
876  pos++; // additional user IDs start one column later
877  TQString uid = str.mid( pos, index2-pos );
878 
879  // set the validity of the corresponding user ID
880  for( UserIDListIterator it( userIDs ); it.current(); ++it )
881  if( (*it)->text() == uid )
882  {
883  kdDebug(5100)<<"Setting the validity of "<<uid<<" to "<<validity<<endl;
884  (*it)->setValidity( validity );
885  break;
886  }
887  }
888 
889  firstLine = false;
890  index = index2 + 1;
891  }
892 }
893 
894 
895 KeyList
896 Base2::parseKeyList( const TQCString& output, bool secretKeys )
897 {
898  kdDebug(5100) << "Kpgp::Base2::parseKeyList()" << endl;
899  KeyList keys;
900  Key *key = 0;
901  Subkey *subkey = 0;
902  int index;
903 
904  // search start of key data
905  if( !strncmp( output.data(), "pub", 3 ) ||
906  !strncmp( output.data(), "sec", 3 ) )
907  index = 0;
908  else
909  {
910  if( secretKeys )
911  index = output.find( "\nsec" );
912  else
913  index = output.find( "\npub" );
914  if( index == -1 )
915  return keys;
916  else
917  index++;
918  }
919 
920  while( true )
921  {
922  int index2;
923 
924  // search the end of the current line
925  if( ( index2 = output.find( '\n', index ) ) == -1 )
926  break;
927 
928  if( !strncmp( output.data() + index, "pub", 3 ) ||
929  !strncmp( output.data() + index, "sec", 3 ) )
930  { // line contains primary key data
931  // Example 1:
932  // pub 1024/E2D074D3 2001/09/09 Test Key <testkey@xyz>
933  // Example 2 (disabled key):
934  // pub- 1024/8CCB2C1B 2001/11/04 Disabled Test Key <disabled@xyz>
935  // Example 3 (expired key):
936  // pub> 1024/7B94827D 2001/09/09 Expired Test Key <expired@xyz> (EXPIRE:2001-09-10)
937  // Example 4 (revoked key):
938  // pub 1024/956721F9 2001/09/09 *** KEY REVOKED ***
939 
940  int pos, pos2;
941 
942  if( key != 0 ) // store the previous key in the key list
943  keys.append( key );
944 
945  key = new Key();
946  key->setSecret( secretKeys );
947  // set default key capabilities
948  key->setCanEncrypt( true );
949  key->setCanSign( true );
950  key->setCanCertify( true );
951 
952  subkey = new Subkey( "", secretKeys );
953  key->addSubkey( subkey );
954  // set default key capabilities
955  subkey->setCanEncrypt( true );
956  subkey->setCanSign( true );
957  subkey->setCanCertify( true );
958  // expiration date defaults to never
959  subkey->setExpirationDate( -1 );
960 
961  // Key Flags
962  switch( output[index+3] )
963  {
964  case ' ': // nothing special
965  break;
966  case '-': // disabled key
967  subkey->setDisabled( true );
968  key->setDisabled( true );
969  break;
970  case '>': // expired key
971  subkey->setExpired( true );
972  key->setExpired( true );
973  break;
974  default:
975  kdDebug(5100) << "Unknown key flag.\n";
976  }
977 
978  // Key Length
979  pos = index + 4;
980  while( output[pos] == ' ' )
981  pos++;
982  pos2 = output.find( '/', pos );
983  subkey->setKeyLength( output.mid( pos, pos2-pos ).toUInt() );
984 
985  // Key ID
986  pos = pos2 + 1;
987  pos2 = output.find( ' ', pos );
988  subkey->setKeyID( output.mid( pos, pos2-pos ) );
989 
990  // Creation Date
991  pos = pos2 + 1;
992  while( output[pos] == ' ' )
993  pos++;
994  pos2 = output.find( ' ', pos );
995  int year = output.mid( pos, 4 ).toInt();
996  int month = output.mid( pos+5, 2 ).toInt();
997  int day = output.mid( pos+8, 2 ).toInt();
998  TQDateTime dt( TQDate( year, month, day ), TQTime( 00, 00 ) );
999  TQDateTime epoch( TQDate( 1970, 01, 01 ), TQTime( 00, 00 ) );
1000  // The calculated creation date isn't exactly correct because TQDateTime
1001  // doesn't know anything about timezones and always assumes local time
1002  // although epoch is of course UTC. But as PGP 2 anyway doesn't print
1003  // the time this doesn't matter too much.
1004  subkey->setCreationDate( epoch.secsTo( dt ) );
1005 
1006  // User ID
1007  pos = pos2 + 1;
1008  while( output[pos] == ' ' )
1009  pos++;
1010  TQCString uid = output.mid( pos, index2-pos );
1011  if( uid != "*** KEY REVOKED ***" )
1012  key->addUserID( uid );
1013  else
1014  {
1015  subkey->setRevoked( true );
1016  key->setRevoked( true );
1017  }
1018  }
1019  else if( output[index] == ' ' )
1020  { // line contains additional key data
1021 
1022  if( key == 0 )
1023  break;
1024 
1025  int pos = index + 1;
1026  while( output[pos] == ' ' )
1027  pos++;
1028 
1029  if( !strncmp( output.data() + pos, "Key fingerprint = ", 18 ) )
1030  { // line contains a fingerprint
1031  // Example:
1032  // Key fingerprint = 47 30 7C 76 05 BF 5E FB 72 41 00 F2 7D 0B D0 49
1033 
1034 // int pos2;
1035 // pos2 = pos + 18;
1036  TQCString fingerprint = output.mid( pos, index2-pos );
1037  // remove white space from the fingerprint
1038  for ( int idx = 0 ; (idx = fingerprint.find(' ', idx)) >= 0 ; )
1039  fingerprint.replace( idx, 1, "" );
1040 
1041  subkey->setFingerprint( fingerprint );
1042  }
1043  else if( !strncmp( output.data() + pos, "Expire: ", 8 ) ||
1044  !strncmp( output.data() + pos, "no expire ", 10 ) )
1045  { // line contains additional key properties
1046  // Examples:
1047  // Expire: 2001/09/10
1048  // no expire ENCRyption only
1049  // no expire SIGNature only
1050 
1051  if( output[pos] == 'E' )
1052  {
1053  // Expiration Date
1054  pos += 8;
1055  int year = output.mid( pos, 4 ).toInt();
1056  int month = output.mid( pos+5, 2 ).toInt();
1057  int day = output.mid( pos+8, 2 ).toInt();
1058  TQDateTime dt( TQDate( year, month, day ), TQTime( 00, 00 ) );
1059  TQDateTime epoch( TQDate( 1970, 01, 01 ), TQTime( 00, 00 ) );
1060  // Here the same comments as for the creation date are valid.
1061  subkey->setExpirationDate( epoch.secsTo( dt ) );
1062  pos += 11; // note that there is always a blank after the expire date
1063  }
1064  else
1065  pos += 10;
1066 
1067  // optional key capabilities (sign/encrypt only)
1068  if( pos != index2 )
1069  {
1070  if( !strncmp( output.data() + pos, "SIGNature only", 14 ) )
1071  {
1072  subkey->setCanEncrypt( false );
1073  key->setCanEncrypt( false );
1074  }
1075  else if( !strncmp( output.data() + pos, "ENCRyption only", 15 ) )
1076  {
1077  subkey->setCanSign( false );
1078  key->setCanSign( false );
1079  subkey->setCanCertify( false );
1080  key->setCanCertify( false );
1081  }
1082  }
1083  }
1084  else
1085  { // line contains an additional user id
1086  // Example:
1087  // Test key (2nd user ID) <abc@xyz>
1088 
1089  key->addUserID( output.mid( pos, index2-pos ) );
1090  }
1091  }
1092 
1093  index = index2 + 1;
1094  }
1095 
1096  if (key != 0) // store the last key in the key list
1097  keys.append( key );
1098 
1099  //kdDebug(5100) << "finished parsing keys" << endl;
1100 
1101  return keys;
1102 }
1103 
1104 
1105 } // namespace Kpgp