libkpgp

kpgpbaseG.cpp

00001 /*
00002     kpgpbaseG.cpp
00003 
00004     Copyright (C) 2001,2002 the KPGP authors
00005     See file AUTHORS.kpgp for details
00006 
00007     This file is part of KPGP, the KDE PGP/GnuPG support library.
00008 
00009     KPGP is free software; you can redistribute it and/or modify
00010     it under the terms of the GNU General Public License as published by
00011     the Free Software Foundation; either version 2 of the License, or
00012     (at your option) any later version.
00013 
00014     You should have received a copy of the GNU General Public License
00015     along with this program; if not, write to the Free Software Foundation,
00016     Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
00017  */
00018 
00019 #ifdef HAVE_CONFIG_H
00020 #include <config.h>
00021 #endif
00022 
00023 #include "kpgpbase.h"
00024 #include "kpgp.h"
00025 
00026 #include <tdelocale.h>
00027 #include <kprocess.h>
00028 #include <kdebug.h>
00029 
00030 #include <tqtextcodec.h>
00031 
00032 #include <string.h> /* strncmp */
00033 
00034 namespace Kpgp {
00035 
00036 BaseG::BaseG()
00037   : Base()
00038 {
00039   // determine the version of gpg (the method is equivalent to gpgme's method)
00040   runGpg( "--version", 0 );
00041   int eol = output.find( '\n' );
00042   if( eol > 0 ) {
00043     int pos = output.findRev( ' ', eol - 1 );
00044     if( pos != -1 ) {
00045       mVersion = output.mid( pos + 1, eol - pos - 1 );
00046       kdDebug(5100) << "found GnuPG " << mVersion << endl;
00047     }
00048   }
00049 }
00050 
00051 
00052 BaseG::~BaseG()
00053 {
00054 }
00055 
00056 
00057 int
00058 BaseG::encrypt( Block& block, const KeyIDList& recipients )
00059 {
00060   return encsign( block, recipients, 0 );
00061 }
00062 
00063 
00064 int
00065 BaseG::clearsign( Block& block, const char *passphrase )
00066 {
00067   return encsign( block, KeyIDList(), passphrase );
00068 }
00069 
00070 
00071 int
00072 BaseG::encsign( Block& block, const KeyIDList& recipients,
00073                 const char *passphrase )
00074 {
00075   TQCString cmd;
00076   int exitStatus = 0;
00077 
00078   if(!recipients.isEmpty() && passphrase != 0)
00079     cmd = "--batch --armor --sign --encrypt --textmode";
00080   else if(!recipients.isEmpty())
00081     cmd = "--batch --armor --encrypt --textmode";
00082   else if(passphrase != 0)
00083     cmd = "--batch --escape-from --clearsign";
00084   else
00085   {
00086     kdDebug(5100) << "kpgpbase: Neither recipients nor passphrase specified." << endl;
00087     return OK;
00088   }
00089 
00090   if(passphrase != 0)
00091     cmd += addUserId();
00092 
00093   if(!recipients.isEmpty())
00094   {
00095     cmd += " --set-filename stdin";
00096 
00097     TQCString pgpUser = Module::getKpgp()->user();
00098     if(Module::getKpgp()->encryptToSelf() && !pgpUser.isEmpty()) {
00099       cmd += " -r 0x";
00100       cmd += pgpUser;
00101     }
00102 
00103     for( KeyIDList::ConstIterator it = recipients.begin();
00104          it != recipients.end(); ++it ) {
00105       cmd += " -r 0x";
00106       cmd += (*it);
00107     }
00108   }
00109 
00110   clear();
00111   input = block.text();
00112   exitStatus = runGpg(cmd.data(), passphrase);
00113   if( !output.isEmpty() )
00114     block.setProcessedText( output );
00115   block.setError( error );
00116 
00117   if( exitStatus != 0 )
00118   {
00119     // this error message is later hopefully overwritten
00120     errMsg = i18n( "Unknown error." );
00121     status = ERROR;
00122   }
00123 
00124 #if 0
00125   // #### FIXME: As we check the keys ourselves the following problems
00126   //             shouldn't occur. Therefore I don't handle them for now.
00127   //             IK 01/2002
00128   if(!recipients.isEmpty())
00129   {
00130     int index = 0;
00131     bool bad = FALSE;
00132     unsigned int num = 0;
00133     TQCString badkeys = "";
00134     // Examples:
00135     // gpg: 0x12345678: skipped: public key not found
00136     // gpg: 0x12345678: skipped: public key is disabled
00137     // gpg: 0x12345678: skipped: unusable public key
00138     // (expired or revoked key)
00139     // gpg: 23456789: no info to calculate a trust probability
00140     // (untrusted key, 23456789 is the key Id of the encryption sub key)
00141     while((index = error.find("skipped: ",index)) != -1)
00142     {
00143       bad = TRUE;
00144       index = error.find('\'',index);
00145       int index2 = error.find('\'',index+1);
00146       badkeys += error.mid(index, index2-index+1) + ", ";
00147       num++;
00148     }
00149     if(bad)
00150     {
00151       badkeys.stripWhiteSpace();
00152       if(num == recipients.count())
00153         errMsg = i18n("Could not find public keys matching the userid(s)\n"
00154                       "%1;\n"
00155                       "the message is not encrypted.")
00156                      .arg( badkeys.data() );
00157       else
00158         errMsg = i18n("Could not find public keys matching the userid(s)\n"
00159                       "%1;\n"
00160                       "these persons will not be able to read the message.")
00161                      .arg( badkeys.data() );
00162       status |= MISSINGKEY;
00163       status |= ERROR;
00164     }
00165   }
00166 #endif
00167   if( passphrase != 0 )
00168   {
00169     // Example 1 (bad passphrase, clearsign only):
00170     // gpg: skipped `0x12345678': bad passphrase
00171     // gpg: [stdin]: clearsign failed: bad passphrase
00172     // Example 2 (bad passphrase, sign & encrypt):
00173     // gpg: skipped `0x12345678': bad passphrase
00174     // gpg: [stdin]: sign+encrypt failed: bad passphrase
00175     // Example 3 (unusable secret key, clearsign only):
00176     // gpg: skipped `0x12345678': unusable secret key
00177     // gpg: [stdin]: clearsign failed: unusable secret key
00178     // Example 4 (unusable secret key, sign & encrypt):
00179     // gpg: skipped `0xAC0EB35D': unusable secret key
00180     // gpg: [stdin]: sign+encrypt failed: unusable secret key
00181     if( error.find("bad passphrase") != -1 )
00182     {
00183       errMsg = i18n("Signing failed because the passphrase is wrong.");
00184       status |= BADPHRASE;
00185       status |= ERR_SIGNING;
00186       status |= ERROR;
00187     }
00188     else if( error.find("unusable secret key") != -1 )
00189     {
00190       errMsg = i18n("Signing failed because your secret key is unusable.");
00191       status |= ERR_SIGNING;
00192       status |= ERROR;
00193     }
00194     else if( !( status & ERROR ) )
00195     {
00196       //kdDebug(5100) << "Base: Good Passphrase!" << endl;
00197       status |= SIGNED;
00198     }
00199   }
00200 
00201   //kdDebug(5100) << "status = " << status << endl;
00202   block.setStatus( status );
00203   return status;
00204 }
00205 
00206 
00207 int
00208 BaseG::decrypt( Block& block, const char *passphrase )
00209 {
00210   int index, index2;
00211   int exitStatus = 0;
00212 
00213   clear();
00214   input = block.text();
00215   exitStatus = runGpg("--batch --decrypt", passphrase);
00216   if( !output.isEmpty() && ( error.find( "gpg: quoted printable" ) == -1 ) )
00217     block.setProcessedText( output );
00218   block.setError( error );
00219 
00220   if(exitStatus == -1) {
00221     errMsg = i18n("Error running gpg");
00222     status = RUN_ERR;
00223     block.setStatus( status );
00224     return status;
00225   }
00226 
00227   // Example 1 (good passphrase, decryption successful):
00228   // gpg: encrypted with 2048-bit ELG-E key, ID 12345678, created 2000-11-11
00229   //       "Foo Bar <foo@bar.xyz>"
00230   //
00231   // Example 2 (bad passphrase):
00232   // gpg: encrypted with 1024-bit RSA key, ID 12345678, created 1991-01-01
00233   //       "Foo Bar <foo@bar.xyz>"
00234   // gpg: public key decryption failed: bad passphrase
00235   // gpg: decryption failed: secret key not available
00236   //
00237   // Example 3 (no secret key available):
00238   // gpg: encrypted with RSA key, ID 12345678
00239   // gpg: decryption failed: secret key not available
00240   //
00241   // Example 4 (good passphrase for second key, decryption successful):
00242   // gpg: encrypted with 2048-bit ELG-E key, ID 12345678, created 2000-01-01
00243   //       "Foo Bar (work) <foo@bar.xyz>"
00244   // gpg: public key decryption failed: bad passphrase
00245   // gpg: encrypted with 2048-bit ELG-E key, ID 23456789, created 2000-02-02
00246   //       "Foo Bar (home) <foo@bar.xyz>"
00247   if( error.find( "gpg: encrypted with" ) != -1 )
00248   {
00249     //kdDebug(5100) << "kpgpbase: message is encrypted" << endl;
00250     status |= ENCRYPTED;
00251     if( error.find( "\ngpg: decryption failed" ) != -1 )
00252     {
00253       if( ( index = error.find( "bad passphrase" ) ) != -1 )
00254       {
00255         if( passphrase != 0 )
00256         {
00257           errMsg = i18n( "Bad passphrase; could not decrypt." );
00258           kdDebug(5100) << "Base: passphrase is bad" << endl;
00259           status |= BADPHRASE;
00260           status |= ERROR;
00261         }
00262         else
00263         {
00264           // Search backwards the user ID of the needed key
00265           index2 = error.findRev('"', index) - 1;
00266           index = error.findRev("      \"", index2) + 7;
00267           // The conversion from UTF8 is necessary because gpg stores and
00268           // prints user IDs in UTF8
00269           block.setRequiredUserId( TQString::fromUtf8( error.mid( index, index2 - index + 1 ) ) );
00270           kdDebug(5100) << "Base: key needed is \"" << block.requiredUserId() << "\"!" << endl;
00271         }
00272       }
00273       else if( error.find( "secret key not available" ) != -1 )
00274       {
00275         // no secret key fitting this message
00276         status |= NO_SEC_KEY;
00277         status |= ERROR;
00278         errMsg = i18n("You do not have the secret key needed to decrypt this message.");
00279         kdDebug(5100) << "Base: no secret key for this message" << endl;
00280       }
00281     }
00282     // check for persons
00283 #if 0
00284     // ##### FIXME: This information is anyway currently not used
00285     //       I'll change it to always determine the recipients.
00286     index = error.find("can only be read by:");
00287     if(index != -1)
00288     {
00289       index = error.find('\n',index);
00290       int end = error.find("\n\n",index);
00291 
00292       mRecipients.clear();
00293       while( (index2 = error.find('\n',index+1)) <= end )
00294       {
00295     TQCString item = error.mid(index+1,index2-index-1);
00296     item.stripWhiteSpace();
00297     mRecipients.append(item);
00298     index = index2;
00299       }
00300     }
00301 #endif
00302   }
00303 
00304   // Example 1 (unknown signature key):
00305   // gpg: Signature made Wed 02 Jan 2002 11:26:33 AM CET using DSA key ID 2E250C64
00306   // gpg: Can't check signature: public key not found
00307   if((index = error.find("Signature made")) != -1)
00308   {
00309     //kdDebug(5100) << "Base: message is signed" << endl;
00310     status |= SIGNED;
00311     // get signature date and signature key ID
00312     // Example: Signature made Sun 06 May 2001 03:49:27 PM CEST using DSA key ID 12345678
00313     index2 = error.find("using", index+15);
00314     block.setSignatureDate( error.mid(index+15, index2-(index+15)-1) );
00315     kdDebug(5100) << "Message was signed on '" << block.signatureDate() << "'\n";
00316     // To handle gnupg > 2.1
00317     // gpg: Signature made Thu 05 Apr 2018 10:02:50 PM CEST
00318     // gpg:                using DSA key A0CF1DC09533E5E87F54DB40F1EEB8CD9FB16A50
00319     // gpg: Good signature from "deloptes <deloptes@gmail.com>" [ultimate]
00320     // so we need extra check
00321     if (error.contains("key ID") > 0) {
00322         index2 = error.find("key ID ", index2) + 7;
00323         block.setSignatureKeyId( error.mid(index2,8) );
00324     }
00325     else {
00326         index2 = error.find("key ", index2) + 4;
00327         // handle variable key size
00328         // gpg: Signature made Mon 02 Apr 2018 03:15:08 PM CEST
00329         // gpg:                using DSA key 05C82CF57AD1DA46
00330         // gpg: Can't check signature: No public key
00331         int end =  error.find("\n", index2);
00332         block.setSignatureKeyId( error.mid(index2,end-index2) );
00333     }
00334     kdDebug(5100) << "Message was signed with key '" << block.signatureKeyId() << "'\n";
00335     // move index to start of next line
00336     index = error.find('\n', index2)+1;
00337 
00338     if ((error.find("Key matching expected", index) != -1)
00339         || (error.find("Can't check signature", index) != -1))
00340     {
00341       status |= UNKNOWN_SIG;
00342       status |= GOODSIG;
00343       block.setSignatureUserId( TQString() );
00344     }
00345     else if( error.find("Good signature", index) != -1 )
00346     {
00347       status |= GOODSIG;
00348       // get the primary user ID of the signer
00349       index = error.find('"',index);
00350       index2 = error.find('\n',index+1);
00351       index2 = error.findRev('"', index2-1);
00352       block.setSignatureUserId( TQString::fromLocal8Bit( error.mid( index+1, index2-index-1 ) ) );
00353     }
00354     else if( error.find("BAD signature", index) != -1 )
00355     {
00356       //kdDebug(5100) << "BAD signature" << endl;
00357       status |= ERROR;
00358       // get the primary user ID of the signer
00359       index = error.find('"',index);
00360       index2 = error.find('\n',index+1);
00361       index2 = error.findRev('"', index2-1);
00362       block.setSignatureUserId( TQString::fromLocal8Bit(  error.mid( index+1, index2-index-1 ) ) );
00363     }
00364     else if( error.find("Can't find the right public key", index) != -1 )
00365     {
00366       // #### fix this hack
00367       // I think this can't happen anymore because if the pubring is missing
00368       // the current GnuPG creates a new empty one.
00369       status |= UNKNOWN_SIG;
00370       status |= GOODSIG; // this is a hack...
00371       block.setSignatureUserId( i18n("??? (file ~/.gnupg/pubring.gpg not found)") );
00372     }
00373     else
00374     {
00375       status |= ERROR;
00376       block.setSignatureUserId( TQString() );
00377     }
00378   }
00379   //kdDebug(5100) << "status = " << status << endl;
00380   block.setStatus( status );
00381   return status;
00382 }
00383 
00384 
00385 Key*
00386 BaseG::readPublicKey( const KeyID& keyID,
00387                       const bool readTrust /* = false */,
00388                       Key* key /* = 0 */ )
00389 {
00390   int exitStatus = 0;
00391 
00392   status = 0;
00393   if( readTrust )
00394     exitStatus = runGpg( "--batch --list-public-keys --with-fingerprint --with-colons --fixed-list-mode 0x" + keyID, 0, true );
00395   else
00396     exitStatus = runGpg( "--batch --list-public-keys --with-fingerprint --with-colons --fixed-list-mode --no-expensive-trust-checks 0x" + keyID, 0, true );
00397 
00398   if(exitStatus != 0) {
00399     status = ERROR;
00400     return 0;
00401   }
00402 
00403   int offset;
00404   // search start of key data
00405   if( !strncmp( output.data(), "pub:", 4 ) )
00406     offset = 0;
00407   else {
00408     offset = output.find( "\npub:" );
00409     if( offset == -1 )
00410       return 0;
00411     else
00412       offset++;
00413   }
00414 
00415   key = parseKeyData( output, offset, key );
00416 
00417   return key;
00418 }
00419 
00420 
00421 KeyList
00422 BaseG::publicKeys( const TQStringList & patterns )
00423 {
00424   int exitStatus = 0;
00425 
00426   // the option --with-colons should be used for interprocess communication
00427   // with gpg (according to Werner Koch)
00428   TQCString cmd = "--batch --list-public-keys --with-fingerprint --with-colons "
00429                  "--fixed-list-mode --no-expensive-trust-checks";
00430   for ( TQStringList::ConstIterator it = patterns.begin();
00431         it != patterns.end(); ++it ) {
00432     cmd += " ";
00433     cmd += TDEProcess::quote( *it ).local8Bit();
00434   }
00435   status = 0;
00436   exitStatus = runGpg( cmd, 0, true );
00437 
00438   if(exitStatus != 0) {
00439     status = ERROR;
00440     return KeyList();
00441   }
00442 
00443   // now we need to parse the output for public keys
00444   KeyList publicKeys = parseKeyList(output, false);
00445 
00446   // sort the list of public keys
00447   publicKeys.sort();
00448 
00449   return publicKeys;
00450 }
00451 
00452 
00453 KeyList
00454 BaseG::secretKeys( const TQStringList & patterns )
00455 {
00456   int exitStatus = 0;
00457 
00458   // the option --with-colons should be used for interprocess communication
00459   // with gpg (according to Werner Koch)
00460   TQCString cmd = "--batch --list-secret-keys --with-fingerprint --with-colons "
00461                  "--fixed-list-mode";
00462   for ( TQStringList::ConstIterator it = patterns.begin();
00463         it != patterns.end(); ++it ) {
00464     cmd += " ";
00465     cmd += TDEProcess::quote( *it ).local8Bit();
00466   }
00467   status = 0;
00468   exitStatus = runGpg( cmd, 0, true );
00469 
00470   if(exitStatus != 0) {
00471     status = ERROR;
00472     return KeyList();
00473   }
00474 
00475   // now we need to parse the output for secret keys
00476   KeyList secretKeys = parseKeyList(output, true);
00477 
00478   // sort the list of secret keys
00479   secretKeys.sort();
00480 
00481   return secretKeys;
00482 }
00483 
00484 
00485 int
00486 BaseG::signKey(const KeyID& keyID, const char *passphrase)
00487 {
00488   TQCString cmd;
00489   int exitStatus = 0;
00490 
00491   cmd = "--batch";
00492   cmd += addUserId();
00493   cmd += " --sign-key 0x";
00494   cmd += keyID;
00495 
00496   status = 0;
00497   exitStatus = runGpg(cmd.data(), passphrase);
00498 
00499   if (exitStatus != 0)
00500     status = ERROR;
00501 
00502   return status;
00503 }
00504 
00505 
00506 TQCString
00507 BaseG::getAsciiPublicKey(const KeyID& keyID)
00508 {
00509   int exitStatus = 0;
00510 
00511   if (keyID.isEmpty())
00512     return TQCString();
00513 
00514   status = 0;
00515   exitStatus = runGpg("--batch --armor --export 0x" + keyID, 0, true);
00516 
00517   if(exitStatus != 0) {
00518     status = ERROR;
00519     return TQCString();
00520   }
00521 
00522   return output;
00523 }
00524 
00525 
00526 Key*
00527 BaseG::parseKeyData( const TQCString& output, int& offset, Key* key /* = 0 */ )
00528 // This function parses the data for a single key which is output by GnuPG
00529 // with the following command line arguments:
00530 //   --batch --list-public-keys --with-fingerprint --with-colons
00531 //   --fixed-list-mode [--no-expensive-trust-checks]
00532 // It expects the key data to start at offset and returns the start of
00533 // the next key's data in offset.
00534 // Subkeys are currently ignored.
00535 {
00536   int index = offset;
00537 
00538   if(    ( strncmp( output.data() + offset, "pub:", 4 ) != 0 )
00539       && ( strncmp( output.data() + offset, "sec:", 4 ) != 0 ) ) {
00540     return 0;
00541   }
00542 
00543   if( key == 0 )
00544     key = new Key();
00545   else
00546     key->clear();
00547 
00548   TQCString keyID;
00549   bool firstKey = true;
00550 
00551   while( true )
00552   {
00553     int eol;
00554     // search the end of the current line
00555     if( ( eol = output.find( '\n', index ) ) == -1 )
00556       break;
00557 
00558     bool bIsPublicKey = false;
00559     if( ( bIsPublicKey = !strncmp( output.data() + index, "pub:", 4 ) )
00560         || !strncmp( output.data() + index, "sec:", 4 ) )
00561     { // line contains primary key data
00562       // Example: pub:f:1024:17:63CB691DFAEBD5FC:860451781::379:-:::scESC:
00563 
00564       // abort parsing if we found the start of the next key
00565       if( !firstKey )
00566         break;
00567       firstKey = false;
00568 
00569       key->setSecret( !bIsPublicKey );
00570 
00571       Subkey *subkey = new Subkey( TQCString(), !bIsPublicKey );
00572 
00573       int pos = index + 4; // begin of 2nd field
00574       int pos2 = output.find( ':', pos );
00575       for( int field = 2; field <= 12; field++ )
00576       {
00577         switch( field )
00578         {
00579         case 2: // the calculated trust
00580           if( pos2 > pos )
00581           {
00582             switch( output[pos] )
00583             {
00584             case 'o': // unknown (this key is new to the system)
00585               break;
00586             case 'i': // the key is invalid, e.g. missing self-signature
00587               subkey->setInvalid( true );
00588               key->setInvalid( true );
00589               break;
00590             case 'd': // the key has been disabled
00591               subkey->setDisabled( true );
00592               key->setDisabled( true );
00593               break;
00594             case 'r': // the key has been revoked
00595               subkey->setRevoked( true );
00596               key->setRevoked( true );
00597               break;
00598             case 'e': // the key has expired
00599               subkey->setExpired( true );
00600               key->setExpired( true );
00601               break;
00602             case '-': // undefined (no path leads to the key)
00603             case 'q': // undefined (no trusted path leads to the key)
00604             case 'n': // don't trust this key at all
00605             case 'm': // the key is marginally trusted
00606             case 'f': // the key is fully trusted
00607             case 'u': // the key is ultimately trusted (secret key available)
00608               // These values are ignored since we determine the key trust
00609               // from the trust values of the user ids.
00610               break;
00611             default:
00612               kdDebug(5100) << "Unknown trust value\n";
00613             }
00614           }
00615           break;
00616         case 3: // length of key in bits
00617           if( pos2 > pos )
00618             subkey->setKeyLength( output.mid( pos, pos2-pos ).toUInt() );
00619           break;
00620         case 4: //  the key algorithm
00621           if( pos2 > pos )
00622             subkey->setKeyAlgorithm( output.mid( pos, pos2-pos ).toUInt() );
00623           break;
00624         case 5: // the long key id
00625           keyID = output.mid( pos, pos2-pos );
00626           subkey->setKeyID( keyID );
00627           break;
00628         case 6: // the creation date (in seconds since 1970-01-01 00:00:00)
00629           if( pos2 > pos )
00630             subkey->setCreationDate( output.mid( pos, pos2-pos ).toLong() );
00631           break;
00632         case 7: // the expiration date (in seconds since 1970-01-01 00:00:00)
00633           if( pos2 > pos )
00634             subkey->setExpirationDate( output.mid( pos, pos2-pos ).toLong() );
00635           else
00636             subkey->setExpirationDate( -1 ); // key expires never
00637           break;
00638         case 8: // local ID (ignored)
00639         case 9: // Ownertrust (ignored for now)
00640         case 10: // User-ID (always empty in --fixed-list-mode)
00641         case 11: // signature class (always empty except for key signatures)
00642           break;
00643         case 12: // key capabilities
00644           for( int i=pos; i<pos2; i++ )
00645             switch( output[i] )
00646             {
00647             case 'e':
00648               subkey->setCanEncrypt( true );
00649               break;
00650             case 's':
00651               subkey->setCanSign( true );
00652               break;
00653             case 'c':
00654               subkey->setCanCertify( true );
00655               break;
00656             case 'E':
00657               key->setCanEncrypt( true );
00658               break;
00659             case 'S':
00660               key->setCanSign( true );
00661               break;
00662             case 'C':
00663               key->setCanCertify( true );
00664               break;
00665             default:
00666               kdDebug(5100) << "Unknown key capability\n";
00667             }
00668           break;
00669         }
00670         pos = pos2 + 1;
00671         pos2 = output.find( ':', pos );
00672       }
00673       key->addSubkey( subkey );
00674     }
00675     else if( !strncmp( output.data() + index, "uid:", 4 ) )
00676     { // line contains a user id
00677       // Example: uid:f::::::::Philip R. Zimmermann <prz@pgp.com>:
00678 
00679       UserID *userID = new UserID( "" );
00680 
00681       int pos = index + 4; // begin of 2nd field
00682       int pos2 = output.find( ':', pos );
00683       for( int field=2; field <= 10; field++ )
00684       {
00685         switch( field )
00686         {
00687         case 2: // the calculated trust
00688           if( pos2 > pos )
00689           {
00690             switch( output[pos] )
00691             {
00692             case 'i': // the user id is invalid, e.g. missing self-signature
00693               userID->setInvalid( true );
00694               break;
00695             case 'r': // the user id has been revoked
00696               userID->setRevoked( true );
00697               break;
00698             case '-': // undefined (no path leads to the key)
00699             case 'q': // undefined (no trusted path leads to the key)
00700               userID->setValidity( KPGP_VALIDITY_UNDEFINED );
00701               break;
00702             case 'n': // don't trust this key at all
00703               userID->setValidity( KPGP_VALIDITY_NEVER );
00704               break;
00705             case 'm': // the key is marginally trusted
00706               userID->setValidity( KPGP_VALIDITY_MARGINAL );
00707               break;
00708             case 'f': // the key is fully trusted
00709               userID->setValidity( KPGP_VALIDITY_FULL );
00710               break;
00711             case 'u': // the key is ultimately trusted (secret key available)
00712               userID->setValidity( KPGP_VALIDITY_ULTIMATE );
00713               break;
00714             default:
00715               kdDebug(5100) << "Unknown trust value\n";
00716             }
00717           }
00718           break;
00719         case 3: // these fields are empty
00720         case 4:
00721         case 5:
00722         case 6:
00723         case 7:
00724         case 8:
00725         case 9:
00726           break;
00727         case 10: // User-ID
00728           TQCString uid = output.mid( pos, pos2-pos );
00729           // replace "\xXX" with the corresponding character;
00730           // other escaped characters, i.e. \n, \r etc., are ignored
00731           // because they shouldn't appear in user IDs
00732           for ( int idx = 0 ; (idx = uid.find( "\\x", idx )) >= 0 ; ++idx ) {
00733             char str[2] = "x";
00734             str[0] = (char) TQString( uid.mid( idx + 2, 2 ) ).toShort( 0, 16 );
00735             uid.replace( idx, 4, str );
00736           }
00737           TQString uidString = TQString::fromUtf8( uid.data() );
00738           // check whether uid was utf-8 encoded
00739           bool isUtf8 = true;
00740           for ( unsigned int i = 0; i + 1 < uidString.length(); ++i ) {
00741             if ( uidString[i].unicode() == 0xdbff &&
00742                  uidString[i+1].row() == 0xde ) {
00743               // we found a non-Unicode character (see TQString::fromUtf8())
00744               isUtf8 = false;
00745               break;
00746             }
00747           }
00748           if( !isUtf8 ) {
00749             // The user id isn't utf-8 encoded. It was most likely
00750             // created with PGP which either used latin1 or koi8-r.
00751             kdDebug(5100) << "User Id '" << uid
00752                           << "' doesn't seem to be utf-8 encoded." << endl;
00753 
00754             // We determine the ratio between non-ASCII and ASCII chars.
00755             // A koi8-r user id should have lots of non-ASCII chars.
00756             int nonAsciiCount = 0, asciiCount = 0;
00757 
00758             // We only look at the first part of the user id (i. e. everything
00759             // before the email address resp. before a comment)
00760             for( signed char* ch = (signed char*)uid.data();
00761                  *ch && ( *ch != '(' ) && ( *ch != '<' );
00762                  ++ch ) {
00763               if( ( ( *ch >= 'A' ) && ( *ch <= 'Z' ) )
00764                   || ( ( *ch >= 'a' ) && ( *ch <= 'z' ) ) )
00765                 ++asciiCount;
00766               else if( *ch < 0 )
00767                 ++nonAsciiCount;
00768             }
00769             kdDebug(5100) << "ascii-nonAscii ratio : " << asciiCount
00770                           << ":" << nonAsciiCount << endl;
00771             if( nonAsciiCount > asciiCount ) {
00772               // assume koi8-r encoding
00773               kdDebug(5100) << "Assume koi8-r encoding." << endl;
00774               TQTextCodec *codec = TQTextCodec::codecForName("KOI8-R");
00775               uidString = codec->toUnicode( uid.data() );
00776               // check the case of the first two characters to find out
00777               // whether the user id is probably CP1251 encoded (for some
00778               // reason in CP1251 the lower case characters have smaller
00779               // codes than the upper case characters, so if the first char
00780               // of the koi8-r decoded user id is lower case and the second
00781               // char is upper case then it's likely that the user id is
00782               // CP1251 encoded)
00783               if( ( uidString.length() >= 2 )
00784                   && ( uidString[0].lower() == uidString[0] )
00785                   && ( uidString[1].upper() == uidString[1] ) ) {
00786                 // koi8-r decoded user id has inverted case, so assume
00787                 // CP1251 encoding
00788                 kdDebug(5100) << "No, it doesn't seem to be koi8-r. "
00789                                  "Use CP 1251 instead." << endl;
00790                 TQTextCodec *codec = TQTextCodec::codecForName("CP1251");
00791                 uidString = codec->toUnicode( uid.data() );
00792               }
00793             }
00794             else {
00795               // assume latin1 encoding
00796               kdDebug(5100) << "Assume latin1 encoding." << endl;
00797               uidString = TQString::fromLatin1( uid.data() );
00798             }
00799           }
00800           userID->setText( uidString );
00801           break;
00802         }
00803         pos = pos2 + 1;
00804         pos2 = output.find( ':', pos );
00805       }
00806 
00807       // user IDs are printed in UTF-8 by gpg (if one uses --with-colons)
00808       key->addUserID( userID );
00809     }
00810     else if( !strncmp( output.data() + index, "fpr:", 4 ) )
00811     { // line contains a fingerprint
00812       // Example: fpr:::::::::17AFBAAF21064E513F037E6E63CB691DFAEBD5FC:
00813 
00814       if (key == 0) // invalid key data
00815     break;
00816 
00817       // search the fingerprint (it's in the 10th field)
00818       int pos = index + 4;
00819       for( int i = 0; i < 8; i++ )
00820         pos = output.find( ':', pos ) + 1;
00821       int pos2 = output.find( ':', pos );
00822 
00823       key->setFingerprint( keyID, output.mid( pos, pos2-pos ) );
00824     }
00825     index = eol + 1;
00826   }
00827 
00828   //kdDebug(5100) << "finished parsing key data\n";
00829 
00830   offset = index;
00831 
00832   return key;
00833 }
00834 
00835 
00836 KeyList
00837 BaseG::parseKeyList( const TQCString& output, bool secretKeys )
00838 {
00839   KeyList keys;
00840   Key *key = 0;
00841   int offset;
00842 
00843   // search start of key data
00844   if(    !strncmp( output.data(), "pub:", 4 )
00845       || !strncmp( output.data(), "sec:", 4 ) )
00846     offset = 0;
00847   else {
00848     if( secretKeys )
00849       offset = output.find( "\nsec:" );
00850     else
00851       offset = output.find( "\npub:" );
00852     if( offset == -1 )
00853       return keys;
00854     else
00855       offset++;
00856   }
00857 
00858   do {
00859     key = parseKeyData( output, offset );
00860     if( key != 0 )
00861       keys.append( key );
00862   }
00863   while( key != 0 );
00864 
00865   //kdDebug(5100) << "finished parsing keys" << endl;
00866 
00867   return keys;
00868 }
00869 
00870 
00871 } // namespace Kpgp