00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
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>
00033
00034 namespace Kpgp {
00035
00036 BaseG::BaseG()
00037 : Base()
00038 {
00039
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
00120 errMsg = i18n( "Unknown error." );
00121 status = ERROR;
00122 }
00123
00124 #if 0
00125
00126
00127
00128 if(!recipients.isEmpty())
00129 {
00130 int index = 0;
00131 bool bad = FALSE;
00132 unsigned int num = 0;
00133 TQCString badkeys = "";
00134
00135
00136
00137
00138
00139
00140
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
00170
00171
00172
00173
00174
00175
00176
00177
00178
00179
00180
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
00197 status |= SIGNED;
00198 }
00199 }
00200
00201
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
00228
00229
00230
00231
00232
00233
00234
00235
00236
00237
00238
00239
00240
00241
00242
00243
00244
00245
00246
00247 if( error.find( "gpg: encrypted with" ) != -1 )
00248 {
00249
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
00265 index2 = error.findRev('"', index) - 1;
00266 index = error.findRev(" \"", index2) + 7;
00267
00268
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
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
00283 #if 0
00284
00285
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
00305
00306
00307 if((index = error.find("Signature made")) != -1)
00308 {
00309
00310 status |= SIGNED;
00311
00312
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
00317
00318
00319
00320
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
00328
00329
00330
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
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
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
00357 status |= ERROR;
00358
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
00367
00368
00369 status |= UNKNOWN_SIG;
00370 status |= GOODSIG;
00371 block.setSignatureUserId( i18n("??? (file ~/.gnupg/pubring.gpg not found)") );
00372 }
00373 else
00374 {
00375 status |= ERROR;
00376 block.setSignatureUserId( TQString() );
00377 }
00378 }
00379
00380 block.setStatus( status );
00381 return status;
00382 }
00383
00384
00385 Key*
00386 BaseG::readPublicKey( const KeyID& keyID,
00387 const bool readTrust ,
00388 Key* key )
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
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
00427
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
00444 KeyList publicKeys = parseKeyList(output, false);
00445
00446
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
00459
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
00476 KeyList secretKeys = parseKeyList(output, true);
00477
00478
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 )
00528
00529
00530
00531
00532
00533
00534
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
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 {
00562
00563
00564
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;
00574 int pos2 = output.find( ':', pos );
00575 for( int field = 2; field <= 12; field++ )
00576 {
00577 switch( field )
00578 {
00579 case 2:
00580 if( pos2 > pos )
00581 {
00582 switch( output[pos] )
00583 {
00584 case 'o':
00585 break;
00586 case 'i':
00587 subkey->setInvalid( true );
00588 key->setInvalid( true );
00589 break;
00590 case 'd':
00591 subkey->setDisabled( true );
00592 key->setDisabled( true );
00593 break;
00594 case 'r':
00595 subkey->setRevoked( true );
00596 key->setRevoked( true );
00597 break;
00598 case 'e':
00599 subkey->setExpired( true );
00600 key->setExpired( true );
00601 break;
00602 case '-':
00603 case 'q':
00604 case 'n':
00605 case 'm':
00606 case 'f':
00607 case 'u':
00608
00609
00610 break;
00611 default:
00612 kdDebug(5100) << "Unknown trust value\n";
00613 }
00614 }
00615 break;
00616 case 3:
00617 if( pos2 > pos )
00618 subkey->setKeyLength( output.mid( pos, pos2-pos ).toUInt() );
00619 break;
00620 case 4:
00621 if( pos2 > pos )
00622 subkey->setKeyAlgorithm( output.mid( pos, pos2-pos ).toUInt() );
00623 break;
00624 case 5:
00625 keyID = output.mid( pos, pos2-pos );
00626 subkey->setKeyID( keyID );
00627 break;
00628 case 6:
00629 if( pos2 > pos )
00630 subkey->setCreationDate( output.mid( pos, pos2-pos ).toLong() );
00631 break;
00632 case 7:
00633 if( pos2 > pos )
00634 subkey->setExpirationDate( output.mid( pos, pos2-pos ).toLong() );
00635 else
00636 subkey->setExpirationDate( -1 );
00637 break;
00638 case 8:
00639 case 9:
00640 case 10:
00641 case 11:
00642 break;
00643 case 12:
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 {
00677
00678
00679 UserID *userID = new UserID( "" );
00680
00681 int pos = index + 4;
00682 int pos2 = output.find( ':', pos );
00683 for( int field=2; field <= 10; field++ )
00684 {
00685 switch( field )
00686 {
00687 case 2:
00688 if( pos2 > pos )
00689 {
00690 switch( output[pos] )
00691 {
00692 case 'i':
00693 userID->setInvalid( true );
00694 break;
00695 case 'r':
00696 userID->setRevoked( true );
00697 break;
00698 case '-':
00699 case 'q':
00700 userID->setValidity( KPGP_VALIDITY_UNDEFINED );
00701 break;
00702 case 'n':
00703 userID->setValidity( KPGP_VALIDITY_NEVER );
00704 break;
00705 case 'm':
00706 userID->setValidity( KPGP_VALIDITY_MARGINAL );
00707 break;
00708 case 'f':
00709 userID->setValidity( KPGP_VALIDITY_FULL );
00710 break;
00711 case 'u':
00712 userID->setValidity( KPGP_VALIDITY_ULTIMATE );
00713 break;
00714 default:
00715 kdDebug(5100) << "Unknown trust value\n";
00716 }
00717 }
00718 break;
00719 case 3:
00720 case 4:
00721 case 5:
00722 case 6:
00723 case 7:
00724 case 8:
00725 case 9:
00726 break;
00727 case 10:
00728 TQCString uid = output.mid( pos, pos2-pos );
00729
00730
00731
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
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
00744 isUtf8 = false;
00745 break;
00746 }
00747 }
00748 if( !isUtf8 ) {
00749
00750
00751 kdDebug(5100) << "User Id '" << uid
00752 << "' doesn't seem to be utf-8 encoded." << endl;
00753
00754
00755
00756 int nonAsciiCount = 0, asciiCount = 0;
00757
00758
00759
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
00773 kdDebug(5100) << "Assume koi8-r encoding." << endl;
00774 TQTextCodec *codec = TQTextCodec::codecForName("KOI8-R");
00775 uidString = codec->toUnicode( uid.data() );
00776
00777
00778
00779
00780
00781
00782
00783 if( ( uidString.length() >= 2 )
00784 && ( uidString[0].lower() == uidString[0] )
00785 && ( uidString[1].upper() == uidString[1] ) ) {
00786
00787
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
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
00808 key->addUserID( userID );
00809 }
00810 else if( !strncmp( output.data() + index, "fpr:", 4 ) )
00811 {
00812
00813
00814 if (key == 0)
00815 break;
00816
00817
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
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
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
00866
00867 return keys;
00868 }
00869
00870
00871 }