libkpgp

kpgpbase5.cpp
1 /*
2  kpgpbase5.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 <tqregexp.h>
30 #include <tqdatetime.h>
31 
32 #include <tdelocale.h>
33 #include <tdeprocess.h>
34 #include <kdebug.h>
35 
36 
37 namespace Kpgp {
38 
39 Base5::Base5()
40  : Base()
41 {
42 }
43 
44 
45 Base5::~Base5()
46 {
47 }
48 
49 
50 int
51 Base5::encrypt( Block& block, const KeyIDList& recipients )
52 {
53  return encsign( block, recipients, 0 );
54 }
55 
56 
57 int
58 Base5::clearsign( Block& block, const TQString &passphrase )
59 {
60  return encsign( block, KeyIDList(), passphrase );
61 }
62 
63 
64 int
65 Base5::encsign( Block& block, const KeyIDList& recipients,
66  const TQString &passphrase )
67 {
68  TQCString cmd;
69  int exitStatus = 0;
70  int index;
71  // used to work around a bug in pgp5. pgp5 treats files
72  // with non ascii chars (umlauts, etc...) as binary files, but
73  // we want a clear signature
74  bool signonly = false;
75 
76  if (!recipients.isEmpty() && !passphrase.isEmpty())
77  cmd = "pgpe +batchmode -afts ";
78  else if(!recipients.isEmpty())
79  cmd = "pgpe +batchmode -aft ";
80  else if (!passphrase.isEmpty())
81  {
82  cmd = "pgps +batchmode -abft ";
83  signonly = true;
84  }
85  else
86  {
87  errMsg = i18n("Neither recipients nor passphrase specified.");
88  return OK;
89  }
90 
91  if (!passphrase.isEmpty())
92  cmd += addUserId();
93 
94  if(!recipients.isEmpty())
95  {
96  if(Module::getKpgp()->encryptToSelf())
97  {
98  cmd += " -r 0x";
99  cmd += Module::getKpgp()->user();
100  }
101 
102  for( KeyIDList::ConstIterator it = recipients.begin();
103  it != recipients.end(); ++it ) {
104  cmd += " -r 0x";
105  cmd += (*it);
106  }
107  }
108 
109  clear();
110  input = block.text();
111 
112  if (signonly)
113  {
114  input.append("\n");
115  input.replace(TQRegExp("[ \t]+\n"), "\n"); //strip trailing whitespace
116  }
117  //We have to do this otherwise it's all in vain
118 
119  exitStatus = run(cmd.data(), passphrase);
120  block.setError( error );
121 
122  if(exitStatus != 0)
123  status = ERROR;
124 
125  // now parse the returned info
126  if(error.find("Cannot unlock private key") != -1)
127  {
128  errMsg = i18n("The passphrase you entered is invalid.");
129  status |= ERROR;
130  status |= BADPHRASE;
131  }
132 //if(!ignoreUntrusted)
133 //{
134  TQCString aStr;
135  index = -1;
136  while((index = error.find("WARNING: The above key",index+1)) != -1)
137  {
138  int index2 = error.find("But you previously",index);
139  int index3 = error.find("WARNING: The above key",index+1);
140  if(index2 == -1 || (index2 > index3 && index3 != -1))
141  {
142  // the key wasn't valid, no encryption to this person
143  // extract the person
144  index2 = error.find('\n',index);
145  index3 = error.find('\n',index2+1);
146  aStr += error.mid(index2+1, index3-index2-1);
147  aStr += ", ";
148  }
149  }
150  if(!aStr.isEmpty())
151  {
152  aStr.truncate(aStr.length()-2);
153  if(error.find("No valid keys found") != -1)
154  errMsg = i18n("The key(s) you want to encrypt your message "
155  "to are not trusted. No encryption done.");
156  else
157  errMsg = i18n("The following key(s) are not trusted:\n%1\n"
158  "Their owner(s) will not be able to decrypt the message.")
159  .arg(TQString(aStr));
160  status |= ERROR;
161  status |= BADKEYS;
162  }
163 //}
164  if((index = error.find("No encryption keys found for")) != -1)
165  {
166  index = error.find(':',index);
167  int index2 = error.find('\n',index);
168 
169  errMsg = i18n("Missing encryption key(s) for:\n%1")
170  .arg(TQString(error.mid(index,index2-index)));
171 // errMsg = TQString("Missing encryption key(s) for: %1")
172 // .arg(error.mid(index,index2-index));
173  status |= ERROR;
174  status |= MISSINGKEY;
175  }
176 
177  if(signonly) {
178  // dash-escape the input
179  if (input[0] == '-')
180  input = "- " + input;
181  for ( int idx = 0 ; (idx = input.find("\n-", idx)) >= 0 ; idx += 4 )
182  input.replace(idx, 2, "\n- -");
183 
184  output = "-----BEGIN PGP SIGNED MESSAGE-----\n\n" + input + "\n" + output;
185  }
186 
187  block.setProcessedText( output );
188  block.setStatus( status );
189  return status;
190 }
191 
192 
193 int
194 Base5::decrypt( Block& block, const TQString &passphrase )
195 {
196  int exitStatus = 0;
197 
198  clear();
199  input = block.text();
200  exitStatus = run("pgpv -f +batchmode=1", passphrase);
201  if( !output.isEmpty() )
202  block.setProcessedText( output );
203  block.setError( error );
204 
205  if(exitStatus == -1) {
206  errMsg = i18n("Error running PGP");
207  status = RUN_ERR;
208  block.setStatus( status );
209  return status;
210  }
211 
212  // lets parse the returned information.
213  int index;
214 
215  index = error.find("Cannot decrypt message");
216  if(index != -1)
217  {
218  //kdDebug(5100) << "message is encrypted" << endl;
219  status |= ENCRYPTED;
220 
221  // ok. we have an encrypted message. Is the passphrase bad,
222  // or do we not have the secret key?
223  if(error.find("Need a pass phrase") != -1)
224  {
225  if (!passphrase.isEmpty())
226  {
227  errMsg = i18n("Bad passphrase; could not decrypt.");
228  kdDebug(5100) << "Base: passphrase is bad" << endl;
229  status |= BADPHRASE;
230  status |= ERROR;
231  }
232  }
233  else
234  {
235  // we don't have the secret key
236  status |= NO_SEC_KEY;
237  status |= ERROR;
238  errMsg = i18n("You do not have the secret key needed to decrypt this message.");
239  kdDebug(5100) << "Base: no secret key for this message" << endl;
240  }
241  // check for persons
242 #if 0
243  // ##### FIXME: This information is anyway currently not used
244  // I'll change it to always determine the recipients.
245  index = error.find("can only be decrypted by:");
246  if(index != -1)
247  {
248  index = error.find('\n',index);
249  int end = error.find("\n\n",index);
250 
251  mRecipients.clear();
252  int index2;
253  while( (index2 = error.find('\n',index+1)) <= end )
254  {
255  TQCString item = error.mid(index+1,index2-index-1);
256  item.stripWhiteSpace();
257  mRecipients.append(item);
258  index = index2;
259  }
260  }
261 #endif
262  }
263  index = error.find("Good signature");
264  if(index != -1)
265  {
266  //kdDebug(5100) << "good signature" << endl;
267  status |= SIGNED;
268  status |= GOODSIG;
269 
270  // get key ID of signer
271  index = error.find("Key ID ", index) + 7;
272  block.setSignatureKeyId( error.mid(index, 8) );
273 
274  // get signer
275  index = error.find('"',index) + 1;
276  int index2 = error.find('"', index);
277  block.setSignatureUserId( error.mid(index, index2-index) );
278 
280  block.setSignatureDate( "" );
281  }
282  index = error.find("BAD signature");
283  if(index != -1)
284  {
285  //kdDebug(5100) << "BAD signature" << endl;
286  status |= SIGNED;
287  status |= ERROR;
288 
289  // get key ID of signer
290  index = error.find("Key ID ", index) + 7;
291  block.setSignatureKeyId( error.mid(index, 8) );
292 
293  // get signer
294  index = error.find('"',index) + 1;
295  int index2 = error.find('"', index);
296  block.setSignatureUserId( error.mid(index, index2-index) );
297 
299  block.setSignatureDate( "" );
300  }
301  index = error.find("Signature by unknown key");
302  if(index != -1)
303  {
304  index = error.find("keyid: 0x",index) + 9;
305  block.setSignatureKeyId( error.mid(index, 8) );
306  block.setSignatureUserId( TQString() );
307  // FIXME: not a very good solution...
308  status |= SIGNED;
309  status |= GOODSIG;
310 
312  block.setSignatureDate( "" );
313  }
314 
315  //kdDebug(5100) << "status = " << status << endl;
316  block.setStatus( status );
317  return status;
318 }
319 
320 
321 Key*
322 Base5::readPublicKey( const KeyID& keyId, const bool readTrust, Key* key )
323 {
324  int exitStatus = 0;
325 
326  status = 0;
327  exitStatus = run( "pgpk -ll 0x" + keyId, 0, true );
328 
329  if(exitStatus != 0) {
330  status = ERROR;
331  return 0;
332  }
333 
334  key = parseSingleKey( output, key );
335 
336  if( key == 0 )
337  {
338  return 0;
339  }
340 
341  if( readTrust )
342  {
343  exitStatus = run( "pgpk -c 0x" + keyId, 0, true );
344 
345  if(exitStatus != 0) {
346  status = ERROR;
347  return 0;
348  }
349 
350  parseTrustDataForKey( key, output );
351  }
352 
353  return key;
354 }
355 
356 
357 KeyList
358 Base5::publicKeys( const TQStringList & patterns )
359 {
360  int exitStatus = 0;
361 
362  TQCString cmd = "pgpk -ll";
363  for ( TQStringList::ConstIterator it = patterns.begin();
364  it != patterns.end(); ++it ) {
365  cmd += " ";
366  cmd += TDEProcess::quote( *it ).local8Bit();
367  }
368  status = 0;
369  exitStatus = run( cmd, 0, true );
370 
371  if(exitStatus != 0) {
372  status = ERROR;
373  return KeyList();
374  }
375 
376  // now we need to parse the output for public keys
377  KeyList keys = parseKeyList( output, false );
378 
379  // sort the list of public keys
380  keys.sort();
381 
382  return keys;
383 }
384 
385 
386 KeyList
387 Base5::secretKeys( const TQStringList & patterns )
388 {
389  int exitStatus = 0;
390 
391  status = 0;
392  TQCString cmd = "pgpk -ll";
393  for ( TQStringList::ConstIterator it = patterns.begin();
394  it != patterns.end(); ++it ) {
395  cmd += " ";
396  cmd += TDEProcess::quote( *it ).local8Bit();
397  }
398  status = 0;
399  exitStatus = run( cmd, 0, true );
400 
401  if(exitStatus != 0) {
402  status = ERROR;
403  return KeyList();
404  }
405 
406  // now we need to parse the output for secret keys
407  KeyList keys = parseKeyList( output, true );
408 
409  // sort the list of public keys
410  keys.sort();
411 
412  return keys;
413 }
414 
415 
416 TQCString Base5::getAsciiPublicKey(const KeyID& keyID)
417 {
418  int exitStatus = 0;
419 
420  if (keyID.isEmpty())
421  return TQCString();
422 
423  status = 0;
424  exitStatus = run( "pgpk -xa 0x" + keyID, 0, true );
425 
426  if(exitStatus != 0) {
427  status = ERROR;
428  return TQCString();
429  }
430 
431  return output;
432 }
433 
434 
435 int
436 Base5::signKey(const KeyID& keyID, const TQString &passphrase)
437 {
438  TQCString cmd;
439  int exitStatus = 0;
440 
441  if (passphrase.isEmpty()) return false;
442 
443  cmd = "pgpk -s -f +batchmode=1 0x";
444  cmd += keyID;
445  cmd += addUserId();
446 
447  status = 0;
448  exitStatus = run(cmd.data(), passphrase);
449 
450  if (exitStatus != 0)
451  status = ERROR;
452 
453  return status;
454 }
455 
456 //-- private functions --------------------------------------------------------
457 
458 Key*
459 Base5::parseKeyData( const TQCString& output, int& offset, Key* key /* = 0 */ )
460 // This function parses the data for a single key which is output by PGP 5
461 // with the following command line:
462 // pgpk -ll
463 // It expects the key data to start at offset and returns the start of
464 // the next key's data in offset.
465 {
466  if( ( strncmp( output.data() + offset, "pub", 3 ) != 0 ) &&
467  ( strncmp( output.data() + offset, "sec", 3 ) != 0 ) )
468  {
469  kdDebug(5100) << "Unknown key type or corrupt key data.\n";
470  return 0;
471  }
472 
473  if( key == 0 )
474  key = new Key();
475  else
476  key->clear();
477 
478  Subkey *subkey = 0;
479  bool primaryKey = true;
480 
481  while( true )
482  {
483  int eol;
484 
485  // search the end of the current line
486  eol = output.find( '\n', offset );
487  if( ( eol == -1 ) || ( eol == offset ) )
488  break;
489 
490  //kdDebug(5100) << "Parsing: " << output.mid(offset, eol-offset) << endl;
491 
492  if( !strncmp( output.data() + offset, "pub", 3 ) ||
493  !strncmp( output.data() + offset, "sec", 3 ) ||
494  !strncmp( output.data() + offset, "sub", 3 ) )
495  { // line contains key data
496  //kdDebug(5100)<<"Key data:\n";
497  int pos, pos2;
498 
499  subkey = new Subkey( "", false );
500  key->addSubkey( subkey );
501 
502  // Key Flags
503  /* From the PGP 5 manual page for pgpk:
504  Following this column is a single character which
505  describes other attributes of the object:
506 
507  @ The object is disabled
508  + The object is axiomatically trusted (i.e., it's
509  your key)
510  */
511  switch( output[offset+3] )
512  {
513  case ' ': // nothing special
514  break;
515  case '@': // disabled key
516  subkey->setDisabled( true );
517  key->setDisabled( true );
518  break;
519  default: // all other flags are ignored
520  //kdDebug(5100) << "Unknown key flag.\n";
521  ;
522  }
523 
524  // Key Length
525  pos = offset + 4;
526  while( output[pos] == ' ' )
527  pos++;
528  pos2 = output.find( ' ', pos );
529  subkey->setKeyLength( output.mid( pos, pos2-pos ).toUInt() );
530  //kdDebug(5100) << "Key Length: "<<subkey->keyLength()<<endl;
531 
532  // Key ID
533  pos = pos2 + 1;
534  while( output[pos] == ' ' )
535  pos++;
536  pos += 2; // skip the '0x'
537  pos2 = output.find( ' ', pos );
538  subkey->setKeyID( output.mid( pos, pos2-pos ) );
539  //kdDebug(5100) << "Key ID: "<<subkey->keyID()<<endl;
540 
541  // Creation Date
542  pos = pos2 + 1;
543  while( output[pos] == ' ' )
544  pos++;
545  pos2 = output.find( ' ', pos );
546  int year = output.mid( pos, 4 ).toInt();
547  int month = output.mid( pos+5, 2 ).toInt();
548  int day = output.mid( pos+8, 2 ).toInt();
549  TQDateTime dt( TQDate( year, month, day ), TQTime( 00, 00 ) );
550  TQDateTime epoch( TQDate( 1970, 01, 01 ), TQTime( 00, 00 ) );
551  // The calculated creation date isn't exactly correct because TQDateTime
552  // doesn't know anything about timezones and always assumes local time
553  // although epoch is of course UTC. But as PGP 5 anyway doesn't print
554  // the time this doesn't matter too much.
555  subkey->setCreationDate( epoch.secsTo( dt ) );
556 
557  // Expiration Date
558  // if the primary key has been revoked the expiration date is not printed
559  if( primaryKey || !key->revoked() )
560  {
561  pos = pos2 + 1;
562  while( output[pos] == ' ' )
563  pos++;
564  pos2 = output.find( ' ', pos );
565  if( output[pos] == '-' )
566  { // key doesn't expire
567  subkey->setExpirationDate( -1 );
568  }
569  else if( !strncmp( output.data() + pos, "*REVOKED*", 9 ) )
570  { // key has been revoked
571  subkey->setRevoked( true );
572  key->setRevoked( true );
573  }
574  else
575  {
576  int year = output.mid( pos, 4 ).toInt();
577  int month = output.mid( pos+5, 2 ).toInt();
578  int day = output.mid( pos+8, 2 ).toInt();
579  TQDateTime dt( TQDate( year, month, day ), TQTime( 00, 00 ) );
580  subkey->setCreationDate( epoch.secsTo( dt ) );
581  // has the key already expired?
582  if( TQDateTime::currentDateTime() >= dt )
583  {
584  subkey->setExpired( true );
585  key->setExpired( true );
586  }
587  }
588  }
589  else if( key->revoked() )
590  subkey->setRevoked( true );
591 
592  // Key algorithm (RSA, DSS, Diffie-Hellman)
593  bool sign = false;
594  bool encr = false;
595  pos = pos2 + 1;
596  while( output[pos] == ' ' )
597  pos++;
598  pos2 = output.find( ' ', pos );
599  if( !strncmp( output.data() + pos, "RSA", 3 ) )
600  {
601  sign = true;
602  encr = true;
603  }
604  else if( !strncmp( output.data() + pos, "DSS", 3 ) )
605  sign = true;
606  else if( !strncmp( output.data() + pos, "Diffie-Hellman", 14 ) )
607  encr = true;
608  else
609  kdDebug(5100)<<"Unknown key algorithm\n";
610 
611  // set key capabilities of the subkey
612  subkey->setCanEncrypt( encr );
613  subkey->setCanSign( sign );
614  subkey->setCanCertify( sign );
615 
616  if( primaryKey )
617  {
618  // Global key capabilities
619  bool canSign = false;
620  bool canEncr = false;
621  pos = pos2 + 1;
622  while( output[pos] == ' ' )
623  pos++;
624  pos2 = eol;
625  if( !strncmp( output.data() + pos, "Sign & Encrypt", 14 ) )
626  {
627  canSign = true;
628  canEncr = true;
629  }
630  else if( !strncmp( output.data() + pos, "Sign only", 9 ) )
631  canSign = true;
632  else if( !strncmp( output.data() + pos, "Encrypt only", 12 ) )
633  canEncr = true;
634  else
635  kdDebug(5100)<<"Unknown key capability\n";
636 
637  // set the global key capabilities
638  if( !key->expired() && !key->revoked() )
639  {
640  key->setCanEncrypt( canEncr );
641  key->setCanSign( canSign );
642  key->setCanCertify( canSign );
643  }
644  //kdDebug(5100)<<"Key capabilities: "<<(key->canEncrypt()?"E":"")<<(key->canSign()?"SC":"")<<endl;
645  primaryKey = false;
646  }
647  }
648  else if( !strncmp( output.data() + offset, "f16", 3 ) ||
649  !strncmp( output.data() + offset, "f20", 3 ) )
650  { // line contains a fingerprint
651  /* Examples:
652  f16 Fingerprint16 = DE 2A 77 08 78 64 7C 42 72 75 B1 A7 3E 42 3F 79
653  f20 Fingerprint20 = 226F 4B63 6DA2 7389 91D1 2A49 D58A 3EC1 5214 181E
654 
655  */
656  int pos = output.find( '=', offset+3 ) + 2;
657  TQCString fingerprint = output.mid( pos, eol-pos );
658  // remove white space from the fingerprint
659  for ( int idx = 0 ; (idx = fingerprint.find(' ', idx)) >= 0 ; )
660  fingerprint.replace( idx, 1, "" );
661  assert( subkey != 0 );
662  subkey->setFingerprint( fingerprint );
663  //kdDebug(5100)<<"Fingerprint: "<<fingerprint<<endl;
664  }
665  else if( !strncmp( output.data() + offset, "uid", 3 ) )
666  { // line contains a uid
667  int pos = offset+5;
668  TQCString uid = output.mid( pos, eol-pos );
669  key->addUserID( uid );
670  // displaying of uids which contain non-ASCII characters is broken in
671  // PGP 5.0i; it shows these characters as \ooo and truncates the uid
672  // because it doesn't take the 3 extra characters per non-ASCII char
673  // into account. Example (with an UTF-8 encoded &ouml;):
674  // uid Ingo Kl\303\266cker <ingo.kloecker@epo
675  // because of this and because we anyway don't know which charset was
676  // used to encode the uid we don't try to decode it
677  }
678  else if ( !strncmp( output.data() + offset, "sig", 3 ) ||
679  !strncmp( output.data() + offset, "SIG", 3 ) ||
680  !strncmp( output.data() + offset, "ret", 3 ) )
681  { // line contains a signature
682  // SIG = sig with own key; ret = sig with revoked key
683  // we ignore it for now
684  }
685 
686  offset = eol + 1;
687  }
688 
689  return key;
690 }
691 
692 
693 Key*
694 Base5::parseSingleKey( const TQCString& output, Key* key /* = 0 */ )
695 {
696  int offset;
697 
698  // search start of header line
699  if( !strncmp( output.data(), "Type Bits", 9 ) )
700  offset = 0;
701  else
702  {
703  offset = output.find( "\nType Bits" ) + 1;
704  if( offset == 0 )
705  return 0;
706  }
707 
708  // key data begins in the next line
709  offset = output.find( '\n', offset ) + 1;
710  if( offset == -1 )
711  return 0;
712 
713  key = parseKeyData( output, offset, key );
714 
715  //kdDebug(5100) << "finished parsing keys" << endl;
716 
717  return key;
718 }
719 
720 
721 KeyList
722 Base5::parseKeyList( const TQCString& output, bool onlySecretKeys )
723 {
724  KeyList keys;
725  Key *key = 0;
726  int offset;
727 
728  // search start of header line
729  if( !strncmp( output.data(), "Type Bits", 9 ) )
730  offset = 0;
731  else
732  {
733  offset = output.find( "\nType Bits" ) + 1;
734  if( offset == 0 )
735  return keys;
736  }
737 
738  // key data begins in the next line
739  offset = output.find( '\n', offset ) + 1;
740  if( offset == -1 )
741  return keys;
742 
743  do
744  {
745  key = parseKeyData( output, offset );
746  if( key != 0 )
747  {
748  // if only secret keys should be read test if the key is secret
749  if( !onlySecretKeys || !key->secret() )
750  keys.append( key );
751  // skip the blank line which separates the keys
752  offset++;
753  }
754  }
755  while( key != 0 );
756 
757  //kdDebug(5100) << "finished parsing keys" << endl;
758 
759  return keys;
760 }
761 
762 
763 void
764 Base5::parseTrustDataForKey( Key* key, const TQCString& str )
765 {
766  if( ( key == 0 ) || str.isEmpty() )
767  return;
768 
769  TQCString keyID = "0x" + key->primaryKeyID();
770  UserIDList userIDs = key->userIDs();
771 
772  // search the start of the trust data
773  int offset = str.find( "\n\n KeyID" ) + 9;
774  if( offset == -1 + 9 )
775  return;
776 
777  offset = str.find( '\n', offset ) + 1;
778  if( offset == -1 + 1 )
779  return;
780 
781  bool ultimateTrust = false;
782  if( !strncmp( str.data() + offset+13, "ultimate", 8 ) )
783  ultimateTrust = true;
784 
785  while( true )
786  { // loop over all trust information about this key
787 
788  int eol;
789 
790  // search the end of the current line
791  if( ( eol = str.find( '\n', offset ) ) == -1 )
792  break;
793 
794  if( str[offset+23] != ' ' )
795  { // line contains a validity value for a user ID
796 
797  // determine the validity
798  Validity validity = KPGP_VALIDITY_UNKNOWN;
799  if( !strncmp( str.data() + offset+23, "complete", 8 ) )
800  if( ultimateTrust )
801  validity = KPGP_VALIDITY_ULTIMATE;
802  else
803  validity = KPGP_VALIDITY_FULL;
804  else if( !strncmp( str.data() + offset+23, "marginal", 8 ) )
805  validity = KPGP_VALIDITY_MARGINAL;
806  else if( !strncmp( str.data() + offset+23, "invalid", 7 ) )
807  validity = KPGP_VALIDITY_UNDEFINED;
808 
809  // determine the user ID
810  int pos = offset + 33;
811  TQString uid = str.mid( pos, eol-pos );
812 
813  // set the validity of the corresponding user ID
814  for( UserIDListIterator it( userIDs ); it.current(); ++it )
815  if( (*it)->text() == uid )
816  {
817  kdDebug(5100)<<"Setting the validity of "<<uid<<" to "<<validity<<endl;
818  (*it)->setValidity( validity );
819  break;
820  }
821  }
822 
823  offset = eol + 1;
824  }
825 }
826 
827 
828 } // namespace Kpgp