summaryrefslogtreecommitdiffstats
path: root/libkpgp
diff options
context:
space:
mode:
authortoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
committertoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
commit460c52653ab0dcca6f19a4f492ed2c5e4e963ab0 (patch)
tree67208f7c145782a7e90b123b982ca78d88cc2c87 /libkpgp
downloadtdepim-460c52653ab0dcca6f19a4f492ed2c5e4e963ab0.tar.gz
tdepim-460c52653ab0dcca6f19a4f492ed2c5e4e963ab0.zip
Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features.
BUG:215923 git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdepim@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'libkpgp')
-rw-r--r--libkpgp/AUTHORS.kpgp13
-rw-r--r--libkpgp/Makefile.am30
-rw-r--r--libkpgp/README3
-rwxr-xr-xlibkpgp/kpgp-3.1-upgrade-address-data.pl61
-rw-r--r--libkpgp/kpgp.cpp1811
-rw-r--r--libkpgp/kpgp.h469
-rw-r--r--libkpgp/kpgp.upd9
-rw-r--r--libkpgp/kpgpbase.cpp684
-rw-r--r--libkpgp/kpgpbase.h239
-rw-r--r--libkpgp/kpgpbase2.cpp1105
-rw-r--r--libkpgp/kpgpbase5.cpp828
-rw-r--r--libkpgp/kpgpbase6.cpp840
-rw-r--r--libkpgp/kpgpbaseG.cpp855
-rw-r--r--libkpgp/kpgpblock.cpp136
-rw-r--r--libkpgp/kpgpblock.h356
-rw-r--r--libkpgp/kpgpkey.cpp260
-rw-r--r--libkpgp/kpgpkey.h780
-rw-r--r--libkpgp/kpgpui.cpp1694
-rw-r--r--libkpgp/kpgpui.h343
-rw-r--r--libkpgp/pics/Makefile.am9
-rw-r--r--libkpgp/pics/key.pngbin0 -> 311 bytes
-rw-r--r--libkpgp/pics/key_bad.pngbin0 -> 339 bytes
-rw-r--r--libkpgp/pics/key_ok.pngbin0 -> 360 bytes
-rw-r--r--libkpgp/pics/key_unknown.pngbin0 -> 360 bytes
24 files changed, 10525 insertions, 0 deletions
diff --git a/libkpgp/AUTHORS.kpgp b/libkpgp/AUTHORS.kpgp
new file mode 100644
index 00000000..4fba20ca
--- /dev/null
+++ b/libkpgp/AUTHORS.kpgp
@@ -0,0 +1,13 @@
+Program: kpgp
+
+Lars Knoll <knoll@mpi-hd.mpg.de>
+ - Original version, PGP2 and PGP5 support
+
+"J. Nick Koston" <bdraco@the.system.is.halted.net>
+ - GnuPG support
+
+Andreas Gungl <a.gungl@gmx.de>
+ - PGP6 and other enhancements
+
+Ingo Klöcker <ingo.kloecker@epost.de>
+ - KpgpKey and other enhancements
diff --git a/libkpgp/Makefile.am b/libkpgp/Makefile.am
new file mode 100644
index 00000000..3b99ef8b
--- /dev/null
+++ b/libkpgp/Makefile.am
@@ -0,0 +1,30 @@
+INCLUDES = $(all_includes)
+SUBDIRS = pics
+
+lib_LTLIBRARIES = libkpgp.la
+
+libkpgp_la_SOURCES = \
+ kpgpblock.cpp \
+ kpgpui.cpp \
+ kpgpkey.cpp \
+ kpgp.cpp \
+ kpgpbase.cpp \
+ kpgpbaseG.cpp \
+ kpgpbase2.cpp \
+ kpgpbase5.cpp \
+ kpgpbase6.cpp
+
+libkpgp_la_LDFLAGS = $(all_libraries) -no-undefined -version-info 4:0:2
+
+libkpgp_la_LIBADD = $(LIB_KDEUI) $(LIB_POLL)
+
+METASOURCES = AUTO
+
+updatedir = $(kde_datadir)/kconf_update
+update_DATA = kpgp.upd
+update_SCRIPTS = kpgp-3.1-upgrade-address-data.pl
+
+messages:
+ $(XGETTEXT) *.cpp *.h -o $(podir)/libkpgp.pot
+
+include $(top_srcdir)/admin/Doxyfile.am
diff --git a/libkpgp/README b/libkpgp/README
new file mode 100644
index 00000000..4c62d0fb
--- /dev/null
+++ b/libkpgp/README
@@ -0,0 +1,3 @@
+pgp abstraction layer
+Maintainer: Ingo Klöcker <ingo.kloecker@epost.de>
+License: GPL
diff --git a/libkpgp/kpgp-3.1-upgrade-address-data.pl b/libkpgp/kpgp-3.1-upgrade-address-data.pl
new file mode 100755
index 00000000..46c41617
--- /dev/null
+++ b/libkpgp/kpgp-3.1-upgrade-address-data.pl
@@ -0,0 +1,61 @@
+#!/usr/bin/perl
+
+# Read all "AddressKeyEntry #" groups and the "EncryptionPreferences" group
+# and store the information together in new "Address #" groups
+
+my %data;
+
+$currentGroup = "";
+$address = "";
+
+while(<>) {
+ next if /^$/;
+ # filter out groups:
+ if( /^\[(.+)\]$/ ) {
+ $currentGroup = $1;
+ # delete the obsolete groups
+ if( ( $currentGroup =~ /^AddressKeyEntry \d*/ ) ||
+ ( $currentGroup eq "EncryptionPreferences" ) ) {
+ print "# DELETEGROUP [$currentGroup]\n";
+ }
+ next;
+ }
+ # store the values of the old groups and delete them
+ if( $currentGroup =~ /^AddressKeyEntry \d*/ ) {
+ if( /^Address=(.*)$/ ) {
+ $address = $1;
+ }
+ elsif( /^Key ID=(.*)$/ ) {
+ $data{$address}{"keyid"} = $1;
+ }
+ }
+ elsif( $currentGroup eq "EncryptionPreferences" ) {
+ ($address,$encrpref) = split /=/;
+ chomp $encrpref;
+ $data{$address}{"encrpref"} = $encrpref;
+ }
+}
+
+# write the new address groups
+$n = 1;
+foreach $address ( keys %data ) {
+ %addressData = %{$data{$address}};
+ print "[Address #$n]\n";
+ print "Address=$address\n";
+ if( exists $addressData{"encrpref"} ) {
+ $encrpref = $addressData{"encrpref"};
+ print "EncryptionPreference=$encrpref\n";
+ }
+ if( exists $addressData{"keyid"} ) {
+ $keyid = $addressData{"keyid"};
+ print "Key IDs=$keyid\n";
+ }
+ $n++;
+}
+$n--;
+
+# write the number of address groups
+print "# DELETE [General]addressKeyEntries\n";
+print "# DELETE [General]addressEntries\n";
+print "[General]\naddressEntries=$n\n";
+
diff --git a/libkpgp/kpgp.cpp b/libkpgp/kpgp.cpp
new file mode 100644
index 00000000..d478d199
--- /dev/null
+++ b/libkpgp/kpgp.cpp
@@ -0,0 +1,1811 @@
+/* -*- mode: C++; c-file-style: "gnu" -*-
+ kpgp.cpp
+
+ Copyright (C) 2001,2002 the KPGP authors
+ See file AUTHORS.kpgp for details
+
+ This file is part of KPGP, the KDE PGP/GnuPG support library.
+
+ KPGP is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <stdio.h>
+#include <time.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <stdarg.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <signal.h>
+
+#include <qlabel.h>
+#include <qcursor.h>
+#include <qapplication.h>
+
+#include <kdebug.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kconfigbase.h>
+#include <kconfig.h>
+#include <kstaticdeleter.h>
+
+#include "kpgpbase.h"
+#include "kpgpui.h"
+#include "kpgp.h"
+
+namespace Kpgp {
+
+Module *Module::kpgpObject = 0L;
+static KStaticDeleter<Module> kpgpod;
+
+Module::Module()
+ : mPublicKeys(),
+ mPublicKeysCached(false),
+ mSecretKeys(),
+ mSecretKeysCached(false),
+ passphrase(0), passphrase_buffer_len(0), havePassPhrase(false)
+{
+ if (!kpgpObject) {
+ kdDebug(5100) << "creating new pgp object" << endl;
+ }
+ kpgpObject=kpgpod.setObject(Module::kpgpObject, this);
+ pgp = 0;
+
+ config = new KConfig("kpgprc");
+
+ init();
+}
+
+Module::~Module()
+{
+ writeAddressData();
+
+ if (kpgpObject == this) kpgpObject = kpgpod.setObject( Module::kpgpObject, 0, false );
+ clear(TRUE);
+ delete config;
+ delete pgp;
+}
+
+// ----------------- public methods -------------------------
+
+void
+Module::init()
+{
+ wipePassPhrase();
+
+ // read kpgp config file entries
+ readConfig();
+
+ // read the email address -> { encryption keys, encryption preference }
+ // associations
+ readAddressData();
+
+ // do we have a pgp executable
+ checkForPGP();
+
+ // create the Base object later when it is
+ // needed to avoid the costly check done for
+ // the autodetection of PGP 2/6
+ //assignPGPBase();
+ delete pgp;
+ pgp=0;
+}
+
+
+void
+Module::readConfig()
+{
+ storePass = config->readBoolEntry("storePass", false);
+ showEncryptionResult = config->readBoolEntry("showEncryptionResult", true);
+ mShowKeyApprovalDlg = config->readBoolEntry( "showKeysForApproval", true );
+ // We have no config GUI for this key anymore, and the KPGP backend isn't ported,
+ // so let's just use Auto all the time. See #92619.
+ ///pgpType = (Module::PGPType) config->readNumEntry("pgpType", tAuto);
+ pgpType = tAuto;
+ flagEncryptToSelf = config->readBoolEntry("encryptToSelf", true);
+}
+
+void
+Module::writeConfig(bool sync)
+{
+ config->writeEntry("storePass", storePass);
+ config->writeEntry("showEncryptionResult", showEncryptionResult);
+ config->writeEntry( "showKeysForApproval", mShowKeyApprovalDlg );
+ //config->writeEntry("pgpType", (int) pgpType);
+ config->writeEntry("encryptToSelf", flagEncryptToSelf);
+
+ if(sync)
+ config->sync();
+
+ /// ### Why is the pgp object deleted? This is only necessary if the
+ /// PGP type was changed in the config dialog.
+ delete pgp;
+ pgp = 0;
+}
+
+
+void
+Module::setUser(const KeyID& keyID)
+{
+ if (pgpUser != keyID) {
+ pgpUser = keyID;
+ wipePassPhrase();
+ }
+}
+
+const KeyID
+Module::user(void) const
+{
+ return pgpUser;
+}
+
+
+void
+Module::setEncryptToSelf(bool flag)
+{
+ flagEncryptToSelf = flag;
+}
+
+bool
+Module::encryptToSelf(void) const
+{
+ return flagEncryptToSelf;
+}
+
+
+void
+Module::setStorePassPhrase(bool flag)
+{
+ storePass = flag;
+}
+
+bool
+Module::storePassPhrase(void) const
+{
+ return storePass;
+}
+
+int
+Module::prepare( bool needPassPhrase, Block* block )
+{
+ if (0 == pgp) assignPGPBase();
+
+ if(!havePgp)
+ {
+ errMsg = i18n("Could not find PGP executable.\n"
+ "Please check your PATH is set correctly.");
+ return 0;
+ }
+
+ if( block && ( block->status() & NO_SEC_KEY ) )
+ return 0;
+
+ if(needPassPhrase && !havePassPhrase) {
+ if( ( tGPG == pgpType ) && ( 0 != getenv("GPG_AGENT_INFO") ) ) {
+ // the user uses gpg-agent which asks itself for the passphrase
+ kdDebug(5100) << "user uses gpg-agent -> don't ask for passphrase\n";
+ // set dummy passphrase (because else signing doesn't work -> FIXME)
+ setPassPhrase( "dummy" );
+ }
+ else {
+ QString ID;
+ if( block )
+ ID = block->requiredUserId();
+ PassphraseDialog passdlg(0, i18n("OpenPGP Security Check"), true, ID);
+ QApplication::setOverrideCursor( QCursor(QCursor::ArrowCursor) );
+ int passdlgResult = passdlg.exec();
+ QApplication::restoreOverrideCursor();
+ if (passdlgResult == QDialog::Accepted) {
+ if (!setPassPhrase(passdlg.passphrase())) {
+ if (strlen(passdlg.passphrase()) >= 1024)
+ errMsg = i18n("Passphrase is too long, it must contain fewer than 1024 characters.");
+ else
+ errMsg = i18n("Out of memory.");
+ return 0;
+ }
+ } else {
+ wipePassPhrase();
+ return -1;
+ }
+ }
+ }
+ return 1;
+}
+
+void
+Module::wipePassPhrase(bool freeMem)
+{
+ if ( passphrase ) {
+ if ( passphrase_buffer_len )
+ memset( passphrase, 0x00, passphrase_buffer_len );
+ else {
+ kdDebug(5100) << "wipePassPhrase: passphrase && !passphrase_buffer_len ???" << endl;
+ passphrase = 0;
+ }
+ }
+ if ( freeMem && passphrase ) {
+ free( passphrase );
+ passphrase = 0;
+ passphrase_buffer_len = 0;
+ }
+ havePassPhrase = false;
+}
+
+bool
+Module::verify( Block& block )
+{
+ int retval;
+
+ if (0 == pgp) assignPGPBase();
+
+ // everything ready
+ if( !prepare( false, &block ) )
+ return false;
+ // ok now try to verify the message.
+ retval = pgp->verify( block );
+
+ if(retval & ERROR)
+ {
+ errMsg = pgp->lastErrorMessage();
+ return false;
+ }
+ return true;
+}
+
+bool
+Module::decrypt( Block& block )
+{
+ int retval;
+
+ if (0 == pgp) assignPGPBase();
+
+ do {
+ // loop as long as the user enters a wrong passphrase and doesn't abort
+ // everything ready
+ if( prepare( true, &block ) != 1 )
+ return FALSE;
+ // ok now try to decrypt the message.
+ retval = pgp->decrypt( block, passphrase );
+ // loop on bad passphrase
+ if( retval & BADPHRASE ) {
+ wipePassPhrase();
+ QApplication::setOverrideCursor( QCursor(QCursor::ArrowCursor) );
+ int ret = KMessageBox::warningContinueCancel(0,
+ i18n("You just entered an invalid passphrase.\n"
+ "Do you want to try again, or "
+ "cancel and view the message undecrypted?"),
+ i18n("PGP Warning"), i18n("&Retry"));
+ QApplication::restoreOverrideCursor();
+ if ( ret == KMessageBox::Cancel ) break;
+ } else
+ break;
+ } while ( true );
+
+ // erase the passphrase if we do not want to keep it
+ cleanupPass();
+
+ if(retval & ERROR)
+ {
+ errMsg = pgp->lastErrorMessage();
+ return false;
+ }
+ return true;
+}
+
+Kpgp::Result
+Module::clearsign( Block& block,
+ const KeyID& keyId, const QCString& charset )
+{
+ return encrypt( block, QStringList(), keyId, true, charset );
+}
+
+Kpgp::Result
+Module::encrypt( Block& block,
+ const QStringList& receivers, const KeyID& keyId,
+ bool sign, const QCString& charset )
+{
+ KeyIDList encryptionKeyIds; // list of keys which are used for encryption
+ int status = 0;
+ errMsg = "";
+
+ if( 0 == pgp ) assignPGPBase();
+
+ setUser( keyId );
+
+ if( !receivers.empty() ) {
+ Kpgp::Result result = getEncryptionKeys( encryptionKeyIds, receivers,
+ keyId );
+ if( Kpgp::Ok != result ) {
+ return result;
+ }
+ }
+
+ status = doEncSign( block, encryptionKeyIds, sign );
+
+ if( status & CANCEL )
+ return Kpgp::Canceled;
+
+ // check for bad passphrase
+ while( status & BADPHRASE ) {
+ wipePassPhrase();
+ QString str = i18n("You entered an invalid passphrase.\n"
+ "Do you want to try again, continue and leave the "
+ "message unsigned, or cancel sending the message?");
+ QApplication::setOverrideCursor( QCursor(QCursor::ArrowCursor) );
+ int ret = KMessageBox::warningYesNoCancel( 0, str,
+ i18n("PGP Warning"),
+ i18n("&Retry"),
+ i18n("Send &Unsigned") );
+ QApplication::restoreOverrideCursor();
+ if( ret == KMessageBox::Cancel ) {
+ return Kpgp::Canceled;
+ }
+ if( ret == KMessageBox::No ) {
+ // the user selected "Send unsigned"
+ if( encryptionKeyIds.isEmpty() ) {
+ block.reset();
+ return Kpgp::Ok;
+ }
+ else {
+ sign = false;
+ }
+ }
+ // ok let's try once again...
+ status = doEncSign( block, encryptionKeyIds, sign );
+ }
+
+ // did signing fail?
+ if( status & ERR_SIGNING ) {
+ QString str = i18n("%1 = 'signing failed' error message",
+ "%1\nDo you want to send the message unsigned, "
+ "or cancel sending the message?")
+ .arg( pgp->lastErrorMessage() );
+ QApplication::setOverrideCursor( QCursor(QCursor::ArrowCursor) );
+ int ret = KMessageBox::warningContinueCancel( 0, str,
+ i18n("PGP Warning"),
+ i18n("Send &Unsigned") );
+ QApplication::restoreOverrideCursor();
+ if( ret == KMessageBox::Cancel ) {
+ return Kpgp::Canceled;
+ }
+ sign = false;
+ status = doEncSign( block, encryptionKeyIds, sign );
+ }
+
+ // check for bad keys
+ if( status & BADKEYS ) {
+ QString str = i18n("%1 = 'bad keys' error message",
+ "%1\nDo you want to encrypt anyway, leave the "
+ "message as-is, or cancel sending the message?")
+ .arg( pgp->lastErrorMessage() );
+
+ QApplication::setOverrideCursor( QCursor(QCursor::ArrowCursor) );
+ int ret = KMessageBox::warningYesNoCancel( 0, str,
+ i18n("PGP Warning"),
+ i18n("Send &Encrypted"),
+ i18n("Send &Unencrypted") );
+ QApplication::restoreOverrideCursor();
+ if( ret == KMessageBox::Cancel ) {
+ return Kpgp::Canceled;
+ }
+ if( ret == KMessageBox::No ) {
+ // the user selected "Send unencrypted"
+ if( sign ) {
+ doEncSign( block, KeyIDList(), sign );
+ }
+ else {
+ block.reset();
+ }
+ return Kpgp::Ok;
+ }
+ }
+
+ if( status & MISSINGKEY ) {
+ QString str = i18n("%1 = 'missing keys' error message",
+ "%1\nDo you want to leave the message as-is, "
+ "or cancel sending the message?")
+ .arg( pgp->lastErrorMessage() );
+ QApplication::setOverrideCursor( QCursor(QCursor::ArrowCursor) );
+ int ret = KMessageBox::warningContinueCancel( 0, str,
+ i18n("PGP Warning"),
+ i18n("&Send As-Is") );
+ QApplication::restoreOverrideCursor();
+ if( ret == KMessageBox::Cancel ) {
+ return Kpgp::Canceled;
+ }
+ block.reset();
+ return Kpgp::Ok;
+ }
+
+ if( status & ERROR ) {
+ // show error dialog
+ errMsg = i18n( "The following error occurred:\n%1" )
+ .arg( pgp->lastErrorMessage() );
+ QString details = i18n( "This is the error message of %1:\n%2" )
+ .arg( ( pgpType == tGPG ) ? "GnuPG" : "PGP" )
+ .arg( block.error().data() );
+ QApplication::setOverrideCursor( QCursor(QCursor::ArrowCursor) );
+ KMessageBox::detailedSorry( 0, errMsg, details );
+ QApplication::restoreOverrideCursor();
+ return Kpgp::Failure;
+ }
+
+ if( showCipherText() ) {
+ // show cipher text dialog
+ CipherTextDialog *cipherTextDlg = new CipherTextDialog( block.text(), charset );
+ QApplication::setOverrideCursor( QCursor(QCursor::ArrowCursor) );
+ bool result = ( cipherTextDlg->exec() == QDialog::Accepted );
+ QApplication::restoreOverrideCursor();
+ delete cipherTextDlg;
+ return result == QDialog::Accepted ? Kpgp::Ok : Kpgp::Canceled;
+ }
+ return Kpgp::Ok;
+}
+
+int
+Module::doEncSign( Block& block,
+ const KeyIDList& recipientKeyIds, bool sign )
+{
+ int retval = 0;
+
+ if( 0 == pgp ) assignPGPBase();
+
+ // to avoid error messages in case pgp is not installed
+ if( !havePgp ) return OK;
+
+ if( sign ) {
+ int result = prepare( true, &block );
+ switch( result ) {
+ case -1:
+ return CANCEL;
+ case 0:
+ return ERROR;
+ }
+ retval = pgp->encsign( block, recipientKeyIds, passphrase );
+ }
+ else {
+ if( !prepare( false, &block ) ) return ERROR;
+ retval = pgp->encrypt( block, recipientKeyIds );
+ }
+ // erase the passphrase if we do not want to keep it
+ cleanupPass();
+
+ return retval;
+}
+
+Kpgp::Result
+Module::getEncryptionKeys( KeyIDList& encryptionKeyIds,
+ const QStringList& recipients,
+ const KeyID& keyId )
+{
+ if( recipients.empty() ) {
+ encryptionKeyIds.clear();
+ return Kpgp::Ok;
+ }
+
+ // list of lists of encryption keys (one list per recipient + one list
+ // for the sender)
+ QValueVector<KeyIDList> recipientKeyIds( recipients.count() + 1 );
+ // add the sender's encryption key(s) to the list of recipient key IDs
+ if( encryptToSelf() ) {
+ recipientKeyIds[0] = KeyIDList( keyId );
+ }
+ else {
+ recipientKeyIds[0] = KeyIDList();
+ }
+ bool showKeysForApproval = false;
+ int i = 1;
+ for( QStringList::ConstIterator it = recipients.begin();
+ it != recipients.end(); ++it, ++i ) {
+ EncryptPref encrPref = encryptionPreference( *it );
+ if( ( encrPref == UnknownEncryptPref ) || ( encrPref == NeverEncrypt ) )
+ showKeysForApproval = true;
+
+ KeyIDList keyIds = getEncryptionKeys( *it );
+ if( keyIds.isEmpty() ) {
+ showKeysForApproval = true;
+ }
+ recipientKeyIds[i] = keyIds;
+ }
+
+ kdDebug(5100) << "recipientKeyIds = (\n";
+ QValueVector<KeyIDList>::const_iterator kit;
+ for( kit = recipientKeyIds.begin(); kit != recipientKeyIds.end(); ++kit ) {
+ kdDebug(5100) << "( 0x" << (*kit).toStringList().join( ", 0x" )
+ << " ),\n";
+ }
+ kdDebug(5100) << ")\n";
+
+ if( showKeysForApproval || mShowKeyApprovalDlg ) {
+ // #### FIXME: Until we support encryption with untrusted keys only
+ // #### trusted keys are allowed
+ unsigned int allowedKeys = PublicKeys | EncryptionKeys | ValidKeys | TrustedKeys;
+#if 0
+ // ### reenable this code when we support encryption with untrusted keys
+ if( pgpType != tGPG ) {
+ // usage of untrusted keys is only possible with GnuPG
+ allowedKeys |= TrustedKeys;
+ }
+#endif
+ // show the recipients <-> key relation
+ KeyApprovalDialog dlg( recipients, recipientKeyIds, allowedKeys );
+
+ QApplication::setOverrideCursor( QCursor(QCursor::ArrowCursor) );
+ int ret = dlg.exec();
+
+ if( ret == QDialog::Rejected ) {
+ QApplication::restoreOverrideCursor();
+ return Kpgp::Canceled;
+ }
+
+ recipientKeyIds = dlg.keys();
+ QApplication::restoreOverrideCursor();
+ }
+
+ // flatten the list of lists of key IDs and count empty key ID lists
+ unsigned int emptyListCount = 0;
+ for( QValueVector<KeyIDList>::const_iterator it = recipientKeyIds.begin();
+ it != recipientKeyIds.end(); ++it ) {
+ if( (*it).isEmpty() ) {
+ // only count empty key ID lists for the recipients
+ if( it != recipientKeyIds.begin() ) {
+ emptyListCount++;
+ }
+ }
+ else {
+ for( KeyIDList::ConstIterator kit = (*it).begin();
+ kit != (*it).end(); kit++ ) {
+ encryptionKeyIds.append( *kit );
+ }
+ }
+ }
+
+ // FIXME-AFTER-KDE-3.1: Show warning if message won't be encrypted to self
+
+ // show a warning if the user didn't select an encryption key for
+ // some of the recipients
+ if( recipientKeyIds.size() == emptyListCount + 1 ) { // (+1 because of the sender's key)
+ QString str = ( recipients.count() == 1 )
+ ? i18n("You did not select an encryption key for the "
+ "recipient of this message; therefore, the message "
+ "will not be encrypted.")
+ : i18n("You did not select an encryption key for any of the "
+ "recipients of this message; therefore, the message "
+ "will not be encrypted.");
+ QApplication::setOverrideCursor( QCursor(QCursor::ArrowCursor) );
+ int ret = KMessageBox::warningContinueCancel( 0, str,
+ i18n("PGP Warning"),
+ i18n("Send &Unencrypted") );
+ QApplication::restoreOverrideCursor();
+ if( ret == KMessageBox::Cancel ) {
+ return Kpgp::Canceled;
+ }
+ else
+ encryptionKeyIds.clear();
+ }
+ else if( emptyListCount > 0 ) {
+ QString str = ( emptyListCount == 1 )
+ ? i18n("You did not select an encryption key for one of "
+ "the recipients; this person will not be able to "
+ "decrypt the message if you encrypt it.")
+ : i18n("You did not select encryption keys for some of "
+ "the recipients; these persons will not be able to "
+ "decrypt the message if you encrypt it." );
+ QApplication::setOverrideCursor( QCursor(QCursor::ArrowCursor) );
+ int ret = KMessageBox::warningYesNoCancel( 0, str,
+ i18n("PGP Warning"),
+ i18n("Send &Encrypted"),
+ i18n("Send &Unencrypted") );
+ QApplication::restoreOverrideCursor();
+ if( ret == KMessageBox::Cancel ) {
+ return Kpgp::Canceled;
+ }
+ else if( ret == KMessageBox::No ) {
+ // the user selected "Send unencrypted"
+ encryptionKeyIds.clear();
+ }
+ }
+
+ return Kpgp::Ok;
+}
+
+int
+Module::encryptionPossible( const QStringList& recipients )
+{
+ if( 0 == pgp ) assignPGPBase();
+
+ if( !usePGP() )
+ return 0;
+
+ if( recipients.empty() )
+ return 0;
+
+ int noKey = 0, never = 0, unknown = 0, always = 0, aip = 0, ask = 0,
+ askwp = 0;
+ for( QStringList::ConstIterator it = recipients.begin();
+ it != recipients.end(); ++it) {
+ if( haveTrustedEncryptionKey( *it ) ) {
+ EncryptPref encrPref = encryptionPreference( *it );
+ switch( encrPref ) {
+ case NeverEncrypt:
+ never++;
+ break;
+ case UnknownEncryptPref:
+ unknown++;
+ break;
+ case AlwaysEncrypt:
+ always++;
+ break;
+ case AlwaysEncryptIfPossible:
+ aip++;
+ break;
+ case AlwaysAskForEncryption:
+ ask++;
+ break;
+ case AskWheneverPossible:
+ askwp++;
+ break;
+ }
+ }
+ else {
+ noKey++;
+ }
+ }
+
+ if( ( always+aip > 0 ) && ( never+unknown+ask+askwp+noKey == 0 ) ) {
+ return 1; // encryption possible and desired
+ }
+
+ if( ( unknown+ask+askwp > 0 ) && ( never+noKey == 0 ) ) {
+ return 2; // encryption possible, but user has to be asked
+ }
+
+ if( ( never+noKey > 0 ) && ( always+ask == 0 ) ) {
+ return 0; // encryption isn't possible or desired
+ }
+
+ return -1; // we can't decide it automatically
+}
+
+bool
+Module::signKey(const KeyID& keyId)
+{
+ if (0 == pgp) assignPGPBase();
+
+ if( prepare( true ) != 1 )
+ return FALSE;
+ if(pgp->signKey(keyId, passphrase) & ERROR)
+ {
+ errMsg = pgp->lastErrorMessage();
+ return false;
+ }
+ return true;
+}
+
+
+const KeyList
+Module::publicKeys()
+{
+ if (0 == pgp) assignPGPBase();
+
+ if (!prepare()) return KeyList();
+
+ if( !mPublicKeysCached ) {
+ readPublicKeys();
+ }
+
+ return mPublicKeys;
+}
+
+
+const KeyList
+Module::secretKeys()
+{
+ if (0 == pgp) assignPGPBase();
+
+ if (!prepare()) return KeyList();
+
+ if( !mSecretKeysCached ) {
+ readSecretKeys();
+ }
+
+ return mSecretKeys;
+}
+
+
+Key*
+Module::publicKey(const KeyID& keyID)
+{
+ readPublicKeys();
+
+ for( KeyListIterator it( mPublicKeys ); (*it); ++it )
+ if( keyID == (*it)->primaryKeyID() ||
+ keyID == (*it)->primaryFingerprint() )
+ return (*it);
+
+ return 0;
+}
+
+Key*
+Module::publicKey( const QString& userID )
+{
+ readPublicKeys();
+
+ for( KeyListIterator it( mPublicKeys ); (*it); ++it )
+ if( (*it)->matchesUserID( userID ) )
+ return (*it);
+
+ return 0;
+}
+
+Key*
+Module::secretKey(const KeyID& keyID)
+{
+ readSecretKeys();
+
+ for( KeyListIterator it( mSecretKeys ); (*it); ++it )
+ if( keyID == (*it)->primaryKeyID() ||
+ keyID == (*it)->primaryFingerprint() )
+ return (*it);
+
+ return 0;
+}
+
+Validity
+Module::keyTrust( const KeyID& keyID )
+{
+ Key *key = publicKey( keyID );
+
+ if( ( 0 == key ) || ( key->keyTrust() == KPGP_VALIDITY_UNKNOWN ) )
+ { // (re)check the key if it's unknown or if its trust is unknown
+ key = rereadKey( keyID, true );
+ if( key == 0 )
+ return KPGP_VALIDITY_UNKNOWN;
+ }
+
+ return key->keyTrust();
+}
+
+Validity
+Module::keyTrust( const QString& userID )
+{
+ Key *key = publicKey( userID );
+
+ if( key == 0 )
+ return KPGP_VALIDITY_UNKNOWN;
+
+ if( key->keyTrust() == KPGP_VALIDITY_UNKNOWN )
+ {
+ key = rereadKey( key->primaryKeyID(), true );
+ if( key == 0 )
+ return KPGP_VALIDITY_UNKNOWN;
+ }
+
+ return key->keyTrust();
+}
+
+bool
+Module::isTrusted( const KeyID& keyID )
+{
+ return ( keyTrust( keyID ) >= KPGP_VALIDITY_MARGINAL );
+}
+
+Key*
+Module::rereadKey( const KeyID& keyID, const bool readTrust /* = true */ )
+{
+ if( 0 == pgp ) assignPGPBase();
+
+ // search the old key data in the key list
+ Key* oldKey = publicKey( keyID );
+
+ Key* newKey = pgp->readPublicKey( keyID, readTrust, oldKey );
+
+ if( ( 0 == oldKey ) && ( 0 != newKey ) )
+ {
+ mPublicKeys.inSort( newKey );
+ kdDebug(5100) << "New public key 0x" << newKey->primaryKeyID() << " ("
+ << newKey->primaryUserID() << ").\n";
+ }
+ else if( ( 0 != oldKey ) && ( 0 == newKey ) )
+ { // the key has been deleted in the meantime
+ kdDebug(5100) << "Public key 0x" << oldKey->primaryKeyID() << " ("
+ << oldKey->primaryUserID() << ") will be removed.\n";
+ mPublicKeys.removeRef( oldKey );
+ }
+
+ return newKey;
+}
+
+QCString
+Module::getAsciiPublicKey(const KeyID& keyID)
+{
+ if (0 == pgp) assignPGPBase();
+
+ return pgp->getAsciiPublicKey(keyID);
+}
+
+
+bool Module::setPassPhrase(const char * aPass)
+{
+ // null out old buffer before we touch the new string. So in case
+ // aPass isn't properly null-terminated, we don't leak secret data.
+ wipePassPhrase();
+
+ if (aPass)
+ {
+ size_t newlen = strlen( aPass );
+ if ( newlen >= 1024 ) {
+ // rediculously long passphrase.
+ // Maybe someone wants to trick us in malloc()'ing
+ // huge buffers...
+ return false;
+ }
+ if ( passphrase_buffer_len < newlen + 1 ) {
+ // too little space in current buffer:
+ // allocate a larger one.
+ if ( passphrase )
+ free( passphrase );
+ passphrase_buffer_len = (newlen + 1 + 15) & ~0xF; // make it a multiple of 16.
+ passphrase = (char*)malloc( passphrase_buffer_len );
+ if (!passphrase) {
+ passphrase_buffer_len = 0;
+ return false;
+ }
+ }
+ memcpy( passphrase, aPass, newlen + 1 );
+ havePassPhrase = true;
+ }
+ return true;
+}
+
+bool
+Module::changePassPhrase()
+{
+ //FIXME...
+ KMessageBox::information(0,i18n("This feature is\nstill missing"));
+ return FALSE;
+}
+
+void
+Module::clear(const bool erasePassPhrase)
+{
+ if(erasePassPhrase)
+ wipePassPhrase(true);
+}
+
+const QString
+Module::lastErrorMsg(void) const
+{
+ return errMsg;
+}
+
+bool
+Module::havePGP(void) const
+{
+ return havePgp;
+}
+
+void
+Module::setShowCipherText(const bool flag)
+{
+ showEncryptionResult = flag;
+}
+
+bool
+Module::showCipherText(void) const
+{
+ return showEncryptionResult;
+}
+
+KeyID
+Module::selectSecretKey( const QString& title,
+ const QString& text,
+ const KeyID& keyId )
+{
+ if( 0 == pgp ) {
+ assignPGPBase();
+ }
+
+ if( usePGP() ) {
+ return selectKey( secretKeys(), title, text, keyId, SecretKeys );
+ }
+ else {
+ KMessageBox::sorry( 0, i18n("You either do not have GnuPG/PGP installed "
+ "or you chose not to use GnuPG/PGP.") );
+ return KeyID();
+ }
+}
+
+KeyID
+Module::selectPublicKey( const QString& title,
+ const QString& text /* = QString::null */,
+ const KeyID& oldKeyId /* = KeyID() */,
+ const QString& address /* = QString::null */,
+ const unsigned int allowedKeys /* = AllKeys */ )
+{
+ if( 0 == pgp ) {
+ assignPGPBase();
+ }
+
+ if( usePGP() ) {
+ KeyID keyId;
+
+ if( address.isEmpty() ) {
+ keyId = selectKey( publicKeys(), title, text, oldKeyId, allowedKeys );
+ }
+ else {
+ bool rememberChoice;
+ keyId = selectKey( rememberChoice, publicKeys(), title, text, oldKeyId,
+ allowedKeys );
+ if( !keyId.isEmpty() && rememberChoice ) {
+ setKeysForAddress( address, KeyIDList( keyId ) );
+ }
+ }
+
+ return keyId;
+ }
+ else {
+ KMessageBox::sorry( 0, i18n("You either do not have GnuPG/PGP installed "
+ "or you chose not to use GnuPG/PGP.") );
+ return KeyID();
+ }
+}
+
+
+KeyIDList
+Module::selectPublicKeys( const QString& title,
+ const QString& text /* = QString::null */,
+ const KeyIDList& oldKeyIds /* = KeyIDList() */,
+ const QString& address /* = QString::null */,
+ const unsigned int allowedKeys /* = AllKeys */ )
+{
+ if( 0 == pgp ) {
+ assignPGPBase();
+ }
+
+ if( usePGP() ) {
+ KeyIDList keyIds;
+
+ if( address.isEmpty() ) {
+ keyIds = selectKeys( publicKeys(), title, text, oldKeyIds, allowedKeys );
+ }
+ else {
+ bool rememberChoice;
+ keyIds = selectKeys( rememberChoice, publicKeys(), title, text,
+ oldKeyIds, allowedKeys );
+ if( !keyIds.isEmpty() && rememberChoice ) {
+ setKeysForAddress( address, keyIds );
+ }
+ }
+
+ return keyIds;
+ }
+ else {
+ KMessageBox::sorry( 0, i18n("You either do not have GnuPG/PGP installed "
+ "or you chose not to use GnuPG/PGP.") );
+ return KeyIDList();
+ }
+}
+
+
+// -- static member functions ----------------------------------------------
+
+Module *
+Module::getKpgp()
+{
+ if (!kpgpObject)
+ {
+ kdError(5100) << "there is no instance of kpgp available" << endl;
+ }
+ return kpgpObject;
+}
+
+
+KConfig *
+Module::getConfig()
+{
+ return getKpgp()->config;
+}
+
+
+bool
+Module::prepareMessageForDecryption( const QCString& msg,
+ QPtrList<Block>& pgpBlocks,
+ QStrList& nonPgpBlocks )
+{
+ BlockType pgpBlock = NoPgpBlock;
+ int start = -1; // start of the current PGP block
+ int lastEnd = -1; // end of the last PGP block
+
+ pgpBlocks.setAutoDelete( true );
+ pgpBlocks.clear();
+ nonPgpBlocks.setAutoDelete( true );
+ nonPgpBlocks.clear();
+
+ if( msg.isEmpty() )
+ {
+ nonPgpBlocks.append( "" );
+ return false;
+ }
+
+ if( !strncmp( msg.data(), "-----BEGIN PGP ", 15 ) )
+ start = 0;
+ else
+ {
+ start = msg.find( "\n-----BEGIN PGP" ) + 1;
+ if( start == 0 )
+ {
+ nonPgpBlocks.append( msg );
+ return false; // message doesn't contain an OpenPGP block
+ }
+ }
+
+ while( start != -1 )
+ {
+ int nextEnd, nextStart;
+
+ // is the PGP block a clearsigned block?
+ if( !strncmp( msg.data() + start + 15, "SIGNED", 6 ) )
+ pgpBlock = ClearsignedBlock;
+ else
+ pgpBlock = UnknownBlock;
+
+ nextEnd = msg.find( "\n-----END PGP", start + 15 );
+ if( nextEnd == -1 )
+ {
+ nonPgpBlocks.append( msg.mid( lastEnd+1 ) );
+ break;
+ }
+ nextStart = msg.find( "\n-----BEGIN PGP", start + 15 );
+
+ if( ( nextStart == -1 ) || ( nextEnd < nextStart ) ||
+ ( pgpBlock == ClearsignedBlock ) )
+ { // most likely we found a PGP block (but we don't check if it's valid)
+ // store the preceding non-PGP block
+ nonPgpBlocks.append( msg.mid( lastEnd+1, start-lastEnd-1 ) );
+ lastEnd = msg.find( "\n", nextEnd + 14 );
+ if( lastEnd == -1 )
+ {
+ pgpBlocks.append( new Block( msg.mid( start ) ) );
+ nonPgpBlocks.append( "" );
+ break;
+ }
+ else
+ {
+ pgpBlocks.append( new Block( msg.mid( start, lastEnd+1-start ) ) );
+ if( ( nextStart != -1 ) && ( nextEnd > nextStart ) )
+ nextStart = msg.find( "\n-----BEGIN PGP", lastEnd+1 );
+ }
+ }
+
+ start = nextStart;
+ if( start == -1 )
+ nonPgpBlocks.append( msg.mid( lastEnd+1 ) );
+ else
+ start++; // move start behind the '\n'
+ }
+
+ return ( !pgpBlocks.isEmpty() );
+}
+
+
+// --------------------- private functions -------------------
+
+bool
+Module::haveTrustedEncryptionKey( const QString& person )
+{
+ if( 0 == pgp ) assignPGPBase();
+
+ if( !usePGP() ) return false;
+
+ readPublicKeys();
+
+ QString address = canonicalAddress( person ).lower();
+
+ // First look for this person's address in the address data dictionary
+ KeyIDList keyIds = keysForAddress( address );
+ if( !keyIds.isEmpty() ) {
+ // Check if at least one of the keys is a trusted and valid encryption key
+ for( KeyIDList::ConstIterator it = keyIds.begin();
+ it != keyIds.end(); ++it ) {
+ keyTrust( *it ); // this is called to make sure that the trust info
+ // for this key is read
+ Key *key = publicKey( *it );
+ if( key && ( key->isValidEncryptionKey() ) &&
+ ( key->keyTrust() >= KPGP_VALIDITY_MARGINAL ) )
+ return true;
+ }
+ }
+
+ // Now search the public keys for matching keys
+ KeyListIterator it( mPublicKeys );
+
+ // search a key which matches the complete address
+ for( it.toFirst(); (*it); ++it ) {
+ // search case insensitively in the list of userIDs of this key
+ if( (*it)->matchesUserID( person, false ) ) {
+ keyTrust( (*it)->primaryKeyID() ); // this is called to make sure that
+ // the trust info for this key is read
+ if( ( (*it)->isValidEncryptionKey() ) &&
+ ( (*it)->keyTrust() >= KPGP_VALIDITY_MARGINAL ) ) {
+ return true;
+ }
+ }
+ }
+
+ // if no key matches the complete address look for a key which matches
+ // the canonical mail address
+ for( it.toFirst(); (*it); ++it ) {
+ // search case insensitively in the list of userIDs of this key
+ if( (*it)->matchesUserID( address, false ) ) {
+ keyTrust( (*it)->primaryKeyID() ); // this is called to make sure that
+ // the trust info for this key is read
+ if( ( (*it)->isValidEncryptionKey() ) &&
+ ( (*it)->keyTrust() >= KPGP_VALIDITY_MARGINAL ) ) {
+ return true;
+ }
+ }
+ }
+
+ // no trusted encryption key was found for the given person
+ return false;
+}
+
+KeyIDList
+Module::getEncryptionKeys( const QString& person )
+{
+ if( 0 == pgp ) assignPGPBase();
+
+ if( !usePGP() ) return KeyIDList();
+
+ readPublicKeys();
+
+ QString address = canonicalAddress( person ).lower();
+
+ // #### FIXME: Until we support encryption with untrusted keys only
+ // #### trusted keys are allowed
+ unsigned int allowedKeys = PublicKeys | EncryptionKeys | ValidKeys | TrustedKeys;
+#if 0
+ // ### reenable this code when we support encryption with untrusted keys
+ if( pgpType != tGPG ) {
+ // usage of untrusted keys is only possible with GnuPG
+ allowedKeys |= TrustedKeys;
+ }
+#endif
+
+ // First look for this person's address in the address->key dictionary
+ KeyIDList keyIds = keysForAddress( address );
+ if( !keyIds.isEmpty() ) {
+ kdDebug(5100) << "Using encryption keys 0x"
+ << keyIds.toStringList().join( ", 0x" )
+ << " for " << person << endl;
+ // Check if all of the keys are a trusted and valid encryption keys
+ bool keysOk = true;
+ for( KeyIDList::ConstIterator it = keyIds.begin();
+ it != keyIds.end(); ++it ) {
+ keyTrust( *it ); // this is called to make sure that the trust info
+ // for this key is read
+ Key *key = publicKey( *it );
+ if( !( key && ( key->isValidEncryptionKey() ) &&
+ ( key->keyTrust() >= KPGP_VALIDITY_MARGINAL ) ) )
+ keysOk = false;
+ }
+ if( keysOk ) {
+ return keyIds;
+ }
+ else {
+ bool rememberChoice;
+ keyIds = selectKeys( rememberChoice, mPublicKeys,
+ i18n("Encryption Key Selection"),
+ i18n("if in your language something like "
+ "'key(s)' isn't possible please "
+ "use the plural in the translation",
+ "There is a problem with the "
+ "encryption key(s) for \"%1\".\n\n"
+ "Please re-select the key(s) which should "
+ "be used for this recipient."
+ ).arg(person),
+ keyIds,
+ allowedKeys );
+ if( !keyIds.isEmpty() ) {
+ if( rememberChoice ) {
+ setKeysForAddress( person, keyIds );
+ }
+ return keyIds;
+ }
+ }
+ }
+
+ // Now search all public keys for matching keys
+ KeyListIterator it( mPublicKeys );
+ KeyList matchingKeys;
+
+ // search all keys which match the complete address
+ kdDebug(5100) << "Looking for keys matching " << person << " ...\n";
+ for( it.toFirst(); (*it); ++it ) {
+ // search case insensitively in the list of userIDs of this key
+ if( (*it)->matchesUserID( person, false ) ) {
+ keyTrust( (*it)->primaryKeyID() ); // this is called to make sure that
+ // the trust info for this key is read
+ if( ( (*it)->isValidEncryptionKey() ) &&
+ ( (*it)->keyTrust() >= KPGP_VALIDITY_MARGINAL ) ) {
+ kdDebug(5100) << "Matching trusted key found: "
+ << (*it)->primaryKeyID() << endl;
+ matchingKeys.append( *it );
+ }
+ }
+ }
+
+ // if no keys match the complete address look for keys which match
+ // the canonical mail address
+ kdDebug(5100) << "Looking for keys matching " << address << " ...\n";
+ if( matchingKeys.isEmpty() ) {
+ for ( it.toFirst(); (*it); ++it ) {
+ // search case insensitively in the list of userIDs of this key
+ if( (*it)->matchesUserID( address, false ) ) {
+ keyTrust( (*it)->primaryKeyID() ); // this is called to make sure that
+ // the trust info for this key is read
+ if( ( (*it)->isValidEncryptionKey() ) &&
+ ( (*it)->keyTrust() >= KPGP_VALIDITY_MARGINAL ) ) {
+ kdDebug(5100) << "Matching trusted key found: "
+ << (*it)->primaryKeyID() << endl;
+ matchingKeys.append( *it );
+ }
+ }
+ }
+ }
+
+ // no match until now, let the user choose the key
+ if( matchingKeys.isEmpty() ) {
+ // FIXME: let user get the key from keyserver
+ bool rememberChoice;
+ KeyIDList keyIds = selectKeys( rememberChoice, mPublicKeys,
+ i18n("Encryption Key Selection"),
+ i18n("if in your language something like "
+ "'key(s)' isn't possible please "
+ "use the plural in the translation",
+ "No valid and trusted OpenPGP key was "
+ "found for \"%1\".\n\n"
+ "Select the key(s) which should "
+ "be used for this recipient."
+ ).arg(person),
+ KeyIDList(),
+ allowedKeys );
+ if( !keyIds.isEmpty() ) {
+ if( rememberChoice ) {
+ setKeysForAddress( person, keyIds );
+ }
+ return keyIds;
+ }
+ }
+ // only one key matches
+ else if( matchingKeys.count() == 1 ) {
+ return KeyIDList( matchingKeys.getFirst()->primaryKeyID() );
+ }
+ // more than one key matches; let the user choose the key(s)
+ else {
+ bool rememberChoice;
+ KeyIDList keyIds = selectKeys( rememberChoice, matchingKeys,
+ i18n("Encryption Key Selection"),
+ i18n("if in your language something like "
+ "'key(s)' isn't possible please "
+ "use the plural in the translation",
+ "More than one key matches \"%1\".\n\n"
+ "Select the key(s) which should "
+ "be used for this recipient."
+ ).arg(person),
+ KeyIDList(),
+ allowedKeys );
+ if( !keyIds.isEmpty() ) {
+ if( rememberChoice ) {
+ setKeysForAddress( person, keyIds );
+ }
+ return keyIds;
+ }
+ }
+
+ return KeyIDList();
+}
+
+// check if pgp 2.6.x or 5.0 is installed
+// kpgp will prefer to user pgp 5.0
+bool
+Module::checkForPGP(void)
+{
+ // get path
+ QCString path;
+ QStrList pSearchPaths;
+ int index = 0;
+ int lastindex = -1;
+
+ havePgp=FALSE;
+
+ path = getenv("PATH");
+ while((index = path.find(":",lastindex+1)) != -1)
+ {
+ pSearchPaths.append(path.mid(lastindex+1,index-lastindex-1));
+ lastindex = index;
+ }
+ if(lastindex != (int)path.length() - 1)
+ pSearchPaths.append( path.mid(lastindex+1,path.length()-lastindex) );
+
+ QStrListIterator it(pSearchPaths);
+
+ haveGpg=FALSE;
+ // lets try gpg
+
+ for ( it.toFirst() ; it.current() ; ++it )
+ {
+ path = (*it);
+ path += "/gpg";
+ if ( !access( path, X_OK ) )
+ {
+ kdDebug(5100) << "Kpgp: gpg found" << endl;
+ havePgp=TRUE;
+ haveGpg=TRUE;
+ break;
+ }
+ }
+
+ // search for pgp5.0
+ havePGP5=FALSE;
+ for ( it.toFirst() ; it.current() ; ++it )
+ {
+ path = (*it);
+ path += "/pgpe";
+ if ( !access( path, X_OK ) )
+ {
+ kdDebug(5100) << "Kpgp: pgp 5 found" << endl;
+ havePgp=TRUE;
+ havePGP5=TRUE;
+ break;
+ }
+ }
+
+ // lets try pgp2.6.x
+ if (!havePgp) {
+ for ( it.toFirst() ; it.current() ; ++it )
+ {
+ path = it.current();
+ path += "/pgp";
+ if ( !access( path, X_OK ) )
+ {
+ kdDebug(5100) << "Kpgp: pgp 2 or 6 found" << endl;
+ havePgp=TRUE;
+ break;
+ }
+ }
+ }
+
+ if (!havePgp)
+ {
+ kdDebug(5100) << "Kpgp: no pgp found" << endl;
+ }
+
+ return havePgp;
+}
+
+void
+Module::assignPGPBase(void)
+{
+ if (pgp)
+ delete pgp;
+
+ if(havePgp)
+ {
+ switch (pgpType)
+ {
+ case tGPG:
+ kdDebug(5100) << "Kpgp: assign pgp - gpg" << endl;
+ pgp = new BaseG();
+ break;
+
+ case tPGP2:
+ kdDebug(5100) << "Kpgp: assign pgp - pgp 2" << endl;
+ pgp = new Base2();
+ break;
+
+ case tPGP5:
+ kdDebug(5100) << "Kpgp: assign pgp - pgp 5" << endl;
+ pgp = new Base5();
+ break;
+
+ case tPGP6:
+ kdDebug(5100) << "Kpgp: assign pgp - pgp 6" << endl;
+ pgp = new Base6();
+ break;
+
+ case tOff:
+ // dummy handler
+ kdDebug(5100) << "Kpgp: pgpBase is dummy " << endl;
+ pgp = new Base();
+ break;
+
+ case tAuto:
+ kdDebug(5100) << "Kpgp: assign pgp - auto" << endl;
+ // fall through
+ default:
+ kdDebug(5100) << "Kpgp: assign pgp - default" << endl;
+ if (haveGpg)
+ {
+ kdDebug(5100) << "Kpgp: pgpBase is gpg " << endl;
+ pgp = new BaseG();
+ pgpType = tGPG;
+ }
+ else if(havePGP5)
+ {
+ kdDebug(5100) << "Kpgp: pgpBase is pgp 5" << endl;
+ pgp = new Base5();
+ pgpType = tPGP5;
+ }
+ else
+ {
+ Base6 *pgp_v6 = new Base6();
+ if (!pgp_v6->isVersion6())
+ {
+ kdDebug(5100) << "Kpgp: pgpBase is pgp 2 " << endl;
+ delete pgp_v6;
+ pgp = new Base2();
+ pgpType = tPGP2;
+ }
+ else
+ {
+ kdDebug(5100) << "Kpgp: pgpBase is pgp 6 " << endl;
+ pgp = pgp_v6;
+ pgpType = tPGP6;
+ }
+ }
+ } // switch
+ }
+ else
+ {
+ // dummy handler
+ kdDebug(5100) << "Kpgp: pgpBase is dummy " << endl;
+ pgp = new Base();
+ pgpType = tOff;
+ }
+}
+
+QString
+Module::canonicalAddress( const QString& _adress )
+{
+ int index,index2;
+
+ QString address = _adress.simplifyWhiteSpace();
+ address = address.stripWhiteSpace();
+
+ // just leave pure e-mail address.
+ if((index = address.find("<")) != -1)
+ if((index2 = address.find("@",index+1)) != -1)
+ if((index2 = address.find(">",index2+1)) != -1)
+ return address.mid(index,index2-index+1);
+
+ if((index = address.find("@")) == -1)
+ {
+ // local address
+ //char hostname[1024];
+ //gethostname(hostname,1024);
+ //return "<" + address + "@" + hostname + ">";
+ return "<" + address + "@localdomain>";
+ }
+ else
+ {
+ int index1 = address.findRev(" ",index);
+ int index2 = address.find(" ",index);
+ if(index2 == -1) index2 = address.length();
+ return "<" + address.mid(index1+1 ,index2-index1-1) + ">";
+ }
+}
+
+void
+Module::readPublicKeys( bool reread )
+{
+ if( 0 == pgp ) assignPGPBase();
+
+ if( !usePGP() )
+ {
+ mPublicKeys.clear();
+ mPublicKeysCached = false;
+ return;
+ }
+
+ if( !mPublicKeysCached || reread )
+ {
+ if( mPublicKeys.isEmpty() )
+ {
+ mPublicKeys = pgp->publicKeys();
+ }
+ else
+ {
+ KeyList newPublicKeyList = pgp->publicKeys();
+
+ // merge the trust info from the old key list into the new key list
+ // FIXME: This is currently O(K^2) where K = #keys. As the key lists
+ // are sorted this can be done in O(K).
+ KeyListIterator it( newPublicKeyList );
+ for( it.toFirst(); (*it); ++it )
+ {
+ Key* oldKey = publicKey( (*it)->primaryKeyID() );
+ if( oldKey )
+ {
+ (*it)->cloneKeyTrust( oldKey );
+ }
+ }
+
+ mPublicKeys = newPublicKeyList;
+ }
+
+ mPublicKeysCached = true;
+ mPublicKeys.setAutoDelete( true );
+ }
+}
+
+void
+Module::readSecretKeys( bool reread )
+{
+ if( 0 == pgp ) assignPGPBase();
+
+ if( !usePGP() )
+ {
+ mSecretKeys.clear();
+ mSecretKeysCached = false;
+ return;
+ }
+
+ if( mSecretKeys.isEmpty() || reread )
+ {
+ if( mSecretKeys.isEmpty() )
+ {
+ mSecretKeys = pgp->secretKeys();
+ }
+ else
+ {
+ KeyList newSecretKeyList = pgp->secretKeys();
+
+ // merge the trust info from the old key list into the new key list
+ // FIXME: This is currently O(K^2) where K = #keys. As the key lists
+ // are sorted this can be done in O(K).
+ KeyListIterator it( newSecretKeyList );
+ for( it.toFirst(); (*it); ++it )
+ {
+ Key* oldKey = secretKey( (*it)->primaryKeyID() );
+ if( oldKey )
+ {
+ (*it)->cloneKeyTrust( oldKey );
+ }
+ }
+
+ mSecretKeys = newSecretKeyList;
+ }
+
+ mSecretKeysCached = true;
+ mSecretKeys.setAutoDelete( true );
+ }
+}
+
+KeyID
+Module::selectKey( const KeyList& keys,
+ const QString& title,
+ const QString& text /* = QString::null */ ,
+ const KeyID& keyId /* = KeyID() */ ,
+ const unsigned int allowedKeys /* = AllKeys */ )
+{
+ KeyID retval = KeyID();
+
+ KeySelectionDialog dlg( keys, title, text, KeyIDList( keyId ), false,
+ allowedKeys, false );
+
+ QApplication::setOverrideCursor( QCursor(QCursor::ArrowCursor) );
+ bool rej = ( dlg.exec() == QDialog::Rejected );
+ QApplication::restoreOverrideCursor();
+
+ if( !rej ) {
+ retval = dlg.key();
+ }
+
+ return retval;
+}
+
+KeyIDList
+Module::selectKeys( const KeyList& keys,
+ const QString& title,
+ const QString& text /* = QString::null */ ,
+ const KeyIDList& keyIds /* = KeyIDList() */ ,
+ const unsigned int allowedKeys /* = AllKeys */ )
+{
+ KeyIDList retval = KeyIDList();
+
+ KeySelectionDialog dlg( keys, title, text, keyIds, false, allowedKeys,
+ true );
+
+ QApplication::setOverrideCursor( QCursor(QCursor::ArrowCursor) );
+ bool rej = ( dlg.exec() == QDialog::Rejected );
+ QApplication::restoreOverrideCursor();
+
+ if( !rej ) {
+ retval = dlg.keys();
+ }
+
+ return retval;
+}
+
+
+KeyID
+Module::selectKey( bool& rememberChoice,
+ const KeyList& keys,
+ const QString& title,
+ const QString& text /* = QString::null */ ,
+ const KeyID& keyId /* = KeyID() */ ,
+ const unsigned int allowedKeys /* = AllKeys */ )
+{
+ KeyID retval = KeyID();
+
+ KeySelectionDialog dlg( keys, title, text, KeyIDList( keyId ), false,
+ allowedKeys, false );
+
+ QApplication::setOverrideCursor( QCursor(QCursor::ArrowCursor) );
+ bool rej = ( dlg.exec() == QDialog::Rejected );
+ QApplication::restoreOverrideCursor();
+
+ if( !rej ) {
+ retval = dlg.key();
+ rememberChoice = dlg.rememberSelection();
+ }
+ else {
+ rememberChoice = false;
+ }
+
+ return retval;
+}
+
+KeyIDList
+Module::selectKeys( bool& rememberChoice,
+ const KeyList& keys,
+ const QString& title,
+ const QString& text /* = QString::null */ ,
+ const KeyIDList& keyIds /* = KeyIDList() */ ,
+ const unsigned int allowedKeys /* = AllKeys */ )
+{
+ KeyIDList retval = KeyIDList();
+
+ KeySelectionDialog dlg( keys, title, text, keyIds, true, allowedKeys,
+ true );
+
+ QApplication::setOverrideCursor( QCursor(QCursor::ArrowCursor) );
+ bool rej = ( dlg.exec() == QDialog::Rejected );
+ QApplication::restoreOverrideCursor();
+
+ if( !rej ) {
+ retval = dlg.keys();
+ rememberChoice = dlg.rememberSelection();
+ }
+ else {
+ rememberChoice = false;
+ }
+
+ return retval;
+}
+
+KeyIDList
+Module::keysForAddress( const QString& address )
+{
+ if( address.isEmpty() ) {
+ return KeyIDList();
+ }
+ QString addr = canonicalAddress( address ).lower();
+ if( addressDataDict.contains( addr ) ) {
+ return addressDataDict[addr].keyIds;
+ }
+ else {
+ return KeyIDList();
+ }
+}
+
+void
+Module::setKeysForAddress( const QString& address, const KeyIDList& keyIds )
+{
+ if( address.isEmpty() ) {
+ return;
+ }
+ QString addr = canonicalAddress( address ).lower();
+ if( addressDataDict.contains( addr ) ) {
+ addressDataDict[addr].keyIds = keyIds;
+ }
+ else {
+ AddressData data;
+ data.encrPref = UnknownEncryptPref;
+ data.keyIds = keyIds;
+ addressDataDict.insert( addr, data );
+ }
+
+ //writeAddressData();
+}
+
+void
+Module::readAddressData()
+{
+ QString address;
+ AddressData data;
+
+ KConfigGroup general( config, "General" );
+ int num = general.readNumEntry( "addressEntries", 0 );
+
+ addressDataDict.clear();
+ for( int i=1; i<=num; i++ ) {
+ KConfigGroup addrGroup( config, QString("Address #%1").arg(i).local8Bit() );
+ address = addrGroup.readEntry( "Address" );
+ data.keyIds = KeyIDList::fromStringList( addrGroup.readListEntry( "Key IDs" ) );
+ data.encrPref = (EncryptPref) addrGroup.readNumEntry( "EncryptionPreference",
+ UnknownEncryptPref );
+// kdDebug(5100) << "Read address " << i << ": " << address
+// << "\nKey IDs: 0x" << data.keyIds.toStringList().join(", 0x")
+// << "\nEncryption preference: " << data.encrPref << endl;
+ if ( !address.isEmpty() ) {
+ addressDataDict.insert( address, data );
+ }
+ }
+}
+
+void
+Module::writeAddressData()
+{
+ KConfigGroup general( config, "General" );
+ general.writeEntry( "addressEntries", addressDataDict.count() );
+
+ int i;
+ AddressDataDict::Iterator it;
+ for ( i=1, it = addressDataDict.begin();
+ it != addressDataDict.end();
+ ++it, i++ ) {
+ KConfigGroup addrGroup( config, QString("Address #%1").arg(i).local8Bit() );
+ addrGroup.writeEntry( "Address", it.key() );
+ addrGroup.writeEntry( "Key IDs", it.data().keyIds.toStringList() );
+ addrGroup.writeEntry( "EncryptionPreference", it.data().encrPref );
+ }
+
+ config->sync();
+}
+
+EncryptPref
+Module::encryptionPreference( const QString& address )
+{
+ QString addr = canonicalAddress( address ).lower();
+ if( addressDataDict.contains( addr ) ) {
+ return addressDataDict[addr].encrPref;
+ }
+ else {
+ return UnknownEncryptPref;
+ }
+}
+
+void
+Module::setEncryptionPreference( const QString& address,
+ const EncryptPref pref )
+{
+ if( address.isEmpty() ) {
+ return;
+ }
+ QString addr = canonicalAddress( address ).lower();
+ if( addressDataDict.contains( addr ) ) {
+ addressDataDict[addr].encrPref = pref;
+ }
+ else {
+ AddressData data;
+ data.encrPref = pref;
+ addressDataDict.insert( addr, data );
+ }
+}
+
+} // namespace Kpgp
diff --git a/libkpgp/kpgp.h b/libkpgp/kpgp.h
new file mode 100644
index 00000000..9b3ade72
--- /dev/null
+++ b/libkpgp/kpgp.h
@@ -0,0 +1,469 @@
+/*
+ kpgp.h
+
+ Copyright (C) 2001,2002 the KPGP authors
+ See file AUTHORS.kpgp for details
+
+ This file is part of KPGP, the KDE PGP/GnuPG support library.
+
+ KPGP is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#ifndef KPGP_H
+#define KPGP_H
+
+#include <stdio.h>
+#include <qstring.h>
+#include <qstrlist.h>
+#include <qdialog.h>
+#include <qwidget.h>
+#include <qcombobox.h>
+#include <qlayout.h>
+#include <qpushbutton.h>
+#include <qlistview.h>
+#include <qbuttongroup.h>
+#include <qradiobutton.h>
+#include <qmultilineedit.h>
+#include <qcheckbox.h>
+
+#include <kdialogbase.h>
+
+#include "kpgpkey.h"
+#include <kdepimmacros.h>
+
+class QLineEdit;
+class QCursor;
+class QCheckBox;
+class QGridLayout;
+
+class KConfig;
+
+namespace Kpgp {
+
+/** This enumerated type is used by Kpgp::* to indicate which keys can be
+ selected by the user. The following values are available:
+ - Kpgp::PublicKeys: Public keys are shown in the selection dialog.
+ - Kpgp::SecretKeys: Secret keys are shown in the selection dialog.
+ - Kpgp::EncryptionKeys: Encryption keys can be selected.
+ - Kpgp::SigningKeys: Signing keys can be selected.
+ - Kpgp::ValidKeys: Only valid keys can be selected.
+ - Kpgp::TrustedKeys: Only trusted keys can be selected.
+ - Kpgp::AllKeys == PublicKeys | SecretKeys | EncryptionKeys | SigningKeys
+*/
+enum { PublicKeys = 1,
+ SecretKeys = 2,
+ EncryptionKeys = 4,
+ SigningKeys = 8,
+ ValidKeys = 16,
+ TrustedKeys = 32,
+ AllKeys = PublicKeys | SecretKeys | EncryptionKeys | SigningKeys,
+ PubSecKeys = PublicKeys | SecretKeys,
+ EncrSignKeys = EncryptionKeys | SigningKeys
+};
+
+enum Result
+{
+ Failure = 0,
+ Ok = 1,
+ Canceled = 2
+};
+
+class Base;
+class Block;
+
+class KDE_EXPORT Module
+{
+ friend class Block;
+
+private:
+ // the class running pgp
+ Base *pgp;
+
+public:
+ Module();
+ virtual ~Module();
+
+ /** the following virtual function form the interface to the
+ application using Kpgp
+ */
+ virtual void readConfig();
+ virtual void writeConfig(bool sync);
+ virtual void init();
+
+ /** decrypts the given OpenPGP block if the passphrase is good.
+ returns false otherwise */
+ bool decrypt( Block& block );
+
+ /** Tries to verify the given OpenPGP block */
+ bool verify( Block& block );
+
+ /** clearsigns the given OpenPGP block with the key corresponding to the
+ given key id. The charset is needed to display the text correctly.
+ Returns
+ Failure if there was an unresolvable error
+ Canceled if signing was canceled
+ Ok if everything is o.k.
+ */
+ Kpgp::Result clearsign( Block& block,
+ const KeyID& keyId, const QCString& charset = 0 );
+
+ /** encrypts the given OpenPGP block for a list of persons. if sign is true
+ then the block is clearsigned with the key corresponding to the given
+ key id. The charset is needed to display the text correctly.
+ Returns
+ Failure if there was an unresolvable error
+ Canceled if encryption was canceled
+ Ok if everything is o.k.
+ */
+ Kpgp::Result encrypt( Block& block,
+ const QStringList& receivers, const KeyID& keyId,
+ bool sign, const QCString& charset = 0 );
+
+ /** Determines the keys which should be used for encrypting the message
+ to the given list of recipients.
+ Returns:
+ Failure if there was an unresolvable error
+ Canceled if encryption was canceled
+ Ok if everything is o.k.
+ */
+ Kpgp::Result getEncryptionKeys( KeyIDList& encryptionKeyIds,
+ const QStringList& recipients,
+ const KeyID& keyId );
+
+ /** checks if encrypting to the given list of persons is possible and
+ desired, i.e. if we have a (trusted) key for every recipient and
+ if encryption to all keys is allowed.
+ Returns
+ 0 if encryption is not possible or not desired,
+ 1 if encryption is possible and desired,
+ 2 if encryption is possible, but the user wants to be asked and
+ -1 if there is a conflict which can't be automatically resolved.
+ */
+ int encryptionPossible( const QStringList& recipients );
+
+protected:
+ int doEncSign( Block& block, const KeyIDList& recipientKeyIds, bool sign );
+
+public:
+ /** sign a key in the keyring with users signature. */
+ bool signKey( const KeyID& keyID );
+
+ /** get the list of cached public keys. */
+ const KeyList publicKeys();
+
+ /** get the list of cached secret keys. */
+ const KeyList secretKeys();
+
+ /** Reads the list of public keys if necessary or if <em>reread</em> is true.
+ */
+ void readPublicKeys( bool reread = false );
+
+ /** Reads the list of secret keys if necessary or if <em>reread</em> is true.
+ */
+ void readSecretKeys( bool reread = false );
+
+ /** try to get an ascii armored key block for the given public key */
+ QCString getAsciiPublicKey( const KeyID& keyID );
+
+ /** Returns the public key with the given key ID or null if no matching
+ key is found.
+ */
+ Key* publicKey( const KeyID& keyID );
+
+ /** Returns the first public key with the given user ID or null if no
+ matching key is found.
+ */
+ Key* publicKey( const QString& userID );
+
+ /** Returns the secret key with the given key ID or null if no matching
+ key is found.
+ */
+ Key* secretKey( const KeyID& keyID );
+
+ /** Returns the trust value for the given key. This is the maximal trust
+ value of any of the user ids of this key.
+ */
+ Validity keyTrust( const KeyID& keyID );
+
+ /** Returns the trust value of a key with the given user id. If more than
+ one key have this user id then the first key with this user id will be
+ chosen.
+ */
+ Validity keyTrust( const QString& userID );
+
+ /** Returns TRUE if the given key is at least trusted marginally. Otherwise
+ FALSE is returned.
+ */
+ bool isTrusted( const KeyID& keyID );
+
+ /** Rereads the key data for the given key and returns the reread data. If
+ <em>readTrust</em> is true then the trust of this key will be determined.
+ */
+ Key* rereadKey( const KeyID& keyID, const bool readTrust = true );
+
+ /** Request the change of the passphrase of the actual secret
+ key. TBI */
+ bool changePassPhrase();
+
+ /** set a user identity to use (if you have more than one...)
+ by default, pgp uses the identity which was generated last. */
+ void setUser(const KeyID& keyID);
+ /** Returns the actual key ID of the currently set key. */
+ const KeyID user() const;
+
+ /** always encrypt message to oneself? */
+ void setEncryptToSelf(bool flag);
+ bool encryptToSelf(void) const;
+
+ /** store passphrase in pgp object
+ Problem: passphrase stays in memory.
+ Advantage: you can call en-/decrypt without always passing the
+ passphrase
+ */
+ void setStorePassPhrase(bool);
+ bool storePassPhrase(void) const;
+
+ /** clears everything from memory */
+ void clear(const bool erasePassPhrase = FALSE);
+
+ /** returns the last error that occurred */
+ const QString lastErrorMsg(void) const;
+
+ // what version of PGP/GPG should we use
+ enum PGPType { tAuto, tGPG, tPGP2, tPGP5, tPGP6, tOff } pgpType;
+
+ // did we find a pgp executable?
+ bool havePGP(void) const;
+
+ /** Should PGP/GnuPG be used? */
+ bool usePGP(void) const { return (havePGP() && (pgpType != tOff)); }
+
+ // show the result of encryption/signing?
+ void setShowCipherText(const bool flag);
+ bool showCipherText(void) const;
+
+ // show the encryption keys for approval?
+ void setShowKeyApprovalDlg(const bool flag);
+ bool showKeyApprovalDlg(void) const;
+
+ /** Shows a key selection dialog with all secret keys and the given title
+ and the (optional) text. If <em>keyId</em> is given, then the
+ corresponding key is selected.
+ */
+ KeyID selectSecretKey( const QString& title,
+ const QString& text = QString::null,
+ const KeyID& keyId = KeyID() );
+
+ /** Shows a key selection dialog with all public keys and the given title
+ and the (optional) text. If <em>oldKeyId</em> is given, then the
+ corresponding key is selected. If <em>address</em> is given, then the
+ chosen key will be stored (if the user wants it to be stored).
+ <em>mode</em> specifies which keys can be selected.
+ */
+ KeyID selectPublicKey( const QString& title,
+ const QString& text = QString::null,
+ const KeyID& oldKeyId = KeyID(),
+ const QString& address = QString::null,
+ const unsigned int allowedKeys = AllKeys );
+
+ /** Shows a key selection dialog with all public keys and the given title
+ and the (optional) text. If <em>oldKeyId</em> is given, then the
+ corresponding key is selected. If <em>address</em> is given, then the
+ chosen key will be stored (if the user wants it to be stored).
+ <em>mode</em> specifies which keys can be selected.
+ */
+ KeyIDList selectPublicKeys( const QString& title,
+ const QString& text = QString::null,
+ const KeyIDList& oldKeyIds = KeyIDList(),
+ const QString& address = QString::null,
+ const unsigned int allowedKeys = AllKeys );
+
+ // FIXME: key management
+
+ /** Reads the encryption preference for the given address
+ from the config file.
+ */
+ EncryptPref encryptionPreference( const QString& address );
+
+ /** Writes the given encryption preference for the given address
+ to the config file.
+ */
+ void setEncryptionPreference( const QString& address,
+ const EncryptPref pref );
+
+ // -- static member functions --------------------------------------------
+
+ /** return the actual pgp object */
+ static Kpgp::Module *getKpgp();
+
+ /** get the kpgp config object */
+ static KConfig *getConfig();
+
+ /** Parses the given message and splits it into OpenPGP blocks and
+ Non-OpenPGP blocks.
+ Returns TRUE if the message contains at least one OpenPGP block and
+ FALSE otherwise.
+ The format is then:
+ <pre>
+ 1st Non-OpenPGP block
+ 1st OpenPGP block
+ 2nd Non-OpenPGP block
+ ...
+ n-th OpenPGP block
+ (n+1)-th Non-OpenPGP block
+ </pre>
+ */
+ static bool prepareMessageForDecryption( const QCString& msg,
+ QPtrList<Block>& pgpBlocks,
+ QStrList& nonPgpBlocks );
+
+private:
+ /** check if we have a trusted encryption key for the given person */
+ bool haveTrustedEncryptionKey( const QString& person );
+
+ /** get a list of encryption keys to be used for the given recipient */
+ KeyIDList getEncryptionKeys( const QString& person );
+
+ /** Set pass phrase */
+ bool setPassPhrase(const char* pass);
+
+ /** test if the PGP executable is found and if there is a passphrase
+ set or given. Returns:
+ 1 if everything is ok
+ 0 (together with some warning message) if something is missing
+ -1 if the passphrase dialog was canceled
+ */
+ int prepare(bool needPassPhrase=FALSE, Block* block = 0 );
+
+ /** cleanup passphrase if it should not be stored. */
+ void cleanupPass() { if (!storePass) wipePassPhrase(); }
+
+ /** Wipes and optionally frees the memory used to hold the
+ passphrase. */
+ void wipePassPhrase(bool free=false);
+
+ // transform an address into canonical form
+ QString canonicalAddress( const QString& person );
+
+ /** Shows a dialog to let the user select a key from the given list of keys
+ */
+ KeyID selectKey( const KeyList& keys,
+ const QString& title,
+ const QString& text = QString::null,
+ const KeyID& keyId = KeyID(),
+ const unsigned int allowedKeys = AllKeys );
+
+ /** Shows a dialog to let the user select a key from the given list of keys
+ */
+ KeyIDList selectKeys( const KeyList& keys,
+ const QString& title,
+ const QString& text = QString::null,
+ const KeyIDList& keyIds = KeyIDList(),
+ const unsigned int allowedKeys = AllKeys );
+
+ /** Shows a dialog to let the user select a key from the given list of keys.
+ The dialog includes a checkbox ("Remember decision"). The state of the
+ checkbox is returned in rememberChoice.
+ */
+ KeyID selectKey( bool& rememberChoice,
+ const KeyList& keys,
+ const QString& title,
+ const QString& text = QString::null,
+ const KeyID& keyId = KeyID(),
+ const unsigned int allowedKeys = AllKeys );
+
+ /** Shows a dialog to let the user select a list of keys from the given
+ list of keys. The dialog includes a checkbox ("Remember decision").
+ The state of the checkbox is returned in rememberChoice.
+ */
+ KeyIDList selectKeys( bool& rememberChoice,
+ const KeyList& keys,
+ const QString& title,
+ const QString& text = QString::null,
+ const KeyIDList& keyIds = KeyIDList(),
+ const unsigned int allowedKeys = AllKeys );
+
+ /** Returns the OpenPGP keys which should be used for encryption to the
+ given address.
+ */
+ KeyIDList keysForAddress( const QString& address );
+
+ /** Set an email address -> list of OpenPGP keys association.
+ */
+ void setKeysForAddress( const QString& address, const KeyIDList& keyIDs );
+
+ /** Remove an email address -> OpenPGP key association. */
+ void removeKeyForAddress( const QString& address );
+
+ /** Reads the email address -> OpenPGP key associations from the config
+ file.
+ */
+ void readAddressData();
+
+ /** Writes the email address -> OpenPGP key associations to the config
+ file.
+ */
+ void writeAddressData();
+
+ bool checkForPGP(void);
+ void assignPGPBase(void);
+
+ static Kpgp::Module *kpgpObject;
+ KConfig *config;
+
+ struct AddressData {
+ KeyIDList keyIds;
+ EncryptPref encrPref;
+ };
+ typedef QMap<QString, AddressData> AddressDataDict;
+ AddressDataDict addressDataDict;
+
+ KeyList mPublicKeys;
+ bool mPublicKeysCached : 1; // did we already read the public keys?
+ KeyList mSecretKeys;
+ bool mSecretKeysCached : 1; // did we already read the secret keys?
+
+ bool storePass : 1;
+ char * passphrase;
+ size_t passphrase_buffer_len;
+
+ QString errMsg;
+
+ KeyID pgpUser; // the key ID which is used to sign/encrypt to self
+ bool flagEncryptToSelf : 1;
+
+ bool havePgp : 1;
+ bool havePGP5 : 1;
+ bool haveGpg : 1;
+ bool havePassPhrase : 1;
+ bool showEncryptionResult : 1;
+ bool mShowKeyApprovalDlg : 1;
+}; // class Module
+
+// -- inlined member functions ---------------------------------------------
+
+inline void
+Module::setShowKeyApprovalDlg( const bool flag )
+{
+ mShowKeyApprovalDlg = flag;
+}
+
+inline bool
+Module::showKeyApprovalDlg( void ) const
+{
+ return mShowKeyApprovalDlg;
+}
+
+// -------------------------------------------------------------------------
+
+} // namespace Kpgp
+
+#endif
+
diff --git a/libkpgp/kpgp.upd b/libkpgp/kpgp.upd
new file mode 100644
index 00000000..ce6c5fcf
--- /dev/null
+++ b/libkpgp/kpgp.upd
@@ -0,0 +1,9 @@
+# Remove the old Encryption Preferences (applies only to KDE3 Beta users)
+Id=preKDE3_a
+File=kpgprc
+RemoveGroup=EncryptionPreferences
+# new format for storing info about encryption keys and encryption preferences
+# for email addresses (in kpgprc)
+Id=3.1-1
+File=kpgprc
+Script=kpgp-3.1-upgrade-address-data.pl,perl
diff --git a/libkpgp/kpgpbase.cpp b/libkpgp/kpgpbase.cpp
new file mode 100644
index 00000000..39762333
--- /dev/null
+++ b/libkpgp/kpgpbase.cpp
@@ -0,0 +1,684 @@
+/*
+ kpgpbase.cpp
+
+ Copyright (C) 2001,2002 the KPGP authors
+ See file AUTHORS.kpgp for details
+
+ This file is part of KPGP, the KDE PGP/GnuPG support library.
+
+ KPGP is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <kdebug.h>
+
+#include <config.h>
+
+#include "kpgpbase.h"
+#include "kpgp.h"
+#include "kpgpblock.h"
+
+#include <stdlib.h> /* setenv, unsetenv */
+#include <unistd.h> /* pipe, close, fork, dup2, execl, _exit, write, read */
+#include <sys/poll.h> /* poll, etc. */
+#include <sys/types.h> /* pid_t */
+#include <sys/wait.h> /* waitpid */
+#include <errno.h>
+
+#include <qapplication.h>
+
+
+namespace Kpgp {
+
+Base::Base()
+ : input(), output(), error(), errMsg(), status(OK)
+{
+}
+
+
+Base::~Base()
+{
+}
+
+
+void
+Base::clear()
+{
+ input = QCString();
+ output = QCString();
+ error = QCString();
+ errMsg = QString::null;
+ status = OK;
+}
+
+
+int
+Base::run( const char *cmd, const char *passphrase, bool onlyReadFromPGP )
+{
+ /* the pipe ppass is used for to pass the password to
+ * pgp. passing the password together with the normal input through
+ * stdin doesn't seem to work as expected (at least for pgp5.0)
+ */
+ char str[1025] = "\0";
+ int pin[2], pout[2], perr[2], ppass[2];
+ int len, len2;
+ FILE *pass;
+ pid_t child_pid;
+ int childExitStatus;
+ struct pollfd pollin, pollout, pollerr;
+ int pollstatus;
+
+ if(passphrase)
+ {
+ pipe(ppass);
+
+ pass = fdopen(ppass[1], "w");
+ fwrite(passphrase, sizeof(char), strlen(passphrase), pass);
+ fwrite("\n", sizeof(char), 1, pass);
+ fclose(pass);
+ close(ppass[1]);
+
+ // tell pgp which fd to use for the passphrase
+ QCString tmp;
+ tmp.sprintf("%d",ppass[0]);
+ ::setenv("PGPPASSFD",tmp.data(),1);
+
+ //Uncomment these lines for testing only! Doing so will decrease security!
+ //kdDebug(5100) << "pgp PGPPASSFD = " << tmp << endl;
+ //kdDebug(5100) << "pgp pass = " << passphrase << endl;
+ }
+ else
+ ::unsetenv("PGPPASSFD");
+
+ //Uncomment these lines for testing only! Doing so will decrease security!
+ kdDebug(5100) << "pgp cmd = " << cmd << endl;
+ //kdDebug(5100) << "pgp input = " << QString(input)
+ // << "input length = " << input.length() << endl;
+
+ error = "";
+ output = "";
+
+ pipe(pin);
+ pipe(pout);
+ pipe(perr);
+
+ QApplication::flushX();
+ if(!(child_pid = fork()))
+ {
+ /*We're the child.*/
+ close(pin[1]);
+ dup2(pin[0], 0);
+ close(pin[0]);
+
+ close(pout[0]);
+ dup2(pout[1], 1);
+ close(pout[1]);
+
+ close(perr[0]);
+ dup2(perr[1], 2);
+ close(perr[1]);
+
+ execl("/bin/sh", "sh", "-c", cmd, (void *)0);
+ _exit(127);
+ }
+
+ /*Only get here if we're the parent.*/
+ close(pin[0]);
+ close(pout[1]);
+ close(perr[1]);
+
+ // poll for "There is data to read."
+ pollout.fd = pout[0];
+ pollout.events = POLLIN;
+ pollout.revents = 0; // init with 0, just in case
+ pollerr.fd = perr[0];
+ pollerr.events = POLLIN;
+ pollerr.revents = 0; // init with 0, just in case
+
+ // poll for "Writing now will not block."
+ pollin.fd = pin[1];
+ pollin.events = POLLOUT;
+ pollin.revents = 0; // init with 0, just in case
+
+ if (!onlyReadFromPGP) {
+ if (!input.isEmpty()) {
+ // write to pin[1] one line after the other to prevent dead lock
+ for (unsigned int i=0; i<input.length(); i+=len2) {
+ len2 = 0;
+
+ // check if writing now to pin[1] will not block (5 ms timeout)
+ //kdDebug(5100) << "Polling pin[1]..." << endl;
+ pollstatus = poll(&pollin, 1, 5);
+ if (pollstatus == 1) {
+ //kdDebug(5100) << "Status for polling pin[1]: " << pollin.revents << endl;
+ if (pollin.revents & POLLERR) {
+ kdDebug(5100) << "PGP seems to have hung up" << endl;
+ break;
+ }
+ else if (pollin.revents & POLLOUT) {
+ // search end of next line
+ if ((len2 = input.find('\n', i)) == -1)
+ len2 = input.length()-i;
+ else
+ len2 = len2-i+1;
+
+ //kdDebug(5100) << "Trying to write " << len2 << " bytes to pin[1] ..." << endl;
+ len2 = write(pin[1], input.mid(i,len2).data(), len2);
+ //kdDebug(5100) << "Wrote " << len2 << " bytes to pin[1] ..." << endl;
+ }
+ }
+ else if (!pollstatus) {
+ //kdDebug(5100) << "Timeout while polling pin[1]: "
+ // << pollin.revents << endl;
+ }
+ else if (pollstatus == -1) {
+ kdDebug(5100) << "Error while polling pin[1]: "
+ << pollin.revents << endl;
+ }
+
+ if (pout[0] >= 0) {
+ do {
+ // check if there is data to read from pout[0]
+ //kdDebug(5100) << "Polling pout[0]..." << endl;
+ pollstatus = poll(&pollout, 1, 0);
+ if (pollstatus == 1) {
+ //kdDebug(5100) << "Status for polling pout[0]: " << pollout.revents << endl;
+ if (pollout.revents & POLLIN) {
+ //kdDebug(5100) << "Trying to read " << 1024 << " bytes from pout[0]" << endl;
+ if ((len = read(pout[0],str,1024))>0) {
+ //kdDebug(5100) << "Read " << len << " bytes from pout[0]" << endl;
+ str[len] ='\0';
+ output += str;
+ }
+ else
+ break;
+ }
+ }
+ else if (pollstatus == -1) {
+ kdDebug(5100) << "Error while polling pout[0]: "
+ << pollout.revents << endl;
+ }
+ } while ((pollstatus == 1) && (pollout.revents & POLLIN));
+ }
+
+ if (perr[0] >= 0) {
+ do {
+ // check if there is data to read from perr[0]
+ //kdDebug(5100) << "Polling perr[0]..." << endl;
+ pollstatus = poll(&pollerr, 1, 0);
+ if (pollstatus == 1) {
+ //kdDebug(5100) << "Status for polling perr[0]: " << pollerr.revents << endl;
+ if (pollerr.revents & POLLIN) {
+ //kdDebug(5100) << "Trying to read " << 1024 << " bytes from perr[0]" << endl;
+ if ((len = read(perr[0],str,1024))>0) {
+ //kdDebug(5100) << "Read " << len << " bytes from perr[0]" << endl;
+ str[len] ='\0';
+ error += str;
+ }
+ else
+ break;
+ }
+ }
+ else if (pollstatus == -1) {
+ kdDebug(5100) << "Error while polling perr[0]: "
+ << pollerr.revents << endl;
+ }
+ } while ((pollstatus == 1) && (pollerr.revents & POLLIN));
+ }
+
+ // abort writing to PGP if PGP hung up
+ if ((pollstatus == 1) &&
+ ((pollout.revents & POLLHUP) || (pollerr.revents & POLLHUP))) {
+ kdDebug(5100) << "PGP hung up" << endl;
+ break;
+ }
+ }
+ }
+ else // if input.isEmpty()
+ write(pin[1], "\n", 1);
+ //kdDebug(5100) << "All input was written to pin[1]" << endl;
+ }
+ close(pin[1]);
+
+ pid_t waitpidRetVal;
+
+ do {
+ //kdDebug(5100) << "Checking if PGP is still running..." << endl;
+ childExitStatus = 0;
+ waitpidRetVal = waitpid(child_pid, &childExitStatus, WNOHANG);
+ //kdDebug(5100) << "waitpid returned " << waitpidRetVal << endl;
+ if (pout[0] >= 0) {
+ do {
+ // check if there is data to read from pout[0]
+ //kdDebug(5100) << "Polling pout[0]..." << endl;
+ pollstatus = poll(&pollout, 1, 0);
+ if (pollstatus == 1) {
+ //kdDebug(5100) << "Status for polling pout[0]: " << pollout.revents << endl;
+ if (pollout.revents & POLLIN) {
+ //kdDebug(5100) << "Trying to read " << 1024 << " bytes from pout[0]" << endl;
+ if ((len = read(pout[0],str,1024))>0) {
+ //kdDebug(5100) << "Read " << len << " bytes from pout[0]" << endl;
+ str[len] ='\0';
+ output += str;
+ } else {
+ /*
+ * Apparently, on NetBSD when the child dies, the pipe begins
+ * receiving empty data packets *before* waitpid() has signaled
+ * that the child has died. Also, notice that this happens
+ * without any error bit being set in pollfd.revents (is this a
+ * NetBSD bug??? ). Notice that these anomalous packets exist
+ * according to poll(), but have length 0 according to read().
+ * Thus, kde can remain stuck inside this loop.
+ *
+ * A solution to this problem is to get out of the inner loop
+ * when read() returns <=0. In this way, kde has another chance
+ * to call waitpid() to check if the child has died -- and this
+ * time the call should succeed.
+ *
+ * Setting POLLHUP in pollfd.revents is not necessary, but I just
+ * like the idea of signaling that something strange has
+ * happened.
+ */
+ pollout.revents |= POLLHUP;
+ break;
+ }
+ }
+ }
+ else if (pollstatus == -1) {
+ kdDebug(5100) << "Error while polling pout[0]: "
+ << pollout.revents << endl;
+ }
+ } while ((pollstatus == 1) && (pollout.revents & POLLIN));
+ }
+
+ if (perr[0] >= 0) {
+ do {
+ // check if there is data to read from perr[0]
+ //kdDebug(5100) << "Polling perr[0]..." << endl;
+ pollstatus = poll(&pollerr, 1, 0);
+ if (pollstatus == 1) {
+ //kdDebug(5100) << "Status for polling perr[0]: " << pollerr.revents << endl;
+ if (pollerr.revents & POLLIN) {
+ //kdDebug(5100) << "Trying to read " << 1024 << " bytes from perr[0]" << endl;
+ if ((len = read(perr[0],str,1024))>0) {
+ //kdDebug(5100) << "Read " << len << " bytes from perr[0]" << endl;
+ str[len] ='\0';
+ error += str;
+ } else {
+ /*
+ * Apparently, on NetBSD when the child dies, the pipe begins
+ * receiving empty data packets *before* waitpid() has signaled
+ * that the child has died. Also, notice that this happens
+ * without any error bit being set in pollfd.revents (is this a
+ * NetBSD bug??? ). Notice that these anomalous packets exist
+ * according to poll(), but have length 0 according to read().
+ * Thus, kde can remain stuck inside this loop.
+ *
+ * A solution to this problem is to get out of the inner loop
+ * when read() returns <=0. In this way, kde has another chance
+ * to call waitpid() to check if the child has died -- and this
+ * time the call should succeed.
+ *
+ * Setting POLLHUP in pollfd.revents is not necessary, but I just
+ * like the idea of signaling that something strange has
+ * happened.
+ */
+ pollerr.revents |= POLLHUP;
+ break;
+ }
+ }
+ }
+ else if (pollstatus == -1) {
+ kdDebug(5100) << "Error while polling perr[0]: "
+ << pollerr.revents << endl;
+ }
+ } while ((pollstatus == 1) && (pollerr.revents & POLLIN));
+ }
+ } while (waitpidRetVal == 0);
+
+ close(pout[0]);
+ close(perr[0]);
+
+ unsetenv("PGPPASSFD");
+ if(passphrase)
+ close(ppass[0]);
+
+ // Did the child exit normally?
+ if (WIFEXITED(childExitStatus) != 0) {
+ // Get the return code of the child
+ childExitStatus = WEXITSTATUS(childExitStatus);
+ kdDebug(5100) << "PGP exited with exit status " << childExitStatus
+ << endl;
+ }
+ else {
+ childExitStatus = -1;
+ kdDebug(5100) << "PGP exited abnormally!" << endl;
+ }
+
+ //Uncomment these lines for testing only! Doing so will decrease security!
+ //kdDebug(5100) << "pgp output = " << QString(output) << endl;
+ //kdDebug(5100) << "pgp error = " << error << endl;
+
+ /* Make the information visible, so that a user can
+ * get to know what's going on during the pgp calls.
+ */
+ kdDebug(5100) << error << endl;
+
+ return childExitStatus;
+}
+
+
+int
+Base::runGpg( const char *cmd, const char *passphrase, bool onlyReadFromGnuPG )
+{
+ /* the pipe ppass is used for to pass the password to
+ * pgp. passing the password together with the normal input through
+ * stdin doesn't seem to work as expected (at least for pgp5.0)
+ */
+ char str[1025] = "\0";
+ int pin[2], pout[2], perr[2], ppass[2];
+ int len, len2;
+ FILE *pass;
+ pid_t child_pid;
+ int childExitStatus;
+ char gpgcmd[1024] = "\0";
+ struct pollfd poller[3];
+ int num_pollers = 0;
+ const int STD_OUT = 0;
+ const int STD_ERR = 1;
+ const int STD_IN = 2;
+ int pollstatus;
+
+ if(passphrase)
+ {
+ pipe(ppass);
+
+ pass = fdopen(ppass[1], "w");
+ fwrite(passphrase, sizeof(char), strlen(passphrase), pass);
+ fwrite("\n", sizeof(char), 1, pass);
+ fclose(pass);
+ close(ppass[1]);
+
+ //Uncomment these lines for testing only! Doing so will decrease security!
+ //kdDebug(5100) << "pass = " << passphrase << endl;
+ }
+
+ //Uncomment these lines for testing only! Doing so will decrease security!
+ //kdDebug(5100) << "pgp cmd = " << cmd << endl;
+ //kdDebug(5100) << "pgp input = " << QString(input)
+ // << "input length = " << input.length() << endl;
+
+ error = "";
+ output = "";
+
+ pipe(pin);
+ pipe(pout);
+ pipe(perr);
+
+ if( passphrase ) {
+ if( mVersion >= "1.0.7" ) {
+ // GnuPG >= 1.0.7 supports the gpg-agent, so we look for it.
+ if( 0 == getenv("GPG_AGENT_INFO") ) {
+ // gpg-agent not found, so we tell gpg not to use the agent
+ snprintf( gpgcmd, 1023,
+ "LANGUAGE=C gpg --no-use-agent --passphrase-fd %d %s",
+ ppass[0], cmd );
+ }
+ else {
+ // gpg-agent seems to be running, so we tell gpg to use the agent
+ snprintf( gpgcmd, 1023,
+ "LANGUAGE=C gpg --use-agent %s",
+ cmd );
+ }
+ }
+ else {
+ // GnuPG < 1.0.7 doesn't know anything about the gpg-agent
+ snprintf( gpgcmd, 1023,
+ "LANGUAGE=C gpg --passphrase-fd %d %s",
+ ppass[0], cmd );
+ }
+ }
+ else {
+ snprintf(gpgcmd, 1023, "LANGUAGE=C gpg %s",cmd);
+ }
+
+ QApplication::flushX();
+ if(!(child_pid = fork()))
+ {
+ /*We're the child.*/
+ close(pin[1]);
+ dup2(pin[0], 0);
+ close(pin[0]);
+
+ close(pout[0]);
+ dup2(pout[1], 1);
+ close(pout[1]);
+
+ close(perr[0]);
+ dup2(perr[1], 2);
+ close(perr[1]);
+
+ //#warning FIXME: there has to be a better way to do this
+ /* this is nasty nasty nasty (but it works) */
+ if( passphrase ) {
+ if( mVersion >= "1.0.7" ) {
+ // GnuPG >= 1.0.7 supports the gpg-agent, so we look for it.
+ if( 0 == getenv("GPG_AGENT_INFO") ) {
+ // gpg-agent not found, so we tell gpg not to use the agent
+ snprintf( gpgcmd, 1023,
+ "LANGUAGE=C gpg --no-use-agent --passphrase-fd %d %s",
+ ppass[0], cmd );
+ }
+ else {
+ // gpg-agent seems to be running, so we tell gpg to use the agent
+ snprintf( gpgcmd, 1023,
+ "LANGUAGE=C gpg --use-agent %s",
+ cmd );
+ }
+ }
+ else {
+ // GnuPG < 1.0.7 doesn't know anything about the gpg-agent
+ snprintf( gpgcmd, 1023,
+ "LANGUAGE=C gpg --passphrase-fd %d %s",
+ ppass[0], cmd );
+ }
+ }
+ else {
+ snprintf(gpgcmd, 1023, "LANGUAGE=C gpg %s",cmd);
+ }
+
+ kdDebug(5100) << "pgp cmd = " << gpgcmd << endl;
+
+ execl("/bin/sh", "sh", "-c", gpgcmd, (void *)0);
+ _exit(127);
+ }
+
+ // Only get here if we're the parent.
+
+ close(pin[0]);
+ close(pout[1]);
+ close(perr[1]);
+
+ // poll for "There is data to read."
+ poller[STD_OUT].fd = pout[0];
+ poller[STD_OUT].events = POLLIN;
+ poller[STD_ERR].fd = perr[0];
+ poller[STD_ERR].events = POLLIN;
+ num_pollers = 2;
+
+ if (!onlyReadFromGnuPG) {
+ // poll for "Writing now will not block."
+ poller[STD_IN].fd = pin[1];
+ poller[STD_IN].events = POLLOUT;
+ num_pollers = 3;
+ } else {
+ close (pin[1]);
+ pin[1] = -1;
+ }
+
+ pid_t waitpidRetVal;
+ unsigned int input_pos = 0;
+
+ do {
+ //kdDebug(5100) << "Checking if GnuPG is still running..." << endl;
+ childExitStatus = 0;
+ waitpidRetVal = waitpid(child_pid, &childExitStatus, WNOHANG);
+ //kdDebug(5100) << "waitpid returned " << waitpidRetVal << endl;
+ do {
+ // poll the pipes
+ pollstatus = poll(poller, num_pollers, 10);
+ if( 0 < pollstatus ) {
+ // Check stdout.
+ if (poller[STD_OUT].revents & POLLIN) {
+ //kdDebug(5100) << "Trying to read " << 1024 << " bytes from pout[0]" << endl;
+ if ((len = read(pout[0],str,1024))>0) {
+ //kdDebug(5100) << "Read " << len << " bytes from pout[0]" << endl;
+ str[len] ='\0';
+ output += str;
+ }
+ else {
+ // FreeBSD/NetBSD workaround
+ //
+ // Apparently, on Free/NetBSD when the child dies, the pipe begins
+ // receiving empty data packets *before* waitpid() has signaled
+ // that the child has died. Also, notice that this happens
+ // without any error bit being set in pollfd.revents (is this a
+ // Free/NetBSD bug??? ). Notice that these anomalous packets exist
+ // according to poll(), but have length 0 according to read().
+ // Thus, we can remain stuck inside this loop.
+ //
+ // A solution to this problem is to get out of the inner loop
+ // when read() returns <=0. In this way, we have another chance
+ // to call waitpid() to check if the child has died -- and this
+ // time the call should succeed.
+ //
+ // Set POLLHUP in pollfd.revents to signal that something strange
+ // has happened and disable polling of stdout.
+ poller[STD_OUT].revents |= POLLHUP;
+ poller[STD_OUT].events = 0;
+ }
+ } else if (poller[STD_OUT].revents & POLLHUP) {
+ // disable polling of stdout
+ poller[STD_OUT].events = 0;
+ }
+
+ // Check stderr.
+ if (poller[STD_ERR].revents & POLLIN) {
+ //kdDebug(5100) << "Trying to read " << 1024 << " bytes from perr[0]" << endl;
+ if ((len = read(poller[STD_ERR].fd,str,1024))>0) {
+ //kdDebug(5100) << "Read " << len << " bytes from perr[0]" << endl;
+ str[len] ='\0';
+ error += str;
+ }
+ else {
+ // FreeBSD/NetBSD workaround (for details see above)
+ poller[STD_ERR].revents |= POLLHUP;
+ poller[STD_ERR].events = 0;
+ }
+ } else if (poller[STD_ERR].revents & POLLHUP) {
+ // disable polling of stderr
+ poller[STD_ERR].events = 0;
+ }
+
+ if (num_pollers > 2) {
+ if (poller[STD_IN].revents & ( POLLERR | POLLHUP ) ) {
+ kdDebug(5100) << "GnuPG seems to have hung up" << endl;
+ close (pin[1]);
+ pin[1] = -1;
+ --num_pollers;
+ }
+ else if (poller[STD_IN].revents & POLLOUT) {
+ if (!input.isEmpty()) {
+ // search end of next line
+ if ((len2 = input.find('\n', input_pos)) == -1)
+ len2 = input.length()-input_pos;
+ else
+ len2 = len2-input_pos+1;
+
+ //kdDebug(5100) << "Trying to write " << len2 << " bytes to pin[1] ..." << endl;
+ len2 = write(pin[1], input.mid(input_pos,len2).data(), len2);
+ //kdDebug(5100) << "Wrote " << len2 << " bytes to pin[1] ..." << endl;
+ input_pos += len2;
+
+ // We are done.
+ if (input_pos >= input.length()) {
+ //kdDebug(5100) << "All input was written to pin[1]" << endl;
+ close (pin[1]);
+ pin[1] = -1;
+ --num_pollers;
+ }
+ }
+ else { // if input.isEmpty()
+ write(pin[1], "\n", 1);
+ //kdDebug(5100) << "All input was written to pin[1]" << endl;
+ close (pin[1]);
+ pin[1] = -1;
+ --num_pollers;
+ }
+ }
+ }
+ }
+ } while ( (pollstatus > 0) && ( (num_pollers > 2)
+ || (poller[STD_OUT].events != 0)
+ || (poller[STD_ERR].events != 0) ) );
+
+ if (pollstatus == -1) {
+ kdDebug(5100) << "GnuPG poll failed, errno: " << errno << endl;
+ }
+
+ } while(waitpidRetVal == 0);
+
+ if( 0 <= pin[1] )
+ close (pin[1]);
+ close(pout[0]);
+ close(perr[0]);
+
+ if(passphrase)
+ close(ppass[0]);
+
+ // Did the child exit normally?
+ if (WIFEXITED(childExitStatus) != 0) {
+ // Get the return code of the child
+ childExitStatus = WEXITSTATUS(childExitStatus);
+ kdDebug(5100) << "GnuPG exited with exit status " << childExitStatus
+ << endl;
+ }
+ else {
+ childExitStatus = -1;
+ kdDebug(5100) << "GnuPG exited abnormally!" << endl;
+ }
+
+ //Uncomment these lines for testing only! Doing so will decrease security!
+ //kdDebug(5100) << "gpg stdout:\n" << QString(output) << endl;
+
+ // Make the information visible, so that a user can
+ // get to know what's going on during the gpg calls.
+ kdDebug(5100) << "gpg stderr:\n" << error << endl;
+
+ return childExitStatus;
+}
+
+
+QCString
+Base::addUserId()
+{
+ QCString cmd;
+ QCString pgpUser = Module::getKpgp()->user();
+
+ if(!pgpUser.isEmpty())
+ {
+ cmd += " -u 0x";
+ cmd += pgpUser;
+ return cmd;
+ }
+ return QCString();
+}
+
+
+} // namespace Kpgp
diff --git a/libkpgp/kpgpbase.h b/libkpgp/kpgpbase.h
new file mode 100644
index 00000000..f0f1d190
--- /dev/null
+++ b/libkpgp/kpgpbase.h
@@ -0,0 +1,239 @@
+/*
+ kpgpbase.h
+
+ Copyright (C) 2001,2002 the KPGP authors
+ See file AUTHORS.kpgp for details
+
+ This file is part of KPGP, the KDE PGP/GnuPG support library.
+
+ KPGP is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#ifndef KPGPBASE_H
+#define KPGPBASE_H
+
+#include <qstring.h>
+#include <qcstring.h>
+#include <qstringlist.h>
+
+#include "kpgpkey.h"
+#include "kpgpblock.h"
+
+namespace Kpgp {
+
+class Base
+{
+public:
+
+ /** virtual class used internally by kpgp */
+ Base();
+ virtual ~Base();
+
+
+ /** Encrypts the message with the given keys. */
+ virtual int encrypt( Block& , const KeyIDList& ) { return OK; }
+
+ /** Clearsigns the message with the currently set key. */
+ virtual int clearsign( Block& , const char *) { return OK; }
+
+ /** Encrypts and signs the message with the given keys. */
+ virtual int encsign( Block& , const KeyIDList& ,
+ const char * = 0) { return OK; }
+
+ /** Decrypts the message. */
+ virtual int decrypt( Block& , const char * = 0) { return OK; }
+
+ /** Verifies the message. */
+ virtual int verify( Block& block ) { return decrypt( block, 0 ); }
+
+
+ /** Reads the key data for the given key and returns it. If
+ <em>readTrust</em> is true then the trust of this key will be determined.
+ If <em>key</em> is not null then the key data will be stored in given
+ key.
+ */
+ virtual Key* readPublicKey( const KeyID&, const bool = false,
+ Key* = 0 )
+ { return 0; }
+
+ /** Returns the list of public keys in the users public keyring. */
+ virtual KeyList publicKeys( const QStringList & = QStringList() )
+ { return KeyList(); }
+
+ /** Returns the list of secret keys in the users secret keyring. */
+ virtual KeyList secretKeys( const QStringList & = QStringList() )
+ { return KeyList(); }
+
+ /** Returns the ascii armored data of the public key with the
+ given key id. */
+ virtual QCString getAsciiPublicKey(const KeyID& ) { return QCString(); }
+
+ /** Signs the given key with the currently set user key. This is currently
+ not implemented. */
+ virtual int signKey(const KeyID& , const char *) { return OK; }
+
+
+ /** Returns an error message if an error occurred during the last
+ operation. */
+ virtual QString lastErrorMessage() const;
+
+
+protected:
+ virtual int run( const char *cmd, const char *passphrase = 0,
+ bool onlyReadFromPGP = false );
+ virtual int runGpg( const char *cmd, const char *passphrase = 0,
+ bool onlyReadFromGnuPG = false );
+ virtual void clear();
+
+ QCString addUserId();
+
+ QCString input;
+ QCString output;
+ QCString error;
+ QString errMsg;
+
+ QCString mVersion;
+
+ int status;
+
+};
+
+// ---------------------------------------------------------------------------
+
+class Base2 : public Base
+{
+
+public:
+ Base2();
+ virtual ~Base2();
+
+ virtual int encrypt( Block& block, const KeyIDList& recipients );
+ virtual int clearsign( Block& block, const char *passphrase );
+ virtual int encsign( Block& block, const KeyIDList& recipients,
+ const char *passphrase = 0 );
+ virtual int decrypt( Block& block, const char *passphrase = 0 );
+ virtual int verify( Block& block ) { return decrypt( block, 0 ); }
+
+ virtual Key* readPublicKey( const KeyID& keyID,
+ const bool readTrust = false,
+ Key* key = 0 );
+ virtual KeyList publicKeys( const QStringList & patterns = QStringList() );
+ virtual KeyList secretKeys( const QStringList & patterns = QStringList() );
+ virtual QCString getAsciiPublicKey( const KeyID& keyID );
+ virtual int signKey( const KeyID& keyID, const char *passphrase );
+
+protected:
+ KeyList doGetPublicKeys( const QCString & cmd,
+ const QStringList & patterns );
+ virtual KeyList parseKeyList( const QCString&, bool );
+
+private:
+ Key* parsePublicKeyData( const QCString& output, Key* key = 0 );
+ void parseTrustDataForKey( Key* key, const QCString& str );
+};
+
+class BaseG : public Base
+{
+
+public:
+ BaseG();
+ virtual ~BaseG();
+
+ virtual int encrypt( Block& block, const KeyIDList& recipients );
+ virtual int clearsign( Block& block, const char *passphrase );
+ virtual int encsign( Block& block, const KeyIDList& recipients,
+ const char *passphrase = 0 );
+ virtual int decrypt( Block& block, const char *passphrase = 0 );
+ virtual int verify( Block& block ) { return decrypt( block, 0 ); }
+
+ virtual Key* readPublicKey( const KeyID& keyID,
+ const bool readTrust = false,
+ Key* key = 0 );
+ virtual KeyList publicKeys( const QStringList & patterns = QStringList() );
+ virtual KeyList secretKeys( const QStringList & patterns = QStringList() );
+ virtual QCString getAsciiPublicKey( const KeyID& keyID );
+ virtual int signKey( const KeyID& keyID, const char *passphrase );
+
+private:
+ Key* parseKeyData( const QCString& output, int& offset, Key* key = 0 );
+ KeyList parseKeyList( const QCString&, bool );
+};
+
+
+class Base5 : public Base
+{
+
+public:
+ Base5();
+ virtual ~Base5();
+
+ virtual int encrypt( Block& block, const KeyIDList& recipients );
+ virtual int clearsign( Block& block, const char *passphrase );
+ virtual int encsign( Block& block, const KeyIDList& recipients,
+ const char *passphrase = 0 );
+ virtual int decrypt( Block& block, const char *passphrase = 0 );
+ virtual int verify( Block& block ) { return decrypt( block, 0 ); }
+
+ virtual Key* readPublicKey( const KeyID& keyID,
+ const bool readTrust = false,
+ Key* key = 0 );
+ virtual KeyList publicKeys( const QStringList & patterns = QStringList() );
+ virtual KeyList secretKeys( const QStringList & patterns = QStringList() );
+ virtual QCString getAsciiPublicKey( const KeyID& keyID );
+ virtual int signKey( const KeyID& keyID, const char *passphrase );
+
+private:
+ Key* parseKeyData( const QCString& output, int& offset, Key* key = 0 );
+ Key* parseSingleKey( const QCString& output, Key* key = 0 );
+ KeyList parseKeyList( const QCString& output, bool );
+ void parseTrustDataForKey( Key* key, const QCString& str );
+};
+
+
+class Base6 : public Base2
+{
+
+public:
+ Base6();
+ virtual ~Base6();
+
+ virtual int decrypt( Block& block, const char *passphrase = 0 );
+ virtual int verify( Block& block ) { return decrypt( block, 0 ); }
+
+ virtual Key* readPublicKey( const KeyID& keyID,
+ const bool readTrust = false,
+ Key* key = 0 );
+ virtual KeyList publicKeys( const QStringList & patterns = QStringList() );
+ virtual KeyList secretKeys( const QStringList & patterns = QStringList() );
+
+ virtual int isVersion6();
+
+protected:
+ virtual KeyList parseKeyList( const QCString &, bool );
+
+private:
+ Key* parseKeyData( const QCString& output, int& offset, Key* key = 0 );
+ Key* parseSingleKey( const QCString& output, Key* key = 0 );
+ void parseTrustDataForKey( Key* key, const QCString& str );
+};
+
+// ---------------------------------------------------------------------------
+// inlined functions
+
+inline QString
+Base::lastErrorMessage() const
+{
+ return errMsg;
+}
+
+
+} // namespace Kpgp
+
+#endif
diff --git a/libkpgp/kpgpbase2.cpp b/libkpgp/kpgpbase2.cpp
new file mode 100644
index 00000000..c4fd0358
--- /dev/null
+++ b/libkpgp/kpgpbase2.cpp
@@ -0,0 +1,1105 @@
+/*
+ kpgpbase2.cpp
+
+ Copyright (C) 2001,2002 the KPGP authors
+ See file AUTHORS.kpgp for details
+
+ This file is part of KPGP, the KDE PGP/GnuPG support library.
+
+ KPGP is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "kpgpbase.h"
+#include "kpgp.h"
+
+#include <string.h> /* strncmp */
+#include <assert.h>
+
+#include <qdatetime.h>
+
+#include <klocale.h>
+#include <kprocess.h>
+#include <kdebug.h>
+
+#define PGP2 "pgp"
+
+namespace Kpgp {
+
+Base2::Base2()
+ : Base()
+{
+}
+
+
+Base2::~Base2()
+{
+}
+
+
+int
+Base2::encrypt( Block& block, const KeyIDList& recipients )
+{
+ return encsign( block, recipients, 0 );
+}
+
+
+int
+Base2::clearsign( Block& block, const char *passphrase )
+{
+ return encsign( block, KeyIDList(), passphrase );
+}
+
+
+int
+Base2::encsign( Block& block, const KeyIDList& recipients,
+ const char *passphrase )
+{
+ QCString cmd;
+ int exitStatus = 0;
+
+ if(!recipients.isEmpty() && passphrase != 0)
+ cmd = PGP2 " +batchmode +language=en +verbose=1 -seat";
+ else if(!recipients.isEmpty())
+ cmd = PGP2 " +batchmode +language=en +verbose=1 -eat";
+ else if(passphrase != 0)
+ cmd = PGP2 " +batchmode +language=en +verbose=1 -sat";
+ else
+ {
+ kdDebug(5100) << "kpgpbase: Neither recipients nor passphrase specified." << endl;
+ return OK;
+ }
+
+ if(passphrase != 0)
+ cmd += addUserId();
+
+ if(!recipients.isEmpty()) {
+ if(Module::getKpgp()->encryptToSelf())
+ {
+ cmd += " 0x";
+ cmd += Module::getKpgp()->user();
+ }
+
+ for( KeyIDList::ConstIterator it = recipients.begin();
+ it != recipients.end(); ++it ) {
+ cmd += " 0x";
+ cmd += (*it);
+ }
+ }
+ cmd += " -f";
+
+ clear();
+ input = block.text();
+ exitStatus = run(cmd.data(), passphrase);
+ if( !output.isEmpty() )
+ block.setProcessedText( output );
+ block.setError( error );
+
+ if(exitStatus != 0)
+ status = ERROR;
+
+#if 0
+ // #### FIXME: As we check the keys ourselves the following problems
+ // shouldn't occur. Therefore I don't handle them for now.
+ // IK 01/2002
+ if(!recipients.isEmpty())
+ {
+ int index = 0;
+ bool bad = FALSE;
+ unsigned int num = 0;
+ QCString badkeys = "";
+ if (error.find("Cannot find the public key") != -1)
+ {
+ index = 0;
+ num = 0;
+ while((index = error.find("Cannot find the public key",index))
+ != -1)
+ {
+ bad = TRUE;
+ index = error.find('\'',index);
+ int index2 = error.find('\'',index+1);
+ if (num++)
+ badkeys += ", ";
+ badkeys += error.mid(index, index2-index+1);
+ }
+ if(bad)
+ {
+ badkeys.stripWhiteSpace();
+ if(num == recipients.count())
+ errMsg = i18n("Could not find public keys matching the userid(s)\n"
+ "%1;\n"
+ "the message is not encrypted.")
+ .arg( badkeys.data() );
+ else
+ errMsg = i18n("Could not find public keys matching the userid(s)\n"
+ "%1;\n"
+ "these persons will not be able to read the message.")
+ .arg( badkeys.data() );
+ status |= MISSINGKEY;
+ status |= ERROR;
+ }
+ }
+ if (error.find("skipping userid") != -1)
+ {
+ index = 0;
+ num = 0;
+ while((index = error.find("skipping userid",index))
+ != -1)
+ {
+ bad = TRUE;
+ int index2 = error.find('\n',index+16);
+ if (num++)
+ badkeys += ", ";
+ badkeys += error.mid(index+16, index2-index-16);
+ index = index2;
+ }
+ if(bad)
+ {
+ badkeys.stripWhiteSpace();
+ if(num == recipients.count())
+ errMsg = i18n("Public keys not certified with trusted signature "
+ "for userid(s)\n"
+ "%1.\n"
+ "The message is not encrypted.")
+ .arg( badkeys.data() );
+ else
+ errMsg = i18n("Public keys not certified with trusted signature "
+ "for userid(s)\n"
+ "%1;\n"
+ "these persons will not be able to read the message.")
+ .arg( badkeys.data() );
+ status |= BADKEYS;
+ status |= ERROR;
+ return status;
+ }
+ }
+ }
+#endif
+ if(passphrase != 0)
+ {
+ if(error.find("Pass phrase is good") != -1)
+ {
+ //kdDebug(5100) << "Base: Good Passphrase!" << endl;
+ status |= SIGNED;
+ }
+ if( error.find("Bad pass phrase") != -1)
+ {
+ errMsg = i18n("Bad passphrase; could not sign.");
+ status |= BADPHRASE;
+ status |= ERR_SIGNING;
+ status |= ERROR;
+ }
+ }
+ if (error.find("Signature error") != -1)
+ {
+ errMsg = i18n("Signing failed: please check your PGP User Identity, "
+ "the PGP setup, and the key rings.");
+ status |= NO_SEC_KEY;
+ status |= ERR_SIGNING;
+ status |= ERROR;
+ }
+ if (error.find("Encryption error") != -1)
+ {
+ errMsg = i18n("Encryption failed: please check your PGP setup "
+ "and the key rings.");
+ status |= NO_SEC_KEY;
+ status |= BADKEYS;
+ status |= ERROR;
+ }
+
+ //kdDebug(5100) << "status = " << status << endl;
+ block.setStatus( status );
+ return status;
+}
+
+
+int
+Base2::decrypt( Block& block, const char *passphrase )
+{
+ int index, index2;
+ int exitStatus = 0;
+
+ clear();
+ input = block.text();
+ exitStatus = run(PGP2 " +batchmode +language=en -f", passphrase);
+ if( !output.isEmpty() )
+ block.setProcessedText( output );
+ block.setError( error );
+
+ // pgp2.6 has sometimes problems with the ascii armor pgp5.0 produces
+ // this hack can solve parts of the problem
+ if(error.find("ASCII armor corrupted.") != -1)
+ {
+ kdDebug(5100) << "removing ASCII armor header" << endl;
+ int index1 = input.find("-----BEGIN PGP SIGNED MESSAGE-----");
+ if(index1 != -1)
+ index1 = input.find("-----BEGIN PGP SIGNATURE-----", index1);
+ else
+ index1 = input.find("-----BEGIN PGP MESSAGE-----");
+ index1 = input.find('\n', index1);
+ index2 = input.find("\n\n", index1);
+ input.remove(index1, index2 - index1);
+ exitStatus = run(PGP2 " +batchmode +language=en -f", passphrase);
+ if( !output.isEmpty() )
+ block.setProcessedText( output );
+ block.setError( error );
+ }
+
+ if(exitStatus == -1) {
+ errMsg = i18n("error running PGP");
+ status = RUN_ERR;
+ block.setStatus( status );
+ return status;
+ }
+
+ /* Example No.1 (PGP 2.6.3in):
+ * File is encrypted. Secret key is required to read it.
+ * Key for user ID: Test Key (only for testing) <testkey@ingo-kloecker.de>
+ * 1024-bit key, key ID E2D074D3, created 2001/09/09
+ *
+ * Error: Bad pass phrase.
+ *
+ * This message can only be read by:
+ * Test key without secret key (for testing only) <nosectestkey@ingo-kloecker.de>
+ * Test Key (only for testing) <testkey@ingo-kloecker.de>
+ *
+ * You do not have the secret key needed to decrypt this file.
+ */
+ /* Example No.2 (PGP 2.6.3in):
+ * File is encrypted. Secret key is required to read it.
+ * This message can only be read by:
+ * Test key without secret key (for testing only) <nosectestkey@ingo-kloecker.de>
+ *
+ * You do not have the secret key needed to decrypt this file.
+ */
+ if(error.find("File is encrypted.") != -1)
+ {
+ //kdDebug(5100) << "kpgpbase: message is encrypted" << endl;
+ status |= ENCRYPTED;
+ if((index = error.find("Key for user ID:")) != -1)
+ {
+ // Find out the key for which the phrase is needed
+ index += 17;
+ index2 = error.find('\n', index);
+ block.setRequiredUserId( error.mid(index, index2 - index) );
+ //kdDebug(5100) << "Base: key needed is \"" << block.requiredUserId() << "\"!\n";
+
+ if((passphrase != 0) && (error.find("Bad pass phrase") != -1))
+ {
+ errMsg = i18n("Bad passphrase; could not decrypt.");
+ kdDebug(5100) << "Base: passphrase is bad" << endl;
+ status |= BADPHRASE;
+ status |= ERROR;
+ }
+ }
+ else
+ {
+ // no secret key fitting this message
+ status |= NO_SEC_KEY;
+ status |= ERROR;
+ errMsg = i18n("You do not have the secret key needed to decrypt this message.");
+ kdDebug(5100) << "Base: no secret key for this message" << endl;
+ }
+ // check for persons
+#if 0
+ // ##### FIXME: This information is anyway currently not used
+ // I'll change it to always determine the recipients.
+ index = error.find("can only be read by:");
+ if(index != -1)
+ {
+ index = error.find('\n',index);
+ int end = error.find("\n\n",index);
+
+ mRecipients.clear();
+ while( (index2 = error.find('\n',index+1)) <= end )
+ {
+ QCString item = error.mid(index+1,index2-index-1);
+ item.stripWhiteSpace();
+ mRecipients.append(item);
+ index = index2;
+ }
+ }
+#endif
+ }
+
+ // handle signed message
+
+ // Examples (made with PGP 2.6.3in)
+ /* Example No. 1 (signed with unknown key):
+ * File has signature. Public key is required to check signature.
+ *
+ * Key matching expected Key ID 12345678 not found in file '/home/user/.pgp/pubring.pgp'.
+ *
+ * WARNING: Can't find the right public key-- can't check signature integrity.
+ */
+ /* Example No. 2 (bad signature):
+ * File has signature. Public key is required to check signature.
+ * ..
+ * WARNING: Bad signature, doesn't match file contents!
+ *
+ * Bad signature from user "Joe User <joe@foo.bar>".
+ * Signature made 2001/09/09 16:01 GMT using 1024-bit key, key ID 12345678
+ */
+ /* Example No. 3.1 (good signature with untrusted key):
+ * File has signature. Public key is required to check signature.
+ * .
+ * Good signature from user "Joe User <joe@foo.bar>".
+ * Signature made 2001/09/09 16:01 GMT using 1024-bit key, key ID 12345678
+ *
+ * WARNING: Because this public key is not certified with a trusted
+ * signature, it is not known with high confidence that this public key
+ * actually belongs to: "Joe User <joe@foo.bar>".
+ */
+ /* Example No. 3.2 (good signature with untrusted key):
+ * File has signature. Public key is required to check signature.
+ * .
+ * Good signature from user "Joe User <joe@foo.bar>".
+ * Signature made 2001/09/09 16:01 GMT using 1024-bit key, key ID 12345678
+ *
+ * WARNING: Because this public key is not certified with enough trusted
+ * signatures, it is not known with high confidence that this public key
+ * actually belongs to: "Joe User <joe@foo.bar>".
+ */
+ /* Example No. 4 (good signature with revoked key):
+ * File has signature. Public key is required to check signature.
+ * .
+ * Good signature from user "Joe User <joe@foo.bar>".
+ * Signature made 2001/09/09 16:01 GMT using 1024-bit key, key ID 12345678
+ *
+ *
+ * Key for user ID: Joe User <joe@foo.bar>
+ * 1024-bit key, key ID 12345678, created 2001/09/09
+ * Key has been revoked.
+ *
+ * WARNING: This key has been revoked by its owner,
+ * possibly because the secret key was compromised.
+ * This could mean that this signature is a forgery.
+ */
+ /* Example No. 5 (good signature with trusted key):
+ * File has signature. Public key is required to check signature.
+ * .
+ * Good signature from user "Joe User <joe@foo.bar>".
+ * Signature made 2001/09/09 16:01 GMT using 1024-bit key, key ID 12345678
+ */
+
+ if((index = error.find("File has signature")) != -1)
+ {
+ // move index to start of next line
+ index = error.find('\n', index+18) + 1;
+ //kdDebug(5100) << "Base: message is signed" << endl;
+ status |= SIGNED;
+ // get signature date and signature key ID
+ if ((index2 = error.find("Signature made", index)) != -1) {
+ index2 += 15;
+ int index3 = error.find("using", index2);
+ block.setSignatureDate( error.mid(index2, index3-index2-1) );
+ kdDebug(5100) << "Message was signed on '" << block.signatureDate() << "'\n";
+ index3 = error.find("key ID ", index3) + 7;
+ block.setSignatureKeyId( error.mid(index3,8) );
+ kdDebug(5100) << "Message was signed with key '" << block.signatureKeyId() << "'\n";
+ }
+ else {
+ // if pgp can't find the keyring it unfortunately doesn't print
+ // the signature date and key ID
+ block.setSignatureDate( "" );
+ block.setSignatureKeyId( "" );
+ }
+
+ if( ( index2 = error.find("Key matching expected", index) ) != -1)
+ {
+ status |= UNKNOWN_SIG;
+ status |= GOODSIG;
+ int index3 = error.find("Key ID ", index2) + 7;
+ block.setSignatureKeyId( error.mid(index3,8) );
+ block.setSignatureUserId( QString::null );
+ }
+ else if( (index2 = error.find("Good signature from", index)) != -1 )
+ {
+ status |= GOODSIG;
+ // get signer
+ index = error.find('"',index2+19);
+ index2 = error.find('"', index+1);
+ block.setSignatureUserId( error.mid(index+1, index2-index-1) );
+ }
+ else if( (index2 = error.find("Bad signature from", index)) != -1 )
+ {
+ status |= ERROR;
+ // get signer
+ index = error.find('"',index2+19);
+ index2 = error.find('"', index+1);
+ block.setSignatureUserId( error.mid(index+1, index2-index-1) );
+ }
+ else if( error.find("Keyring file", index) != -1 )
+ {
+ // #### fix this hack
+ status |= UNKNOWN_SIG;
+ status |= GOODSIG; // this is a hack...
+ // determine file name of missing keyring file
+ index = error.find('\'', index) + 1;
+ index2 = error.find('\'', index);
+ block.setSignatureUserId( i18n("The keyring file %1 does not exist.\n"
+ "Please check your PGP setup.").arg(error.mid(index, index2-index)) );
+ }
+ else
+ {
+ status |= ERROR;
+ block.setSignatureUserId( i18n("Unknown error") );
+ }
+ }
+ //kdDebug(5100) << "status = " << status << endl;
+ block.setStatus( status );
+ return status;
+}
+
+
+Key*
+Base2::readPublicKey( const KeyID& keyID,
+ const bool readTrust /* = false */,
+ Key* key /* = 0 */ )
+{
+ int exitStatus = 0;
+
+ status = 0;
+ exitStatus = run( PGP2 " +batchmode +language=en +verbose=0 -kvc -f 0x" +
+ keyID, 0, true );
+
+ if(exitStatus != 0) {
+ status = ERROR;
+ return 0;
+ }
+
+ key = parsePublicKeyData( output, key );
+
+ if( key == 0 )
+ {
+ return 0;
+ }
+
+ if( readTrust )
+ {
+ exitStatus = run( PGP2 " +batchmode +language=en +verbose=0 -kc -f",
+ 0, true );
+
+ if(exitStatus != 0) {
+ status = ERROR;
+ return 0;
+ }
+
+ parseTrustDataForKey( key, error );
+ }
+
+ return key;
+}
+
+
+KeyList
+Base2::publicKeys( const QStringList & patterns )
+{
+ return doGetPublicKeys( PGP2 " +batchmode +language=en +verbose=0 -kvc -f",
+ patterns );
+}
+
+KeyList
+Base2::doGetPublicKeys( const QCString & cmd, const QStringList & patterns )
+{
+ int exitStatus = 0;
+ KeyList publicKeys;
+
+ status = 0;
+ if ( patterns.isEmpty() ) {
+ exitStatus = run( cmd, 0, true );
+
+ if ( exitStatus != 0 ) {
+ status = ERROR;
+ return KeyList();
+ }
+
+ // now we need to parse the output for public keys
+ publicKeys = parseKeyList( output, false );
+ }
+ else {
+ typedef QMap<QCString, Key*> KeyMap;
+ KeyMap map;
+
+ for ( QStringList::ConstIterator it = patterns.begin();
+ it != patterns.end(); ++it ) {
+ exitStatus = run( cmd + " " + KProcess::quote( *it ).local8Bit(),
+ 0, true );
+
+ if ( exitStatus != 0 ) {
+ status = ERROR;
+ return KeyList();
+ }
+
+ // now we need to parse the output for public keys
+ publicKeys = parseKeyList( output, false );
+
+ // put all new keys into a map, remove duplicates
+ while ( !publicKeys.isEmpty() ) {
+ Key * key = publicKeys.take( 0 );
+ if ( !map.contains( key->primaryFingerprint() ) )
+ map.insert( key->primaryFingerprint(), key );
+ else
+ delete key;
+ }
+ }
+ // build list from the map
+ for ( KeyMap::ConstIterator it = map.begin(); it != map.end(); ++it ) {
+ publicKeys.append( it.data() );
+ }
+ }
+
+ // sort the list of public keys
+ publicKeys.sort();
+
+ return publicKeys;
+}
+
+KeyList
+Base2::secretKeys( const QStringList & patterns )
+{
+ return publicKeys( patterns );
+}
+
+
+int
+Base2::signKey(const KeyID& keyID, const char *passphrase)
+{
+ QCString cmd;
+ int exitStatus = 0;
+
+ cmd = PGP2 " +batchmode +language=en -ks -f ";
+ cmd += addUserId();
+ cmd += " 0x" + keyID;
+
+ status = 0;
+ exitStatus = run(cmd.data(),passphrase);
+
+ if (exitStatus != 0)
+ status = ERROR;
+
+ return status;
+}
+
+
+QCString Base2::getAsciiPublicKey(const KeyID& keyID)
+{
+ int exitStatus = 0;
+
+ if (keyID.isEmpty())
+ return QCString();
+
+ status = 0;
+ exitStatus = run( PGP2 " +batchmode +force +language=en -kxaf 0x" + keyID,
+ 0, true );
+
+ if(exitStatus != 0) {
+ status = ERROR;
+ return QCString();
+ }
+
+ return output;
+}
+
+
+Key*
+Base2::parsePublicKeyData( const QCString& output, Key* key /* = 0 */ )
+{
+ Subkey *subkey = 0;
+ int index;
+
+ // search start of key data
+ if( !strncmp( output.data(), "pub", 3 ) ||
+ !strncmp( output.data(), "sec", 3 ) )
+ index = 0;
+ else
+ {
+ /*
+ if( secretKeys )
+ index = output.find( "\nsec" );
+ else
+ */
+ index = output.find( "\npub" );
+ if( index == -1 )
+ return 0;
+ else
+ index++;
+ }
+
+ while( true )
+ {
+ int index2;
+
+ // search the end of the current line
+ if( ( index2 = output.find( '\n', index ) ) == -1 )
+ break;
+
+ if( !strncmp( output.data() + index, "pub", 3 ) ||
+ !strncmp( output.data() + index, "sec", 3 ) )
+ { // line contains primary key data
+ // Example 1 (nothing special):
+ // pub 1024/E2D074D3 2001/09/09 Test Key <testkey@xyz>
+ // Example 2 (disabled key):
+ // pub- 1024/8CCB2C1B 2001/11/04 Disabled Test Key <disabled@xyz>
+ // Example 3 (expired key):
+ // pub> 1024/7B94827D 2001/09/09 Expired Test Key <expired@xyz> (EXPIRE:2001-09-10)
+ // Example 4 (revoked key):
+ // pub 1024/956721F9 2001/09/09 *** KEY REVOKED ***
+
+ int pos, pos2;
+
+ if( key == 0 )
+ key = new Key();
+ else
+ key->clear();
+ /*key->setSecret( secretKeys );*/
+ // set default key capabilities
+ key->setCanEncrypt( true );
+ key->setCanSign( true );
+ key->setCanCertify( true );
+
+ /*subkey = new Subkey( "", secretKeys );*/
+ subkey = new Subkey( "", false );
+ key->addSubkey( subkey );
+ // set default key capabilities
+ subkey->setCanEncrypt( true );
+ subkey->setCanSign( true );
+ subkey->setCanCertify( true );
+ // expiration date defaults to never
+ subkey->setExpirationDate( -1 );
+
+ // Key Flags
+ switch( output[index+3] )
+ {
+ case ' ': // nothing special
+ break;
+ case '-': // disabled key
+ subkey->setDisabled( true );
+ key->setDisabled( true );
+ break;
+ case '>': // expired key
+ subkey->setExpired( true );
+ key->setExpired( true );
+ break;
+ default:
+ kdDebug(5100) << "Unknown key flag.\n";
+ }
+
+ // Key Length
+ pos = index + 4;
+ while( output[pos] == ' ' )
+ pos++;
+ pos2 = output.find( '/', pos );
+ subkey->setKeyLength( output.mid( pos, pos2-pos ).toUInt() );
+
+ // Key ID
+ pos = pos2 + 1;
+ pos2 = output.find( ' ', pos );
+ subkey->setKeyID( output.mid( pos, pos2-pos ) );
+
+ // Creation Date
+ pos = pos2 + 1;
+ while( output[pos] == ' ' )
+ pos++;
+ pos2 = output.find( ' ', pos );
+ int year = output.mid( pos, 4 ).toInt();
+ int month = output.mid( pos+5, 2 ).toInt();
+ int day = output.mid( pos+8, 2 ).toInt();
+ QDateTime dt( QDate( year, month, day ), QTime( 00, 00 ) );
+ QDateTime epoch( QDate( 1970, 01, 01 ), QTime( 00, 00 ) );
+ // The calculated creation date isn't exactly correct because QDateTime
+ // doesn't know anything about timezones and always assumes local time
+ // although epoch is of course UTC. But as PGP 2 anyway doesn't print
+ // the time this doesn't matter too much.
+ subkey->setCreationDate( epoch.secsTo( dt ) );
+
+ // User ID
+ pos = pos2 + 1;
+ while( output[pos] == ' ' )
+ pos++;
+ QCString uid = output.mid( pos, index2-pos );
+ if( uid != "*** KEY REVOKED ***" )
+ key->addUserID( uid );
+ else
+ {
+ subkey->setRevoked( true );
+ key->setRevoked( true );
+ }
+ }
+ else if( output[index] == ' ' )
+ { // line contains additional key data
+
+ if( key == 0 )
+ break;
+ assert( subkey != 0 );
+
+ int pos = index + 1;
+ while( output[pos] == ' ' )
+ pos++;
+
+ if( !strncmp( output.data() + pos, "Key fingerprint = ", 18 ) )
+ { // line contains a fingerprint
+ // Example:
+ // Key fingerprint = 47 30 7C 76 05 BF 5E FB 72 41 00 F2 7D 0B D0 49
+
+ QCString fingerprint = output.mid( pos, index2-pos );
+ // remove white space from the fingerprint
+ for ( int idx = 0 ; (idx = fingerprint.find(' ', idx)) >= 0 ; )
+ fingerprint.replace( idx, 1, "" );
+
+ subkey->setFingerprint( fingerprint );
+ }
+ else if( !strncmp( output.data() + pos, "Expire: ", 8 ) ||
+ !strncmp( output.data() + pos, "no expire ", 10 ) )
+ { // line contains additional key properties
+ // Examples:
+ // Expire: 2001/09/10
+ // no expire ENCRyption only
+ // no expire SIGNature only
+
+ if( output[pos] == 'E' )
+ {
+ // Expiration Date
+ pos += 8;
+ int year = output.mid( pos, 4 ).toInt();
+ int month = output.mid( pos+5, 2 ).toInt();
+ int day = output.mid( pos+8, 2 ).toInt();
+ QDateTime dt( QDate( year, month, day ), QTime( 00, 00 ) );
+ QDateTime epoch( QDate( 1970, 01, 01 ), QTime( 00, 00 ) );
+ // Here the same comments as for the creation date are valid.
+ subkey->setExpirationDate( epoch.secsTo( dt ) );
+ pos += 11; // note that there is always a blank after the expire date
+ }
+ else
+ pos += 10;
+
+ // optional key capabilities (sign/encrypt only)
+ if( pos != index2 )
+ {
+ if( !strncmp( output.data() + pos, "SIGNature only", 14 ) )
+ {
+ subkey->setCanEncrypt( false );
+ key->setCanEncrypt( false );
+ }
+ else if( !strncmp( output.data() + pos, "ENCRyption only", 15 ) )
+ {
+ subkey->setCanSign( false );
+ key->setCanSign( false );
+ subkey->setCanCertify( false );
+ key->setCanCertify( false );
+ }
+ }
+ }
+ else
+ { // line contains an additional user id
+ // Example:
+ // Test key (2nd user ID) <abc@xyz>
+
+ key->addUserID( output.mid( pos, index2-pos ) );
+ }
+ }
+ index = index2 + 1;
+ }
+
+ //kdDebug(5100) << "finished parsing key data\n";
+
+ return key;
+}
+
+
+void
+Base2::parseTrustDataForKey( Key* key, const QCString& str )
+{
+ if( ( key == 0 ) || str.isEmpty() )
+ return;
+
+ QCString keyID = key->primaryKeyID();
+ UserIDList userIDs = key->userIDs();
+
+ // search the trust data belonging to this key
+ int index = str.find( '\n' ) + 1;
+ while( ( index > 0 ) &&
+ ( strncmp( str.data() + index+2, keyID.data(), 8 ) != 0 ) )
+ index = str.find( '\n', index ) + 1;
+
+ if( index == 0 )
+ return;
+
+ bool ultimateTrust = false;
+ if( !strncmp( str.data() + index+11, "ultimate", 8 ) )
+ ultimateTrust = true;
+
+ bool firstLine = true;
+
+ while( true )
+ { // loop over all trust information about this key
+ int index2;
+
+ // search the end of the current line
+ if( ( index2 = str.find( '\n', index ) ) == -1 )
+ break;
+
+ // check if trust info for the next key starts
+ if( !firstLine && ( str[index+2] != ' ' ) )
+ break;
+
+ if( str[index+21] != ' ' )
+ { // line contains a validity value for a user ID
+
+ // determine the validity
+ Validity validity = KPGP_VALIDITY_UNKNOWN;
+ if( !strncmp( str.data() + index+21, "complete", 8 ) )
+ if( ultimateTrust )
+ validity = KPGP_VALIDITY_ULTIMATE;
+ else
+ validity = KPGP_VALIDITY_FULL;
+ else if( !strncmp( str.data() + index+21, "marginal", 8 ) )
+ validity = KPGP_VALIDITY_MARGINAL;
+ else if( !strncmp( str.data() + index+21, "never", 5 ) )
+ validity = KPGP_VALIDITY_NEVER;
+ else if( !strncmp( str.data() + index+21, "undefined", 9 ) )
+ validity = KPGP_VALIDITY_UNDEFINED;
+
+ // determine the user ID
+ int pos = index + 31;
+ if( str[index+2] == ' ' )
+ pos++; // additional user IDs start one column later
+ QString uid = str.mid( pos, index2-pos );
+
+ // set the validity of the corresponding user ID
+ for( UserIDListIterator it( userIDs ); it.current(); ++it )
+ if( (*it)->text() == uid )
+ {
+ kdDebug(5100)<<"Setting the validity of "<<uid<<" to "<<validity<<endl;
+ (*it)->setValidity( validity );
+ break;
+ }
+ }
+
+ firstLine = false;
+ index = index2 + 1;
+ }
+}
+
+
+KeyList
+Base2::parseKeyList( const QCString& output, bool secretKeys )
+{
+ kdDebug(5100) << "Kpgp::Base2::parseKeyList()" << endl;
+ KeyList keys;
+ Key *key = 0;
+ Subkey *subkey = 0;
+ int index;
+
+ // search start of key data
+ if( !strncmp( output.data(), "pub", 3 ) ||
+ !strncmp( output.data(), "sec", 3 ) )
+ index = 0;
+ else
+ {
+ if( secretKeys )
+ index = output.find( "\nsec" );
+ else
+ index = output.find( "\npub" );
+ if( index == -1 )
+ return keys;
+ else
+ index++;
+ }
+
+ while( true )
+ {
+ int index2;
+
+ // search the end of the current line
+ if( ( index2 = output.find( '\n', index ) ) == -1 )
+ break;
+
+ if( !strncmp( output.data() + index, "pub", 3 ) ||
+ !strncmp( output.data() + index, "sec", 3 ) )
+ { // line contains primary key data
+ // Example 1:
+ // pub 1024/E2D074D3 2001/09/09 Test Key <testkey@xyz>
+ // Example 2 (disabled key):
+ // pub- 1024/8CCB2C1B 2001/11/04 Disabled Test Key <disabled@xyz>
+ // Example 3 (expired key):
+ // pub> 1024/7B94827D 2001/09/09 Expired Test Key <expired@xyz> (EXPIRE:2001-09-10)
+ // Example 4 (revoked key):
+ // pub 1024/956721F9 2001/09/09 *** KEY REVOKED ***
+
+ int pos, pos2;
+
+ if( key != 0 ) // store the previous key in the key list
+ keys.append( key );
+
+ key = new Key();
+ key->setSecret( secretKeys );
+ // set default key capabilities
+ key->setCanEncrypt( true );
+ key->setCanSign( true );
+ key->setCanCertify( true );
+
+ subkey = new Subkey( "", secretKeys );
+ key->addSubkey( subkey );
+ // set default key capabilities
+ subkey->setCanEncrypt( true );
+ subkey->setCanSign( true );
+ subkey->setCanCertify( true );
+ // expiration date defaults to never
+ subkey->setExpirationDate( -1 );
+
+ // Key Flags
+ switch( output[index+3] )
+ {
+ case ' ': // nothing special
+ break;
+ case '-': // disabled key
+ subkey->setDisabled( true );
+ key->setDisabled( true );
+ break;
+ case '>': // expired key
+ subkey->setExpired( true );
+ key->setExpired( true );
+ break;
+ default:
+ kdDebug(5100) << "Unknown key flag.\n";
+ }
+
+ // Key Length
+ pos = index + 4;
+ while( output[pos] == ' ' )
+ pos++;
+ pos2 = output.find( '/', pos );
+ subkey->setKeyLength( output.mid( pos, pos2-pos ).toUInt() );
+
+ // Key ID
+ pos = pos2 + 1;
+ pos2 = output.find( ' ', pos );
+ subkey->setKeyID( output.mid( pos, pos2-pos ) );
+
+ // Creation Date
+ pos = pos2 + 1;
+ while( output[pos] == ' ' )
+ pos++;
+ pos2 = output.find( ' ', pos );
+ int year = output.mid( pos, 4 ).toInt();
+ int month = output.mid( pos+5, 2 ).toInt();
+ int day = output.mid( pos+8, 2 ).toInt();
+ QDateTime dt( QDate( year, month, day ), QTime( 00, 00 ) );
+ QDateTime epoch( QDate( 1970, 01, 01 ), QTime( 00, 00 ) );
+ // The calculated creation date isn't exactly correct because QDateTime
+ // doesn't know anything about timezones and always assumes local time
+ // although epoch is of course UTC. But as PGP 2 anyway doesn't print
+ // the time this doesn't matter too much.
+ subkey->setCreationDate( epoch.secsTo( dt ) );
+
+ // User ID
+ pos = pos2 + 1;
+ while( output[pos] == ' ' )
+ pos++;
+ QCString uid = output.mid( pos, index2-pos );
+ if( uid != "*** KEY REVOKED ***" )
+ key->addUserID( uid );
+ else
+ {
+ subkey->setRevoked( true );
+ key->setRevoked( true );
+ }
+ }
+ else if( output[index] == ' ' )
+ { // line contains additional key data
+
+ if( key == 0 )
+ break;
+
+ int pos = index + 1;
+ while( output[pos] == ' ' )
+ pos++;
+
+ if( !strncmp( output.data() + pos, "Key fingerprint = ", 18 ) )
+ { // line contains a fingerprint
+ // Example:
+ // Key fingerprint = 47 30 7C 76 05 BF 5E FB 72 41 00 F2 7D 0B D0 49
+
+ int pos2;
+ pos2 = pos + 18;
+ QCString fingerprint = output.mid( pos, index2-pos );
+ // remove white space from the fingerprint
+ for ( int idx = 0 ; (idx = fingerprint.find(' ', idx)) >= 0 ; )
+ fingerprint.replace( idx, 1, "" );
+
+ subkey->setFingerprint( fingerprint );
+ }
+ else if( !strncmp( output.data() + pos, "Expire: ", 8 ) ||
+ !strncmp( output.data() + pos, "no expire ", 10 ) )
+ { // line contains additional key properties
+ // Examples:
+ // Expire: 2001/09/10
+ // no expire ENCRyption only
+ // no expire SIGNature only
+
+ if( output[pos] == 'E' )
+ {
+ // Expiration Date
+ pos += 8;
+ int year = output.mid( pos, 4 ).toInt();
+ int month = output.mid( pos+5, 2 ).toInt();
+ int day = output.mid( pos+8, 2 ).toInt();
+ QDateTime dt( QDate( year, month, day ), QTime( 00, 00 ) );
+ QDateTime epoch( QDate( 1970, 01, 01 ), QTime( 00, 00 ) );
+ // Here the same comments as for the creation date are valid.
+ subkey->setExpirationDate( epoch.secsTo( dt ) );
+ pos += 11; // note that there is always a blank after the expire date
+ }
+ else
+ pos += 10;
+
+ // optional key capabilities (sign/encrypt only)
+ if( pos != index2 )
+ {
+ if( !strncmp( output.data() + pos, "SIGNature only", 14 ) )
+ {
+ subkey->setCanEncrypt( false );
+ key->setCanEncrypt( false );
+ }
+ else if( !strncmp( output.data() + pos, "ENCRyption only", 15 ) )
+ {
+ subkey->setCanSign( false );
+ key->setCanSign( false );
+ subkey->setCanCertify( false );
+ key->setCanCertify( false );
+ }
+ }
+ }
+ else
+ { // line contains an additional user id
+ // Example:
+ // Test key (2nd user ID) <abc@xyz>
+
+ key->addUserID( output.mid( pos, index2-pos ) );
+ }
+ }
+
+ index = index2 + 1;
+ }
+
+ if (key != 0) // store the last key in the key list
+ keys.append( key );
+
+ //kdDebug(5100) << "finished parsing keys" << endl;
+
+ return keys;
+}
+
+
+} // namespace Kpgp
diff --git a/libkpgp/kpgpbase5.cpp b/libkpgp/kpgpbase5.cpp
new file mode 100644
index 00000000..c67dfd29
--- /dev/null
+++ b/libkpgp/kpgpbase5.cpp
@@ -0,0 +1,828 @@
+/*
+ kpgpbase5.cpp
+
+ Copyright (C) 2001,2002 the KPGP authors
+ See file AUTHORS.kpgp for details
+
+ This file is part of KPGP, the KDE PGP/GnuPG support library.
+
+ KPGP is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "kpgpbase.h"
+#include "kpgp.h"
+
+#include <string.h> /* strncmp */
+#include <assert.h>
+
+#include <qregexp.h>
+#include <qdatetime.h>
+
+#include <klocale.h>
+#include <kprocess.h>
+#include <kdebug.h>
+
+
+namespace Kpgp {
+
+Base5::Base5()
+ : Base()
+{
+}
+
+
+Base5::~Base5()
+{
+}
+
+
+int
+Base5::encrypt( Block& block, const KeyIDList& recipients )
+{
+ return encsign( block, recipients, 0 );
+}
+
+
+int
+Base5::clearsign( Block& block, const char *passphrase )
+{
+ return encsign( block, KeyIDList(), passphrase );
+}
+
+
+int
+Base5::encsign( Block& block, const KeyIDList& recipients,
+ const char *passphrase )
+{
+ QCString cmd;
+ int exitStatus = 0;
+ int index;
+ // used to work around a bug in pgp5. pgp5 treats files
+ // with non ascii chars (umlauts, etc...) as binary files, but
+ // we want a clear signature
+ bool signonly = false;
+
+ if(!recipients.isEmpty() && passphrase != 0)
+ cmd = "pgpe +batchmode -afts ";
+ else if(!recipients.isEmpty())
+ cmd = "pgpe +batchmode -aft ";
+ else if(passphrase != 0)
+ {
+ cmd = "pgps +batchmode -abft ";
+ signonly = true;
+ }
+ else
+ {
+ errMsg = i18n("Neither recipients nor passphrase specified.");
+ return OK;
+ }
+
+ if(passphrase != 0)
+ cmd += addUserId();
+
+ if(!recipients.isEmpty())
+ {
+ if(Module::getKpgp()->encryptToSelf())
+ {
+ cmd += " -r 0x";
+ cmd += Module::getKpgp()->user();
+ }
+
+ for( KeyIDList::ConstIterator it = recipients.begin();
+ it != recipients.end(); ++it ) {
+ cmd += " -r 0x";
+ cmd += (*it);
+ }
+ }
+
+ clear();
+ input = block.text();
+
+ if (signonly)
+ {
+ input.append("\n");
+ input.replace(QRegExp("[ \t]+\n"), "\n"); //strip trailing whitespace
+ }
+ //We have to do this otherwise it's all in vain
+
+ exitStatus = run(cmd.data(), passphrase);
+ block.setError( error );
+
+ if(exitStatus != 0)
+ status = ERROR;
+
+ // now parse the returned info
+ if(error.find("Cannot unlock private key") != -1)
+ {
+ errMsg = i18n("The passphrase you entered is invalid.");
+ status |= ERROR;
+ status |= BADPHRASE;
+ }
+//if(!ignoreUntrusted)
+//{
+ QCString aStr;
+ index = -1;
+ while((index = error.find("WARNING: The above key",index+1)) != -1)
+ {
+ int index2 = error.find("But you previously",index);
+ int index3 = error.find("WARNING: The above key",index+1);
+ if(index2 == -1 || (index2 > index3 && index3 != -1))
+ {
+ // the key wasn't valid, no encryption to this person
+ // extract the person
+ index2 = error.find('\n',index);
+ index3 = error.find('\n',index2+1);
+ aStr += error.mid(index2+1, index3-index2-1);
+ aStr += ", ";
+ }
+ }
+ if(!aStr.isEmpty())
+ {
+ aStr.truncate(aStr.length()-2);
+ if(error.find("No valid keys found") != -1)
+ errMsg = i18n("The key(s) you want to encrypt your message "
+ "to are not trusted. No encryption done.");
+ else
+ errMsg = i18n("The following key(s) are not trusted:\n%1\n"
+ "Their owner(s) will not be able to decrypt the message.")
+ .arg(aStr);
+ status |= ERROR;
+ status |= BADKEYS;
+ }
+//}
+ if((index = error.find("No encryption keys found for")) != -1)
+ {
+ index = error.find(':',index);
+ int index2 = error.find('\n',index);
+
+ errMsg = i18n("Missing encryption key(s) for:\n%1")
+ .arg(error.mid(index,index2-index));
+// errMsg = QString("Missing encryption key(s) for: %1")
+// .arg(error.mid(index,index2-index));
+ status |= ERROR;
+ status |= MISSINGKEY;
+ }
+
+ if(signonly) {
+ // dash-escape the input
+ if (input[0] == '-')
+ input = "- " + input;
+ for ( int idx = 0 ; (idx = input.find("\n-", idx)) >= 0 ; idx += 4 )
+ input.replace(idx, 2, "\n- -");
+
+ output = "-----BEGIN PGP SIGNED MESSAGE-----\n\n" + input + "\n" + output;
+ }
+
+ block.setProcessedText( output );
+ block.setStatus( status );
+ return status;
+}
+
+
+int
+Base5::decrypt( Block& block, const char *passphrase )
+{
+ int exitStatus = 0;
+
+ clear();
+ input = block.text();
+ exitStatus = run("pgpv -f +batchmode=1", passphrase);
+ if( !output.isEmpty() )
+ block.setProcessedText( output );
+ block.setError( error );
+
+ if(exitStatus == -1) {
+ errMsg = i18n("Error running PGP");
+ status = RUN_ERR;
+ block.setStatus( status );
+ return status;
+ }
+
+ // lets parse the returned information.
+ int index;
+
+ index = error.find("Cannot decrypt message");
+ if(index != -1)
+ {
+ //kdDebug(5100) << "message is encrypted" << endl;
+ status |= ENCRYPTED;
+
+ // ok. we have an encrypted message. Is the passphrase bad,
+ // or do we not have the secret key?
+ if(error.find("Need a pass phrase") != -1)
+ {
+ if(passphrase != 0)
+ {
+ errMsg = i18n("Bad passphrase; could not decrypt.");
+ kdDebug(5100) << "Base: passphrase is bad" << endl;
+ status |= BADPHRASE;
+ status |= ERROR;
+ }
+ }
+ else
+ {
+ // we don't have the secret key
+ status |= NO_SEC_KEY;
+ status |= ERROR;
+ errMsg = i18n("You do not have the secret key needed to decrypt this message.");
+ kdDebug(5100) << "Base: no secret key for this message" << endl;
+ }
+ // check for persons
+#if 0
+ // ##### FIXME: This information is anyway currently not used
+ // I'll change it to always determine the recipients.
+ index = error.find("can only be decrypted by:");
+ if(index != -1)
+ {
+ index = error.find('\n',index);
+ int end = error.find("\n\n",index);
+
+ mRecipients.clear();
+ int index2;
+ while( (index2 = error.find('\n',index+1)) <= end )
+ {
+ QCString item = error.mid(index+1,index2-index-1);
+ item.stripWhiteSpace();
+ mRecipients.append(item);
+ index = index2;
+ }
+ }
+#endif
+ }
+ index = error.find("Good signature");
+ if(index != -1)
+ {
+ //kdDebug(5100) << "good signature" << endl;
+ status |= SIGNED;
+ status |= GOODSIG;
+
+ // get key ID of signer
+ index = error.find("Key ID ", index) + 7;
+ block.setSignatureKeyId( error.mid(index, 8) );
+
+ // get signer
+ index = error.find('"',index) + 1;
+ int index2 = error.find('"', index);
+ block.setSignatureUserId( error.mid(index, index2-index) );
+
+ /// ### FIXME get signature date
+ block.setSignatureDate( "" );
+ }
+ index = error.find("BAD signature");
+ if(index != -1)
+ {
+ //kdDebug(5100) << "BAD signature" << endl;
+ status |= SIGNED;
+ status |= ERROR;
+
+ // get key ID of signer
+ index = error.find("Key ID ", index) + 7;
+ block.setSignatureKeyId( error.mid(index, 8) );
+
+ // get signer
+ index = error.find('"',index) + 1;
+ int index2 = error.find('"', index);
+ block.setSignatureUserId( error.mid(index, index2-index) );
+
+ /// ### FIXME get signature date
+ block.setSignatureDate( "" );
+ }
+ index = error.find("Signature by unknown key");
+ if(index != -1)
+ {
+ index = error.find("keyid: 0x",index) + 9;
+ block.setSignatureKeyId( error.mid(index, 8) );
+ block.setSignatureUserId( QString::null );
+ // FIXME: not a very good solution...
+ status |= SIGNED;
+ status |= GOODSIG;
+
+ /// ### FIXME get signature date
+ block.setSignatureDate( "" );
+ }
+
+ //kdDebug(5100) << "status = " << status << endl;
+ block.setStatus( status );
+ return status;
+}
+
+
+Key*
+Base5::readPublicKey( const KeyID& keyId, const bool readTrust, Key* key )
+{
+ int exitStatus = 0;
+
+ status = 0;
+ exitStatus = run( "pgpk -ll 0x" + keyId, 0, true );
+
+ if(exitStatus != 0) {
+ status = ERROR;
+ return 0;
+ }
+
+ key = parseSingleKey( output, key );
+
+ if( key == 0 )
+ {
+ return 0;
+ }
+
+ if( readTrust )
+ {
+ exitStatus = run( "pgpk -c 0x" + keyId, 0, true );
+
+ if(exitStatus != 0) {
+ status = ERROR;
+ return 0;
+ }
+
+ parseTrustDataForKey( key, output );
+ }
+
+ return key;
+}
+
+
+KeyList
+Base5::publicKeys( const QStringList & patterns )
+{
+ int exitStatus = 0;
+
+ QCString cmd = "pgpk -ll";
+ for ( QStringList::ConstIterator it = patterns.begin();
+ it != patterns.end(); ++it ) {
+ cmd += " ";
+ cmd += KProcess::quote( *it ).local8Bit();
+ }
+ status = 0;
+ exitStatus = run( cmd, 0, true );
+
+ if(exitStatus != 0) {
+ status = ERROR;
+ return KeyList();
+ }
+
+ // now we need to parse the output for public keys
+ KeyList keys = parseKeyList( output, false );
+
+ // sort the list of public keys
+ keys.sort();
+
+ return keys;
+}
+
+
+KeyList
+Base5::secretKeys( const QStringList & patterns )
+{
+ int exitStatus = 0;
+
+ status = 0;
+ QCString cmd = "pgpk -ll";
+ for ( QStringList::ConstIterator it = patterns.begin();
+ it != patterns.end(); ++it ) {
+ cmd += " ";
+ cmd += KProcess::quote( *it ).local8Bit();
+ }
+ status = 0;
+ exitStatus = run( cmd, 0, true );
+
+ if(exitStatus != 0) {
+ status = ERROR;
+ return KeyList();
+ }
+
+ // now we need to parse the output for secret keys
+ KeyList keys = parseKeyList( output, true );
+
+ // sort the list of public keys
+ keys.sort();
+
+ return keys;
+}
+
+
+QCString Base5::getAsciiPublicKey(const KeyID& keyID)
+{
+ int exitStatus = 0;
+
+ if (keyID.isEmpty())
+ return QCString();
+
+ status = 0;
+ exitStatus = run( "pgpk -xa 0x" + keyID, 0, true );
+
+ if(exitStatus != 0) {
+ status = ERROR;
+ return QCString();
+ }
+
+ return output;
+}
+
+
+int
+Base5::signKey(const KeyID& keyID, const char *passphrase)
+{
+ QCString cmd;
+ int exitStatus = 0;
+
+ if(passphrase == 0) return false;
+
+ cmd = "pgpk -s -f +batchmode=1 0x";
+ cmd += keyID;
+ cmd += addUserId();
+
+ status = 0;
+ exitStatus = run(cmd.data(), passphrase);
+
+ if (exitStatus != 0)
+ status = ERROR;
+
+ return status;
+}
+
+//-- private functions --------------------------------------------------------
+
+Key*
+Base5::parseKeyData( const QCString& output, int& offset, Key* key /* = 0 */ )
+// This function parses the data for a single key which is output by PGP 5
+// with the following command line:
+// pgpk -ll
+// It expects the key data to start at offset and returns the start of
+// the next key's data in offset.
+{
+ if( ( strncmp( output.data() + offset, "pub", 3 ) != 0 ) &&
+ ( strncmp( output.data() + offset, "sec", 3 ) != 0 ) )
+ {
+ kdDebug(5100) << "Unknown key type or corrupt key data.\n";
+ return 0;
+ }
+
+ if( key == 0 )
+ key = new Key();
+ else
+ key->clear();
+
+ Subkey *subkey = 0;
+ bool primaryKey = true;
+
+ while( true )
+ {
+ int eol;
+
+ // search the end of the current line
+ eol = output.find( '\n', offset );
+ if( ( eol == -1 ) || ( eol == offset ) )
+ break;
+
+ //kdDebug(5100) << "Parsing: " << output.mid(offset, eol-offset) << endl;
+
+ if( !strncmp( output.data() + offset, "pub", 3 ) ||
+ !strncmp( output.data() + offset, "sec", 3 ) ||
+ !strncmp( output.data() + offset, "sub", 3 ) )
+ { // line contains key data
+ //kdDebug(5100)<<"Key data:\n";
+ int pos, pos2;
+
+ subkey = new Subkey( "", false );
+ key->addSubkey( subkey );
+
+ // Key Flags
+ /* From the PGP 5 manual page for pgpk:
+ Following this column is a single character which
+ describes other attributes of the object:
+
+ @ The object is disabled
+ + The object is axiomatically trusted (i.e., it's
+ your key)
+ */
+ switch( output[offset+3] )
+ {
+ case ' ': // nothing special
+ break;
+ case '@': // disabled key
+ subkey->setDisabled( true );
+ key->setDisabled( true );
+ break;
+ default: // all other flags are ignored
+ //kdDebug(5100) << "Unknown key flag.\n";
+ ;
+ }
+
+ // Key Length
+ pos = offset + 4;
+ while( output[pos] == ' ' )
+ pos++;
+ pos2 = output.find( ' ', pos );
+ subkey->setKeyLength( output.mid( pos, pos2-pos ).toUInt() );
+ //kdDebug(5100) << "Key Length: "<<subkey->keyLength()<<endl;
+
+ // Key ID
+ pos = pos2 + 1;
+ while( output[pos] == ' ' )
+ pos++;
+ pos += 2; // skip the '0x'
+ pos2 = output.find( ' ', pos );
+ subkey->setKeyID( output.mid( pos, pos2-pos ) );
+ //kdDebug(5100) << "Key ID: "<<subkey->keyID()<<endl;
+
+ // Creation Date
+ pos = pos2 + 1;
+ while( output[pos] == ' ' )
+ pos++;
+ pos2 = output.find( ' ', pos );
+ int year = output.mid( pos, 4 ).toInt();
+ int month = output.mid( pos+5, 2 ).toInt();
+ int day = output.mid( pos+8, 2 ).toInt();
+ QDateTime dt( QDate( year, month, day ), QTime( 00, 00 ) );
+ QDateTime epoch( QDate( 1970, 01, 01 ), QTime( 00, 00 ) );
+ // The calculated creation date isn't exactly correct because QDateTime
+ // doesn't know anything about timezones and always assumes local time
+ // although epoch is of course UTC. But as PGP 5 anyway doesn't print
+ // the time this doesn't matter too much.
+ subkey->setCreationDate( epoch.secsTo( dt ) );
+
+ // Expiration Date
+ // if the primary key has been revoked the expiration date is not printed
+ if( primaryKey || !key->revoked() )
+ {
+ pos = pos2 + 1;
+ while( output[pos] == ' ' )
+ pos++;
+ pos2 = output.find( ' ', pos );
+ if( output[pos] == '-' )
+ { // key doesn't expire
+ subkey->setExpirationDate( -1 );
+ }
+ else if( !strncmp( output.data() + pos, "*REVOKED*", 9 ) )
+ { // key has been revoked
+ subkey->setRevoked( true );
+ key->setRevoked( true );
+ }
+ else
+ {
+ int year = output.mid( pos, 4 ).toInt();
+ int month = output.mid( pos+5, 2 ).toInt();
+ int day = output.mid( pos+8, 2 ).toInt();
+ QDateTime dt( QDate( year, month, day ), QTime( 00, 00 ) );
+ subkey->setCreationDate( epoch.secsTo( dt ) );
+ // has the key already expired?
+ if( QDateTime::currentDateTime() >= dt )
+ {
+ subkey->setExpired( true );
+ key->setExpired( true );
+ }
+ }
+ }
+ else if( key->revoked() )
+ subkey->setRevoked( true );
+
+ // Key algorithm (RSA, DSS, Diffie-Hellman)
+ bool sign = false;
+ bool encr = false;
+ pos = pos2 + 1;
+ while( output[pos] == ' ' )
+ pos++;
+ pos2 = output.find( ' ', pos );
+ if( !strncmp( output.data() + pos, "RSA", 3 ) )
+ {
+ sign = true;
+ encr = true;
+ }
+ else if( !strncmp( output.data() + pos, "DSS", 3 ) )
+ sign = true;
+ else if( !strncmp( output.data() + pos, "Diffie-Hellman", 14 ) )
+ encr = true;
+ else
+ kdDebug(5100)<<"Unknown key algorithm\n";
+
+ // set key capabilities of the subkey
+ subkey->setCanEncrypt( encr );
+ subkey->setCanSign( sign );
+ subkey->setCanCertify( sign );
+
+ if( primaryKey )
+ {
+ // Global key capabilities
+ bool canSign = false;
+ bool canEncr = false;
+ pos = pos2 + 1;
+ while( output[pos] == ' ' )
+ pos++;
+ pos2 = eol;
+ if( !strncmp( output.data() + pos, "Sign & Encrypt", 14 ) )
+ {
+ canSign = true;
+ canEncr = true;
+ }
+ else if( !strncmp( output.data() + pos, "Sign only", 9 ) )
+ canSign = true;
+ else if( !strncmp( output.data() + pos, "Encrypt only", 12 ) )
+ canEncr = true;
+ else
+ kdDebug(5100)<<"Unknown key capability\n";
+
+ // set the global key capabilities
+ if( !key->expired() && !key->revoked() )
+ {
+ key->setCanEncrypt( canEncr );
+ key->setCanSign( canSign );
+ key->setCanCertify( canSign );
+ }
+ //kdDebug(5100)<<"Key capabilities: "<<(key->canEncrypt()?"E":"")<<(key->canSign()?"SC":"")<<endl;
+ primaryKey = false;
+ }
+ }
+ else if( !strncmp( output.data() + offset, "f16", 3 ) ||
+ !strncmp( output.data() + offset, "f20", 3 ) )
+ { // line contains a fingerprint
+ /* Examples:
+ f16 Fingerprint16 = DE 2A 77 08 78 64 7C 42 72 75 B1 A7 3E 42 3F 79
+ f20 Fingerprint20 = 226F 4B63 6DA2 7389 91D1 2A49 D58A 3EC1 5214 181E
+
+ */
+ int pos = output.find( '=', offset+3 ) + 2;
+ QCString fingerprint = output.mid( pos, eol-pos );
+ // remove white space from the fingerprint
+ for ( int idx = 0 ; (idx = fingerprint.find(' ', idx)) >= 0 ; )
+ fingerprint.replace( idx, 1, "" );
+ assert( subkey != 0 );
+ subkey->setFingerprint( fingerprint );
+ //kdDebug(5100)<<"Fingerprint: "<<fingerprint<<endl;
+ }
+ else if( !strncmp( output.data() + offset, "uid", 3 ) )
+ { // line contains a uid
+ int pos = offset+5;
+ QCString uid = output.mid( pos, eol-pos );
+ key->addUserID( uid );
+ // displaying of uids which contain non-ASCII characters is broken in
+ // PGP 5.0i; it shows these characters as \ooo and truncates the uid
+ // because it doesn't take the 3 extra characters per non-ASCII char
+ // into account. Example (with an UTF-8 encoded &ouml;):
+ // uid Ingo Kl\303\266cker <ingo.kloecker@epo
+ // because of this and because we anyway don't know which charset was
+ // used to encode the uid we don't try to decode it
+ }
+ else if ( !strncmp( output.data() + offset, "sig", 3 ) ||
+ !strncmp( output.data() + offset, "SIG", 3 ) ||
+ !strncmp( output.data() + offset, "ret", 3 ) )
+ { // line contains a signature
+ // SIG = sig with own key; ret = sig with revoked key
+ // we ignore it for now
+ }
+
+ offset = eol + 1;
+ }
+
+ return key;
+}
+
+
+Key*
+Base5::parseSingleKey( const QCString& output, Key* key /* = 0 */ )
+{
+ int offset;
+
+ // search start of header line
+ if( !strncmp( output.data(), "Type Bits", 9 ) )
+ offset = 0;
+ else
+ {
+ offset = output.find( "\nType Bits" ) + 1;
+ if( offset == 0 )
+ return 0;
+ }
+
+ // key data begins in the next line
+ offset = output.find( '\n', offset ) + 1;
+ if( offset == -1 )
+ return 0;
+
+ key = parseKeyData( output, offset, key );
+
+ //kdDebug(5100) << "finished parsing keys" << endl;
+
+ return key;
+}
+
+
+KeyList
+Base5::parseKeyList( const QCString& output, bool onlySecretKeys )
+{
+ KeyList keys;
+ Key *key = 0;
+ int offset;
+
+ // search start of header line
+ if( !strncmp( output.data(), "Type Bits", 9 ) )
+ offset = 0;
+ else
+ {
+ offset = output.find( "\nType Bits" ) + 1;
+ if( offset == 0 )
+ return keys;
+ }
+
+ // key data begins in the next line
+ offset = output.find( '\n', offset ) + 1;
+ if( offset == -1 )
+ return keys;
+
+ do
+ {
+ key = parseKeyData( output, offset );
+ if( key != 0 )
+ {
+ // if only secret keys should be read test if the key is secret
+ if( !onlySecretKeys || !key->secret() )
+ keys.append( key );
+ // skip the blank line which separates the keys
+ offset++;
+ }
+ }
+ while( key != 0 );
+
+ //kdDebug(5100) << "finished parsing keys" << endl;
+
+ return keys;
+}
+
+
+void
+Base5::parseTrustDataForKey( Key* key, const QCString& str )
+{
+ if( ( key == 0 ) || str.isEmpty() )
+ return;
+
+ QCString keyID = "0x" + key->primaryKeyID();
+ UserIDList userIDs = key->userIDs();
+
+ // search the start of the trust data
+ int offset = str.find( "\n\n KeyID" ) + 9;
+ if( offset == -1 + 9 )
+ return;
+
+ offset = str.find( '\n', offset ) + 1;
+ if( offset == -1 + 1 )
+ return;
+
+ bool ultimateTrust = false;
+ if( !strncmp( str.data() + offset+13, "ultimate", 8 ) )
+ ultimateTrust = true;
+
+ while( true )
+ { // loop over all trust information about this key
+
+ int eol;
+
+ // search the end of the current line
+ if( ( eol = str.find( '\n', offset ) ) == -1 )
+ break;
+
+ if( str[offset+23] != ' ' )
+ { // line contains a validity value for a user ID
+
+ // determine the validity
+ Validity validity = KPGP_VALIDITY_UNKNOWN;
+ if( !strncmp( str.data() + offset+23, "complete", 8 ) )
+ if( ultimateTrust )
+ validity = KPGP_VALIDITY_ULTIMATE;
+ else
+ validity = KPGP_VALIDITY_FULL;
+ else if( !strncmp( str.data() + offset+23, "marginal", 8 ) )
+ validity = KPGP_VALIDITY_MARGINAL;
+ else if( !strncmp( str.data() + offset+23, "invalid", 7 ) )
+ validity = KPGP_VALIDITY_UNDEFINED;
+
+ // determine the user ID
+ int pos = offset + 33;
+ QString uid = str.mid( pos, eol-pos );
+
+ // set the validity of the corresponding user ID
+ for( UserIDListIterator it( userIDs ); it.current(); ++it )
+ if( (*it)->text() == uid )
+ {
+ kdDebug(5100)<<"Setting the validity of "<<uid<<" to "<<validity<<endl;
+ (*it)->setValidity( validity );
+ break;
+ }
+ }
+
+ offset = eol + 1;
+ }
+}
+
+
+} // namespace Kpgp
diff --git a/libkpgp/kpgpbase6.cpp b/libkpgp/kpgpbase6.cpp
new file mode 100644
index 00000000..75e47e1d
--- /dev/null
+++ b/libkpgp/kpgpbase6.cpp
@@ -0,0 +1,840 @@
+/*
+ kpgpbase6.cpp
+
+ Copyright (C) 2001,2002 the KPGP authors
+ See file AUTHORS.kpgp for details
+
+ This file is part of KPGP, the KDE PGP/GnuPG support library.
+
+ KPGP is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "kpgpbase.h"
+
+#include <string.h> /* strncmp */
+#include <assert.h>
+
+#include <qdatetime.h>
+
+#include <klocale.h>
+#include <kdebug.h>
+
+#define PGP6 "pgp"
+
+namespace Kpgp {
+
+Base6::Base6()
+ : Base2()
+{
+}
+
+
+Base6::~Base6()
+{
+}
+
+
+int
+Base6::decrypt( Block& block, const char *passphrase )
+{
+ int index, index2;
+ int exitStatus = 0;
+
+ clear();
+ input = block.text();
+ exitStatus = run( PGP6 " +batchmode +language=C -f", passphrase);
+ if( !output.isEmpty() )
+ block.setProcessedText( output );
+ block.setError( error );
+
+ if(exitStatus == -1) {
+ errMsg = i18n("error running PGP");
+ status = RUN_ERR;
+ block.setStatus( status );
+ return status;
+ }
+
+ // encrypted message
+ if( error.find("File is encrypted.") != -1)
+ {
+ //kdDebug(5100) << "kpgpbase: message is encrypted" << endl;
+ status |= ENCRYPTED;
+ if((index = error.find("Key for user ID")) != -1)
+ {
+ // Find out the key for which the phrase is needed
+ index = error.find(':', index) + 2;
+ index2 = error.find('\n', index);
+ block.setRequiredUserId( error.mid(index, index2 - index) );
+ //kdDebug(5100) << "Base: key needed is \"" << block.requiredUserId() << "\"!\n";
+
+ // Test output length to find out, if the passphrase is
+ // bad. If someone knows a better way, please fix this.
+ /// ### This could be done by forcing PGP6 to be more verbose
+ /// by adding an additional '+verbose=2' to the command line
+ if (!passphrase || !output.length())
+ {
+ errMsg = i18n("Bad passphrase; could not decrypt.");
+ //kdDebug(5100) << "Base: passphrase is bad" << endl;
+ status |= BADPHRASE;
+ status |= ERROR;
+ }
+ }
+ else if( error.find("You do not have the secret key needed to decrypt this file.") != -1)
+ {
+ errMsg = i18n("You do not have the secret key for this message.");
+ //kdDebug(5100) << "Base: no secret key for this message" << endl;
+ status |= NO_SEC_KEY;
+ status |= ERROR;
+ }
+ }
+
+ // signed message
+
+ // Examples (made with PGP 6.5.8)
+ /* Example no. 1 (signed with unknown key):
+ * File is signed. signature not checked.
+ * Signature made 2001/11/25 11:55 GMT
+ * key does not meet validity threshold.
+ *
+ * WARNING: Because this public key is not certified with a trusted
+ * signature, it is not known with high confidence that this public key
+ * actually belongs to: "(KeyID: 0x475027BD)".
+ */
+ /* Example no. 2 (signed with untrusted key):
+ * File is signed. Good signature from user "Joe User <joe@foo.bar>".
+ * Signature made 2001/12/05 13:09 GMT
+ *
+ * WARNING: Because this public key is not certified with a trusted
+ * signature, it is not known with high confidence that this public key
+ * actually belongs to: "Joe User <joe@foo.bar>".
+ */
+ /* Example no. 3 (signed with trusted key):
+ * File is signed. Good signature from user "Joe User <joe@foo.bar>".
+ * Signature made 2001/12/05 13:09 GMT
+ */
+ if(((index = error.find("File is signed.")) != -1)
+ || (error.find("Good signature") != -1 ))
+ {
+ //kdDebug(5100) << "Base: message is signed" << endl;
+ status |= SIGNED;
+ // determine the signature date
+ if( ( index2 = error.find( "Signature made", index ) ) != -1 )
+ {
+ index2 += 15;
+ int eol = error.find( '\n', index2 );
+ block.setSignatureDate( error.mid( index2, eol-index2 ) );
+ kdDebug(5100) << "Message was signed on '" << block.signatureDate() << "'\n";
+ }
+ else
+ block.setSignatureDate( QCString() );
+ // determine signature status and signature key
+ if( error.find("signature not checked") != -1)
+ {
+ index = error.find("KeyID:",index);
+ block.setSignatureKeyId( error.mid(index+9,8) );
+ block.setSignatureUserId( QString::null );
+ status |= UNKNOWN_SIG;
+ status |= GOODSIG;
+ }
+ else if((index = error.find("Good signature")) != -1 )
+ {
+ status |= GOODSIG;
+ // get signer
+ index = error.find('"',index)+1;
+ index2 = error.find('"', index);
+ block.setSignatureUserId( error.mid(index, index2-index) );
+
+ // get key ID of signer
+ index = error.find("KeyID:",index2);
+ if (index == -1)
+ block.setSignatureKeyId( QCString() );
+ else
+ block.setSignatureKeyId( error.mid(index+9,8) );
+ }
+ else if( error.find("Can't find the right public key") != -1 )
+ {
+ // #### fix this hack
+ // #### This doesn't happen with PGP 6.5.8 because it seems to
+ // #### automatically create an empty pubring if it doesn't exist.
+ status |= UNKNOWN_SIG;
+ status |= GOODSIG; // this is a hack...
+ block.setSignatureUserId( i18n("??? (file ~/.pgp/pubring.pkr not found)") );
+ block.setSignatureKeyId( "???" );
+ }
+ else
+ {
+ status |= ERROR;
+ block.setSignatureUserId( QString::null );
+ block.setSignatureKeyId( QCString() );
+ }
+ }
+ //kdDebug(5100) << "status = " << status << endl;
+ block.setStatus( status );
+ return status;
+}
+
+
+Key*
+Base6::readPublicKey( const KeyID& keyID,
+ const bool readTrust /* = false */,
+ Key* key /* = 0 */ )
+{
+ int exitStatus = 0;
+
+ status = 0;
+ exitStatus = run( PGP6 " +batchmode -compatible +verbose=0 +language=C -kvvc "
+ "0x" + keyID, 0, true );
+
+ if(exitStatus != 0) {
+ status = ERROR;
+ return 0;
+ }
+
+ key = parseSingleKey( output, key );
+
+ if( key == 0 )
+ {
+ return 0;
+ }
+
+ if( readTrust )
+ {
+ exitStatus = run( PGP6 " +batchmode -compatible +verbose=0 +language=C -kc "
+ "0x" + keyID, 0, true );
+
+ if(exitStatus != 0) {
+ status = ERROR;
+ return 0;
+ }
+
+ parseTrustDataForKey( key, output );
+ }
+
+ return key;
+}
+
+
+KeyList
+Base6::publicKeys( const QStringList & patterns )
+{
+ return doGetPublicKeys( PGP6 " +batchmode -compatible +verbose=0 "
+ "+language=C -kvvc", patterns );
+}
+
+
+/*
+QStrList
+Base6::pubKeys()
+{
+ int index, index2;
+ int exitStatus = 0;
+ int compatibleMode = 1;
+
+ status = 0;
+ exitStatus = run("pgp +batchmode +language=C -kv -f");
+
+ if(exitStatus != 0) {
+ status = ERROR;
+ return 0;
+ }
+
+ //truncate trailing "\n"
+ if (error.length() > 1) error.truncate(error.length()-1);
+
+ QStrList publicKeys;
+ index = error.find("bits/keyID",1); // skip first to "\n"
+ if (index ==-1)
+ {
+ index = error.find("Type bits",1); // skip first to "\n"
+ if (index == -1)
+ return 0;
+ else
+ compatibleMode = 0;
+ }
+
+ while( (index = error.find("\n",index)) != -1)
+ {
+ //parse line
+ QCString line;
+ if( (index2 = error.find("\n",index+1)) != -1)
+ // skip last line
+ {
+ int index3;
+ if (compatibleMode)
+ {
+ int index_pub = error.find("pub ",index);
+ int index_sec = error.find("sec ",index);
+ if (index_pub < 0)
+ index3 = index_sec;
+ else if (index_sec < 0)
+ index3 = index_pub;
+ else
+ index3 = (index_pub < index_sec ? index_pub : index_sec);
+ }
+ else
+ {
+ int index_rsa = error.find("RSA ",index);
+ int index_dss = error.find("DSS ",index);
+ if (index_rsa < 0)
+ index3 = index_dss;
+ else if (index_dss < 0)
+ index3 = index_rsa;
+ else
+ index3 = (index_rsa < index_dss ? index_rsa : index_dss);
+ }
+
+ if( (index3 >index2) || (index3 == -1) )
+ {
+ // second address for the same key
+ line = error.mid(index+1,index2-index-1);
+ line = line.stripWhiteSpace();
+ } else {
+ // line with new key
+ int index4 = error.find(QRegExp("/\\d{2}/\\d{2} "), index);
+ line = error.mid(index4+7,index2-index4-7);
+ }
+ //kdDebug(5100) << "Base: found key for " << (const char *)line << endl;
+
+ // don't add PGP's comments to the key list
+ if (strncmp(line.data(),"*** KEY EXPIRED ***",19) &&
+ line.find(QRegExp("^expires \\d{4}/\\d{2}/\\d{2}")) < 0 &&
+ strncmp(line.data(),"*** DEFAULT SIGNING KEY ***",27)) {
+ publicKeys.append(line);
+ }
+ }
+ else
+ break;
+ index = index2;
+ }
+
+ // Also look for pgp key groups
+ exitStatus = run("pgp +batchmode +language=C -gv -f");
+
+ if(exitStatus != 0) {
+ status = ERROR;
+ return 0;
+ }
+
+ index = 0;
+ while ( (index = error.find("\n >", index)) != -1 ) {
+ QCString line;
+ index += 4;
+ index2 = error.find(" \"", index);
+ line = error.mid(index, index2-index+1).stripWhiteSpace();
+
+ //kdDebug(5100) << "Base6: found key group for " << line << endl;
+ publicKeys.append(line);
+ }
+
+ return publicKeys;
+}
+*/
+
+
+KeyList
+Base6::secretKeys( const QStringList & patterns )
+{
+ return publicKeys( patterns );
+}
+
+
+int
+Base6::isVersion6()
+{
+ int exitStatus = 0;
+
+ exitStatus = run( PGP6, 0, true );
+
+ if(exitStatus == -1) {
+ errMsg = i18n("error running PGP");
+ status = RUN_ERR;
+ return 0;
+ }
+
+ if( error.find("Version 6") != -1)
+ {
+ //kdDebug(5100) << "kpgpbase: pgp version 6.x detected" << endl;
+ return 1;
+ }
+
+ //kdDebug(5100) << "kpgpbase: not pgp version 6.x" << endl;
+ return 0;
+}
+
+
+Key*
+Base6::parseKeyData( const QCString& output, int& offset, Key* key /* = 0 */ )
+// This function parses the data for a single key which is output by PGP 6
+// with the following command line arguments:
+// +batchmode -compatible +verbose=0 +language=C -kvvc
+// It expects the key data to start at offset and returns the start of
+// the next key's data in offset.
+{
+ if( ( strncmp( output.data() + offset, "DSS", 3 ) != 0 ) &&
+ ( strncmp( output.data() + offset, "RSA", 3 ) != 0 ) )
+ {
+ kdDebug(5100) << "Unknown key type or corrupt key data.\n";
+ return 0;
+ }
+
+ Subkey *subkey = 0;
+ bool firstLine = true;
+ bool canSign = false;
+ bool canEncr = false;
+ bool fpr = false;
+
+ while( true )
+ {
+ int eol;
+
+ // search the end of the current line
+ if( ( eol = output.find( '\n', offset ) ) == -1 )
+ break;
+
+ //kdDebug(5100) << "Parsing: " << output.mid(offset, eol-offset) << endl;
+
+ if( firstLine && ( !strncmp( output.data() + offset, "DSS", 3 ) ||
+ !strncmp( output.data() + offset, "RSA", 3 ) ) )
+ { // line contains primary key data
+ // Example 1:
+ // RSA 1024 0xE2D074D3 2001/09/09 Test Key <testkey@xyz>
+ // Example 2 (disabled key):
+ // RSA@ 1024 0x8CCB2C1B 2001/11/04 Disabled Test Key <disabled@xyz>
+ // Example 3 (expired key):
+ // RSA 1024 0x7B94827D 2001/09/09 *** KEY EXPIRED ***
+ // Example 4 (revoked key):
+ // RSA 1024 0x956721F9 2001/09/09 *** KEY REVOKED ***
+ // Example 5 (default signing key):
+ // RSA 1024 0x12345678 2001/09/09 *** DEFAULT SIGNING KEY ***
+ // Example 6 (expiring key):
+ // RSA 2048 0xC11DB2E5 2000/02/24 expires 2001/12/31
+ // Example 7 (complex example):
+ // DSS 1024 0x80E104A7 2000/06/05 expires 2002/05/31
+ // DSS 1024 0x80E104A7 2001/06/27 *** KEY REVOKED ***expires 2002/06/27
+ // DH 1024 0x80E104A7 2000/06/05 *** KEY REVOKED ****** KEY EXPIRED ***
+ //kdDebug(5100)<<"Primary key data:\n";
+ bool sign = false;
+ bool encr = false;
+
+ // set default key capabilities
+ if( !strncmp( output.data() + offset, "DSS", 3 ) )
+ sign = true;
+ if( !strncmp( output.data() + offset, "RSA", 3 ) )
+ {
+ sign = true;
+ encr = true;
+ }
+
+ int pos, pos2;
+
+ if( key == 0 )
+ key = new Key();
+ else
+ key->clear();
+
+ subkey = new Subkey( "", false );
+ key->addSubkey( subkey );
+ // expiration date defaults to never
+ subkey->setExpirationDate( -1 );
+
+ // Key Flags
+ switch( output[offset+3] )
+ {
+ case ' ': // nothing special
+ break;
+ case '@': // disabled key
+ subkey->setDisabled( true );
+ key->setDisabled( true );
+ break;
+ default:
+ kdDebug(5100) << "Unknown key flag.\n";
+ }
+
+ // Key Length
+ pos = offset + 4;
+ while( output[pos] == ' ' )
+ pos++;
+ pos2 = output.find( ' ', pos );
+ subkey->setKeyLength( output.mid( pos, pos2-pos ).toUInt() );
+ //kdDebug(5100) << "Key Length: "<<subkey->keyLength()<<endl;
+
+ // Key ID
+ pos = pos2 + 1;
+ while( output[pos] == ' ' )
+ pos++;
+ pos += 2; // skip the '0x'
+ pos2 = output.find( ' ', pos );
+ subkey->setKeyID( output.mid( pos, pos2-pos ) );
+ //kdDebug(5100) << "Key ID: "<<subkey->keyID()<<endl;
+
+ // Creation Date
+ pos = pos2 + 1;
+ while( output[pos] == ' ' )
+ pos++;
+ pos2 = output.find( ' ', pos );
+ int year = output.mid( pos, 4 ).toInt();
+ int month = output.mid( pos+5, 2 ).toInt();
+ int day = output.mid( pos+8, 2 ).toInt();
+ QDateTime dt( QDate( year, month, day ), QTime( 00, 00 ) );
+ QDateTime epoch( QDate( 1970, 01, 01 ), QTime( 00, 00 ) );
+ // The calculated creation date isn't exactly correct because QDateTime
+ // doesn't know anything about timezones and always assumes local time
+ // although epoch is of course UTC. But as PGP 6 anyway doesn't print
+ // the time this doesn't matter too much.
+ subkey->setCreationDate( epoch.secsTo( dt ) );
+
+ // User ID or key properties
+ pos = pos2 + 1;
+ while( output[pos] == ' ' )
+ pos++;
+ while( pos < eol )
+ { // loop over User ID resp. key properties
+ if( !strncmp( output.data() + pos, "*** KEY REVOKED ***", 19 ) )
+ {
+ sign = false;
+ encr = false;
+ subkey->setRevoked( true );
+ key->setRevoked( true );
+ pos += 19;
+ //kdDebug(5100) << "Key was revoked.\n";
+ }
+ else if( !strncmp( output.data() + pos, "*** KEY EXPIRED ***", 19 ) )
+ {
+ sign = false;
+ encr = false;
+ subkey->setExpired( true );
+ key->setExpired( true );
+ pos += 19;
+ //kdDebug(5100) << "Key has expired.\n";
+ }
+ else if( !strncmp( output.data() + pos, "expires ", 8 ) )
+ {
+ pos += 8;
+ int year = output.mid( pos, 4 ).toInt();
+ int month = output.mid( pos+5, 2 ).toInt();
+ int day = output.mid( pos+8, 2 ).toInt();
+ QDateTime dt( QDate( year, month, day ), QTime( 00, 00 ) );
+ // Here the same comments as for the creation date are valid.
+ subkey->setExpirationDate( epoch.secsTo( dt ) );
+ pos += 10;
+ //kdDebug(5100) << "Key expires...\n";
+ }
+ else if( !strncmp( output.data() + pos, "*** DEFAULT SIGNING KEY ***", 27 ) )
+ {
+ pos += 27;
+ //kdDebug(5100) << "Key is default signing key.\n";
+ }
+ else
+ {
+ QCString uid = output.mid( pos, eol-pos );
+ key->addUserID( uid );
+ pos = eol;
+ //kdDebug(5100) << "User ID:"<<uid<<endl;
+ }
+ }
+ // set key capabilities of the primary subkey
+ subkey->setCanEncrypt( encr );
+ subkey->setCanSign( sign );
+ subkey->setCanCertify( sign );
+ // remember the global key capabilities
+ canSign = sign;
+ canEncr = encr;
+ }
+ else if( !strncmp( output.data() + offset, "DSS", 3 ) ||
+ !strncmp( output.data() + offset, " DH", 3 ) ||
+ !strncmp( output.data() + offset, "RSA", 3 ) )
+ { // line contains secondary key data (or data for the next key)
+ if( fpr )
+ break; // here begins the next key's data
+ //kdDebug(5100)<<"Secondary key data:\n";
+
+ if( key == 0 )
+ break;
+
+ bool sign = false;
+ bool encr = false;
+
+ // set default key capabilities
+ if( !strncmp( output.data() + offset, "DSS", 3 ) )
+ sign = true;
+ if( !strncmp( output.data() + offset, " DH", 3 ) )
+ encr = true;
+ if( !strncmp( output.data() + offset, "RSA", 3 ) )
+ {
+ sign = true;
+ encr = true;
+ }
+
+ int pos, pos2;
+
+ // Key Length of secondary key (ignored)
+ pos = offset + 4;
+ while( output[pos] == ' ' )
+ pos++;
+ pos2 = output.find( ' ', pos );
+
+ // Key ID (ignored as it is anyway equal to the primary key id)
+ pos = pos2 + 1;
+ while( output[pos] == ' ' )
+ pos++;
+ pos2 = output.find( ' ', pos );
+
+ // Creation Date of secondary key (ignored)
+ pos = pos2 + 1;
+ while( output[pos] == ' ' )
+ pos++;
+ pos2 = output.find( ' ', pos );
+
+ // User ID or key properties
+ pos = pos2 + 1;
+ while( output[pos] == ' ' )
+ pos++;
+ while( pos < eol )
+ { // loop over User ID resp. key properties
+ if( !strncmp( output.data() + pos, "*** KEY REVOKED ***", 19 ) )
+ {
+ sign = false;
+ encr = false;
+ pos += 19;
+ //kdDebug(5100) << "Key was revoked.\n";
+ }
+ else if( !strncmp( output.data() + pos, "*** KEY EXPIRED ***", 19 ) )
+ {
+ sign = false;
+ encr = false;
+ pos += 19;
+ //kdDebug(5100) << "Key has expired.\n";
+ }
+ else if( !strncmp( output.data() + pos, "expires ", 8 ) )
+ {
+ pos += 18; // skip the expiration date
+ //kdDebug(5100) << "Key expires...\n";
+ }
+ else if( !strncmp( output.data() + pos, "*** DEFAULT SIGNING KEY ***", 27 ) )
+ {
+ pos += 27;
+ //kdDebug(5100) << "Key is default signing key.\n";
+ }
+ else
+ {
+ QCString uid = output.mid( pos, eol-pos );
+ key->addUserID( uid );
+ pos = eol;
+ //kdDebug(5100) << "User ID:"<<uid<<endl;
+ }
+ }
+ // store the global key capabilities
+ canSign = canSign || sign;
+ canEncr = canEncr || encr;
+ }
+ else if( !strncmp( output.data() + offset, "Unknown type", 12 ) )
+ { // line contains key data of unknown type (ignored)
+ kdDebug(5100)<<"Unknown key type.\n";
+ }
+ else if( output[offset] == ' ' )
+ { // line contains additional key data
+ if( key == 0 )
+ break;
+ //kdDebug(5100)<<"Additional key data:\n";
+
+ int pos = offset + 1;
+ while( output[pos] == ' ' )
+ pos++;
+
+ if( !strncmp( output.data() + pos, "Key fingerprint = ", 18 ) )
+ { // line contains a fingerprint
+ // Example:
+ // Key fingerprint = D0 6C BB 3A F5 16 82 C4 F3 A0 8A B3 7B 16 99 70
+
+ fpr = true; // we found a fingerprint
+
+ pos += 18;
+ QCString fingerprint = output.mid( pos, eol-pos );
+ // remove white space from the fingerprint
+ for ( int idx = 0 ; (idx = fingerprint.find(' ', idx)) >= 0 ; )
+ fingerprint.replace( idx, 1, "" );
+
+ //kdDebug(5100)<<"Fingerprint: "<<fingerprint<<endl;
+ assert( subkey != 0 );
+ subkey->setFingerprint( fingerprint );
+ }
+ else
+ { // line contains an additional user id
+ // Example:
+ // Test key (2nd user ID) <abc@xyz>
+
+ //kdDebug(5100)<<"User ID: "<<output.mid( pos, eol-pos )<<endl;
+ key->addUserID( output.mid( pos, eol-pos ) );
+ }
+ }
+ else if( !strncmp( output.data() + offset, "sig", 3 ) )
+ { // line contains signature data (ignored)
+ //kdDebug(5100)<<"Signature.\n";
+ }
+ else // end of key data
+ break;
+
+ firstLine = false;
+ offset = eol + 1;
+ }
+
+ if( key != 0 )
+ {
+ // set the global key capabilities
+ key->setCanEncrypt( canEncr );
+ key->setCanSign( canSign );
+ key->setCanCertify( canSign );
+ //kdDebug(5100)<<"Key capabilities: "<<(canEncr?"E":"")<<(canSign?"SC":"")<<endl;
+ }
+
+ return key;
+}
+
+
+Key*
+Base6::parseSingleKey( const QCString& output, Key* key /* = 0 */ )
+{
+ int offset;
+
+ // search start of header line
+ if( !strncmp( output.data(), "Type bits", 9 ) )
+ offset = 9;
+ else
+ {
+ offset = output.find( "\nType bits" );
+ if( offset == -1 )
+ return 0;
+ else
+ offset += 10;
+ }
+
+ // key data begins in the next line
+ offset = output.find( '\n', offset ) + 1;
+ if( offset == 0 )
+ return 0;
+
+ key = parseKeyData( output, offset, key );
+
+ //kdDebug(5100) << "finished parsing keys" << endl;
+
+ return key;
+}
+
+
+KeyList
+Base6::parseKeyList( const QCString& output, bool secretKeys )
+{
+ kdDebug(5100) << "Kpgp::Base6::parseKeyList()" << endl;
+ KeyList keys;
+ Key *key = 0;
+ int offset;
+
+ // search start of header line
+ if( !strncmp( output.data(), "Type bits", 9 ) )
+ offset = 0;
+ else
+ {
+ offset = output.find( "\nType bits" ) + 1;
+ if( offset == 0 )
+ return keys;
+ }
+
+ // key data begins in the next line
+ offset = output.find( '\n', offset ) + 1;
+ if( offset == -1 )
+ return keys;
+
+ do
+ {
+ key = parseKeyData( output, offset );
+ if( key != 0 )
+ {
+ key->setSecret( secretKeys );
+ keys.append( key );
+ }
+ }
+ while( key != 0 );
+
+ //kdDebug(5100) << "finished parsing keys" << endl;
+
+ return keys;
+}
+
+
+void
+Base6::parseTrustDataForKey( Key* key, const QCString& str )
+{
+ if( ( key == 0 ) || str.isEmpty() )
+ return;
+
+ QCString keyID = "0x" + key->primaryKeyID();
+ UserIDList userIDs = key->userIDs();
+
+ // search the start of the trust data
+ int offset = str.find( "\n\n KeyID" );
+ if( offset == -1 )
+ return;
+
+ offset = str.find( '\n', offset ) + 1;
+ if( offset == 0 )
+ return;
+
+ bool ultimateTrust = false;
+ if( !strncmp( str.data() + offset+13, "ultimate", 8 ) )
+ ultimateTrust = true;
+
+ while( true )
+ { // loop over all trust information about this key
+
+ int eol;
+
+ // search the end of the current line
+ if( ( eol = str.find( '\n', offset ) ) == -1 )
+ break;
+
+ if( str[offset+23] != ' ' )
+ { // line contains a validity value for a user ID
+
+ // determine the validity
+ Validity validity = KPGP_VALIDITY_UNKNOWN;
+ if( !strncmp( str.data() + offset+23, "complete", 8 ) )
+ if( ultimateTrust )
+ validity = KPGP_VALIDITY_ULTIMATE;
+ else
+ validity = KPGP_VALIDITY_FULL;
+ else if( !strncmp( str.data() + offset+23, "marginal", 8 ) )
+ validity = KPGP_VALIDITY_MARGINAL;
+ else if( !strncmp( str.data() + offset+23, "invalid", 7 ) )
+ validity = KPGP_VALIDITY_UNDEFINED;
+
+ // determine the user ID
+ int pos = offset + 33;
+ QString uid = str.mid( pos, eol-pos );
+
+ // set the validity of the corresponding user ID
+ for( UserIDListIterator it( userIDs ); it.current(); ++it )
+ if( (*it)->text() == uid )
+ {
+ kdDebug(5100)<<"Setting the validity of "<<uid<<" to "<<validity<<endl;
+ (*it)->setValidity( validity );
+ break;
+ }
+ }
+
+ offset = eol + 1;
+ }
+}
+
+
+} // namespace Kpgp
diff --git a/libkpgp/kpgpbaseG.cpp b/libkpgp/kpgpbaseG.cpp
new file mode 100644
index 00000000..0a66a065
--- /dev/null
+++ b/libkpgp/kpgpbaseG.cpp
@@ -0,0 +1,855 @@
+/*
+ kpgpbaseG.cpp
+
+ Copyright (C) 2001,2002 the KPGP authors
+ See file AUTHORS.kpgp for details
+
+ This file is part of KPGP, the KDE PGP/GnuPG support library.
+
+ KPGP is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "kpgpbase.h"
+#include "kpgp.h"
+
+#include <klocale.h>
+#include <kprocess.h>
+#include <kdebug.h>
+
+#include <qtextcodec.h>
+
+#include <string.h> /* strncmp */
+
+namespace Kpgp {
+
+BaseG::BaseG()
+ : Base()
+{
+ // determine the version of gpg (the method is equivalent to gpgme's method)
+ runGpg( "--version", 0 );
+ int eol = output.find( '\n' );
+ if( eol > 0 ) {
+ int pos = output.findRev( ' ', eol - 1 );
+ if( pos != -1 ) {
+ mVersion = output.mid( pos + 1, eol - pos - 1 );
+ kdDebug(5100) << "found GnuPG " << mVersion << endl;
+ }
+ }
+}
+
+
+BaseG::~BaseG()
+{
+}
+
+
+int
+BaseG::encrypt( Block& block, const KeyIDList& recipients )
+{
+ return encsign( block, recipients, 0 );
+}
+
+
+int
+BaseG::clearsign( Block& block, const char *passphrase )
+{
+ return encsign( block, KeyIDList(), passphrase );
+}
+
+
+int
+BaseG::encsign( Block& block, const KeyIDList& recipients,
+ const char *passphrase )
+{
+ QCString cmd;
+ int exitStatus = 0;
+
+ if(!recipients.isEmpty() && passphrase != 0)
+ cmd = "--batch --armor --sign --encrypt --textmode";
+ else if(!recipients.isEmpty())
+ cmd = "--batch --armor --encrypt --textmode";
+ else if(passphrase != 0)
+ cmd = "--batch --escape-from --clearsign";
+ else
+ {
+ kdDebug(5100) << "kpgpbase: Neither recipients nor passphrase specified." << endl;
+ return OK;
+ }
+
+ if(passphrase != 0)
+ cmd += addUserId();
+
+ if(!recipients.isEmpty())
+ {
+ cmd += " --set-filename stdin";
+
+ QCString pgpUser = Module::getKpgp()->user();
+ if(Module::getKpgp()->encryptToSelf() && !pgpUser.isEmpty()) {
+ cmd += " -r 0x";
+ cmd += pgpUser;
+ }
+
+ for( KeyIDList::ConstIterator it = recipients.begin();
+ it != recipients.end(); ++it ) {
+ cmd += " -r 0x";
+ cmd += (*it);
+ }
+ }
+
+ clear();
+ input = block.text();
+ exitStatus = runGpg(cmd.data(), passphrase);
+ if( !output.isEmpty() )
+ block.setProcessedText( output );
+ block.setError( error );
+
+ if( exitStatus != 0 )
+ {
+ // this error message is later hopefully overwritten
+ errMsg = i18n( "Unknown error." );
+ status = ERROR;
+ }
+
+#if 0
+ // #### FIXME: As we check the keys ourselves the following problems
+ // shouldn't occur. Therefore I don't handle them for now.
+ // IK 01/2002
+ if(!recipients.isEmpty())
+ {
+ int index = 0;
+ bool bad = FALSE;
+ unsigned int num = 0;
+ QCString badkeys = "";
+ // Examples:
+ // gpg: 0x12345678: skipped: public key not found
+ // gpg: 0x12345678: skipped: public key is disabled
+ // gpg: 0x12345678: skipped: unusable public key
+ // (expired or revoked key)
+ // gpg: 23456789: no info to calculate a trust probability
+ // (untrusted key, 23456789 is the key Id of the encryption sub key)
+ while((index = error.find("skipped: ",index)) != -1)
+ {
+ bad = TRUE;
+ index = error.find('\'',index);
+ int index2 = error.find('\'',index+1);
+ badkeys += error.mid(index, index2-index+1) + ", ";
+ num++;
+ }
+ if(bad)
+ {
+ badkeys.stripWhiteSpace();
+ if(num == recipients.count())
+ errMsg = i18n("Could not find public keys matching the userid(s)\n"
+ "%1;\n"
+ "the message is not encrypted.")
+ .arg( badkeys.data() );
+ else
+ errMsg = i18n("Could not find public keys matching the userid(s)\n"
+ "%1;\n"
+ "these persons will not be able to read the message.")
+ .arg( badkeys.data() );
+ status |= MISSINGKEY;
+ status |= ERROR;
+ }
+ }
+#endif
+ if( passphrase != 0 )
+ {
+ // Example 1 (bad passphrase, clearsign only):
+ // gpg: skipped `0x12345678': bad passphrase
+ // gpg: [stdin]: clearsign failed: bad passphrase
+ // Example 2 (bad passphrase, sign & encrypt):
+ // gpg: skipped `0x12345678': bad passphrase
+ // gpg: [stdin]: sign+encrypt failed: bad passphrase
+ // Example 3 (unusable secret key, clearsign only):
+ // gpg: skipped `0x12345678': unusable secret key
+ // gpg: [stdin]: clearsign failed: unusable secret key
+ // Example 4 (unusable secret key, sign & encrypt):
+ // gpg: skipped `0xAC0EB35D': unusable secret key
+ // gpg: [stdin]: sign+encrypt failed: unusable secret key
+ if( error.find("bad passphrase") != -1 )
+ {
+ errMsg = i18n("Signing failed because the passphrase is wrong.");
+ status |= BADPHRASE;
+ status |= ERR_SIGNING;
+ status |= ERROR;
+ }
+ else if( error.find("unusable secret key") != -1 )
+ {
+ errMsg = i18n("Signing failed because your secret key is unusable.");
+ status |= ERR_SIGNING;
+ status |= ERROR;
+ }
+ else if( !( status & ERROR ) )
+ {
+ //kdDebug(5100) << "Base: Good Passphrase!" << endl;
+ status |= SIGNED;
+ }
+ }
+
+ //kdDebug(5100) << "status = " << status << endl;
+ block.setStatus( status );
+ return status;
+}
+
+
+int
+BaseG::decrypt( Block& block, const char *passphrase )
+{
+ int index, index2;
+ int exitStatus = 0;
+
+ clear();
+ input = block.text();
+ exitStatus = runGpg("--batch --decrypt", passphrase);
+ if( !output.isEmpty() && ( error.find( "gpg: quoted printable" ) == -1 ) )
+ block.setProcessedText( output );
+ block.setError( error );
+
+ if(exitStatus == -1) {
+ errMsg = i18n("Error running gpg");
+ status = RUN_ERR;
+ block.setStatus( status );
+ return status;
+ }
+
+ // Example 1 (good passphrase, decryption successful):
+ // gpg: encrypted with 2048-bit ELG-E key, ID 12345678, created 2000-11-11
+ // "Foo Bar <foo@bar.xyz>"
+ //
+ // Example 2 (bad passphrase):
+ // gpg: encrypted with 1024-bit RSA key, ID 12345678, created 1991-01-01
+ // "Foo Bar <foo@bar.xyz>"
+ // gpg: public key decryption failed: bad passphrase
+ // gpg: decryption failed: secret key not available
+ //
+ // Example 3 (no secret key available):
+ // gpg: encrypted with RSA key, ID 12345678
+ // gpg: decryption failed: secret key not available
+ //
+ // Example 4 (good passphrase for second key, decryption successful):
+ // gpg: encrypted with 2048-bit ELG-E key, ID 12345678, created 2000-01-01
+ // "Foo Bar (work) <foo@bar.xyz>"
+ // gpg: public key decryption failed: bad passphrase
+ // gpg: encrypted with 2048-bit ELG-E key, ID 23456789, created 2000-02-02
+ // "Foo Bar (home) <foo@bar.xyz>"
+ if( error.find( "gpg: encrypted with" ) != -1 )
+ {
+ //kdDebug(5100) << "kpgpbase: message is encrypted" << endl;
+ status |= ENCRYPTED;
+ if( error.find( "\ngpg: decryption failed" ) != -1 )
+ {
+ if( ( index = error.find( "bad passphrase" ) ) != -1 )
+ {
+ if( passphrase != 0 )
+ {
+ errMsg = i18n( "Bad passphrase; could not decrypt." );
+ kdDebug(5100) << "Base: passphrase is bad" << endl;
+ status |= BADPHRASE;
+ status |= ERROR;
+ }
+ else
+ {
+ // Search backwards the user ID of the needed key
+ index2 = error.findRev('"', index) - 1;
+ index = error.findRev(" \"", index2) + 7;
+ // The conversion from UTF8 is necessary because gpg stores and
+ // prints user IDs in UTF8
+ block.setRequiredUserId( QString::fromUtf8( error.mid( index, index2 - index + 1 ) ) );
+ kdDebug(5100) << "Base: key needed is \"" << block.requiredUserId() << "\"!" << endl;
+ }
+ }
+ else if( error.find( "secret key not available" ) != -1 )
+ {
+ // no secret key fitting this message
+ status |= NO_SEC_KEY;
+ status |= ERROR;
+ errMsg = i18n("You do not have the secret key needed to decrypt this message.");
+ kdDebug(5100) << "Base: no secret key for this message" << endl;
+ }
+ }
+ // check for persons
+#if 0
+ // ##### FIXME: This information is anyway currently not used
+ // I'll change it to always determine the recipients.
+ index = error.find("can only be read by:");
+ if(index != -1)
+ {
+ index = error.find('\n',index);
+ int end = error.find("\n\n",index);
+
+ mRecipients.clear();
+ while( (index2 = error.find('\n',index+1)) <= end )
+ {
+ QCString item = error.mid(index+1,index2-index-1);
+ item.stripWhiteSpace();
+ mRecipients.append(item);
+ index = index2;
+ }
+ }
+#endif
+ }
+
+ // Example 1 (unknown signature key):
+ // gpg: Signature made Wed 02 Jan 2002 11:26:33 AM CET using DSA key ID 2E250C64
+ // gpg: Can't check signature: public key not found
+ if((index = error.find("Signature made")) != -1)
+ {
+ //kdDebug(5100) << "Base: message is signed" << endl;
+ status |= SIGNED;
+ // get signature date and signature key ID
+ // Example: Signature made Sun 06 May 2001 03:49:27 PM CEST using DSA key ID 12345678
+ index2 = error.find("using", index+15);
+ block.setSignatureDate( error.mid(index+15, index2-(index+15)-1) );
+ kdDebug(5100) << "Message was signed on '" << block.signatureDate() << "'\n";
+ index2 = error.find("key ID ", index2) + 7;
+ block.setSignatureKeyId( error.mid(index2,8) );
+ kdDebug(5100) << "Message was signed with key '" << block.signatureKeyId() << "'\n";
+ // move index to start of next line
+ index = error.find('\n', index2)+1;
+
+ if ((error.find("Key matching expected", index) != -1)
+ || (error.find("Can't check signature", index) != -1))
+ {
+ status |= UNKNOWN_SIG;
+ status |= GOODSIG;
+ block.setSignatureUserId( QString::null );
+ }
+ else if( error.find("Good signature", index) != -1 )
+ {
+ status |= GOODSIG;
+ // get the primary user ID of the signer
+ index = error.find('"',index);
+ index2 = error.find('\n',index+1);
+ index2 = error.findRev('"', index2-1);
+ block.setSignatureUserId( error.mid( index+1, index2-index-1 ) );
+ }
+ else if( error.find("BAD signature", index) != -1 )
+ {
+ //kdDebug(5100) << "BAD signature" << endl;
+ status |= ERROR;
+ // get the primary user ID of the signer
+ index = error.find('"',index);
+ index2 = error.find('\n',index+1);
+ index2 = error.findRev('"', index2-1);
+ block.setSignatureUserId( error.mid( index+1, index2-index-1 ) );
+ }
+ else if( error.find("Can't find the right public key", index) != -1 )
+ {
+ // #### fix this hack
+ // I think this can't happen anymore because if the pubring is missing
+ // the current GnuPG creates a new empty one.
+ status |= UNKNOWN_SIG;
+ status |= GOODSIG; // this is a hack...
+ block.setSignatureUserId( i18n("??? (file ~/.gnupg/pubring.gpg not found)") );
+ }
+ else
+ {
+ status |= ERROR;
+ block.setSignatureUserId( QString::null );
+ }
+ }
+ //kdDebug(5100) << "status = " << status << endl;
+ block.setStatus( status );
+ return status;
+}
+
+
+Key*
+BaseG::readPublicKey( const KeyID& keyID,
+ const bool readTrust /* = false */,
+ Key* key /* = 0 */ )
+{
+ int exitStatus = 0;
+
+ status = 0;
+ if( readTrust )
+ exitStatus = runGpg( "--batch --list-public-keys --with-fingerprint --with-colons --fixed-list-mode 0x" + keyID, 0, true );
+ else
+ exitStatus = runGpg( "--batch --list-public-keys --with-fingerprint --with-colons --fixed-list-mode --no-expensive-trust-checks 0x" + keyID, 0, true );
+
+ if(exitStatus != 0) {
+ status = ERROR;
+ return 0;
+ }
+
+ int offset;
+ // search start of key data
+ if( !strncmp( output.data(), "pub:", 4 ) )
+ offset = 0;
+ else {
+ offset = output.find( "\npub:" );
+ if( offset == -1 )
+ return 0;
+ else
+ offset++;
+ }
+
+ key = parseKeyData( output, offset, key );
+
+ return key;
+}
+
+
+KeyList
+BaseG::publicKeys( const QStringList & patterns )
+{
+ int exitStatus = 0;
+
+ // the option --with-colons should be used for interprocess communication
+ // with gpg (according to Werner Koch)
+ QCString cmd = "--batch --list-public-keys --with-fingerprint --with-colons "
+ "--fixed-list-mode --no-expensive-trust-checks";
+ for ( QStringList::ConstIterator it = patterns.begin();
+ it != patterns.end(); ++it ) {
+ cmd += " ";
+ cmd += KProcess::quote( *it ).local8Bit();
+ }
+ status = 0;
+ exitStatus = runGpg( cmd, 0, true );
+
+ if(exitStatus != 0) {
+ status = ERROR;
+ return KeyList();
+ }
+
+ // now we need to parse the output for public keys
+ KeyList publicKeys = parseKeyList(output, false);
+
+ // sort the list of public keys
+ publicKeys.sort();
+
+ return publicKeys;
+}
+
+
+KeyList
+BaseG::secretKeys( const QStringList & patterns )
+{
+ int exitStatus = 0;
+
+ // the option --with-colons should be used for interprocess communication
+ // with gpg (according to Werner Koch)
+ QCString cmd = "--batch --list-secret-keys --with-fingerprint --with-colons "
+ "--fixed-list-mode";
+ for ( QStringList::ConstIterator it = patterns.begin();
+ it != patterns.end(); ++it ) {
+ cmd += " ";
+ cmd += KProcess::quote( *it ).local8Bit();
+ }
+ status = 0;
+ exitStatus = runGpg( cmd, 0, true );
+
+ if(exitStatus != 0) {
+ status = ERROR;
+ return KeyList();
+ }
+
+ // now we need to parse the output for secret keys
+ KeyList secretKeys = parseKeyList(output, true);
+
+ // sort the list of secret keys
+ secretKeys.sort();
+
+ return secretKeys;
+}
+
+
+int
+BaseG::signKey(const KeyID& keyID, const char *passphrase)
+{
+ QCString cmd;
+ int exitStatus = 0;
+
+ cmd = "--batch";
+ cmd += addUserId();
+ cmd += " --sign-key 0x";
+ cmd += keyID;
+
+ status = 0;
+ exitStatus = runGpg(cmd.data(), passphrase);
+
+ if (exitStatus != 0)
+ status = ERROR;
+
+ return status;
+}
+
+
+QCString
+BaseG::getAsciiPublicKey(const KeyID& keyID)
+{
+ int exitStatus = 0;
+
+ if (keyID.isEmpty())
+ return QCString();
+
+ status = 0;
+ exitStatus = runGpg("--batch --armor --export 0x" + keyID, 0, true);
+
+ if(exitStatus != 0) {
+ status = ERROR;
+ return QCString();
+ }
+
+ return output;
+}
+
+
+Key*
+BaseG::parseKeyData( const QCString& output, int& offset, Key* key /* = 0 */ )
+// This function parses the data for a single key which is output by GnuPG
+// with the following command line arguments:
+// --batch --list-public-keys --with-fingerprint --with-colons
+// --fixed-list-mode [--no-expensive-trust-checks]
+// It expects the key data to start at offset and returns the start of
+// the next key's data in offset.
+// Subkeys are currently ignored.
+{
+ int index = offset;
+
+ if( ( strncmp( output.data() + offset, "pub:", 4 ) != 0 )
+ && ( strncmp( output.data() + offset, "sec:", 4 ) != 0 ) ) {
+ return 0;
+ }
+
+ if( key == 0 )
+ key = new Key();
+ else
+ key->clear();
+
+ QCString keyID;
+ bool firstKey = true;
+
+ while( true )
+ {
+ int eol;
+ // search the end of the current line
+ if( ( eol = output.find( '\n', index ) ) == -1 )
+ break;
+
+ bool bIsPublicKey = false;
+ if( ( bIsPublicKey = !strncmp( output.data() + index, "pub:", 4 ) )
+ || !strncmp( output.data() + index, "sec:", 4 ) )
+ { // line contains primary key data
+ // Example: pub:f:1024:17:63CB691DFAEBD5FC:860451781::379:-:::scESC:
+
+ // abort parsing if we found the start of the next key
+ if( !firstKey )
+ break;
+ firstKey = false;
+
+ key->setSecret( !bIsPublicKey );
+
+ Subkey *subkey = new Subkey( QCString(), !bIsPublicKey );
+
+ int pos = index + 4; // begin of 2nd field
+ int pos2 = output.find( ':', pos );
+ for( int field = 2; field <= 12; field++ )
+ {
+ switch( field )
+ {
+ case 2: // the calculated trust
+ if( pos2 > pos )
+ {
+ switch( output[pos] )
+ {
+ case 'o': // unknown (this key is new to the system)
+ break;
+ case 'i': // the key is invalid, e.g. missing self-signature
+ subkey->setInvalid( true );
+ key->setInvalid( true );
+ break;
+ case 'd': // the key has been disabled
+ subkey->setDisabled( true );
+ key->setDisabled( true );
+ break;
+ case 'r': // the key has been revoked
+ subkey->setRevoked( true );
+ key->setRevoked( true );
+ break;
+ case 'e': // the key has expired
+ subkey->setExpired( true );
+ key->setExpired( true );
+ break;
+ case '-': // undefined (no path leads to the key)
+ case 'q': // undefined (no trusted path leads to the key)
+ case 'n': // don't trust this key at all
+ case 'm': // the key is marginally trusted
+ case 'f': // the key is fully trusted
+ case 'u': // the key is ultimately trusted (secret key available)
+ // These values are ignored since we determine the key trust
+ // from the trust values of the user ids.
+ break;
+ default:
+ kdDebug(5100) << "Unknown trust value\n";
+ }
+ }
+ break;
+ case 3: // length of key in bits
+ if( pos2 > pos )
+ subkey->setKeyLength( output.mid( pos, pos2-pos ).toUInt() );
+ break;
+ case 4: // the key algorithm
+ if( pos2 > pos )
+ subkey->setKeyAlgorithm( output.mid( pos, pos2-pos ).toUInt() );
+ break;
+ case 5: // the long key id
+ keyID = output.mid( pos, pos2-pos );
+ subkey->setKeyID( keyID );
+ break;
+ case 6: // the creation date (in seconds since 1970-01-01 00:00:00)
+ if( pos2 > pos )
+ subkey->setCreationDate( output.mid( pos, pos2-pos ).toLong() );
+ break;
+ case 7: // the expiration date (in seconds since 1970-01-01 00:00:00)
+ if( pos2 > pos )
+ subkey->setExpirationDate( output.mid( pos, pos2-pos ).toLong() );
+ else
+ subkey->setExpirationDate( -1 ); // key expires never
+ break;
+ case 8: // local ID (ignored)
+ case 9: // Ownertrust (ignored for now)
+ case 10: // User-ID (always empty in --fixed-list-mode)
+ case 11: // signature class (always empty except for key signatures)
+ break;
+ case 12: // key capabilities
+ for( int i=pos; i<pos2; i++ )
+ switch( output[i] )
+ {
+ case 'e':
+ subkey->setCanEncrypt( true );
+ break;
+ case 's':
+ subkey->setCanSign( true );
+ break;
+ case 'c':
+ subkey->setCanCertify( true );
+ break;
+ case 'E':
+ key->setCanEncrypt( true );
+ break;
+ case 'S':
+ key->setCanSign( true );
+ break;
+ case 'C':
+ key->setCanCertify( true );
+ break;
+ default:
+ kdDebug(5100) << "Unknown key capability\n";
+ }
+ break;
+ }
+ pos = pos2 + 1;
+ pos2 = output.find( ':', pos );
+ }
+ key->addSubkey( subkey );
+ }
+ else if( !strncmp( output.data() + index, "uid:", 4 ) )
+ { // line contains a user id
+ // Example: uid:f::::::::Philip R. Zimmermann <prz@pgp.com>:
+
+ UserID *userID = new UserID( "" );
+
+ int pos = index + 4; // begin of 2nd field
+ int pos2 = output.find( ':', pos );
+ for( int field=2; field <= 10; field++ )
+ {
+ switch( field )
+ {
+ case 2: // the calculated trust
+ if( pos2 > pos )
+ {
+ switch( output[pos] )
+ {
+ case 'i': // the user id is invalid, e.g. missing self-signature
+ userID->setInvalid( true );
+ break;
+ case 'r': // the user id has been revoked
+ userID->setRevoked( true );
+ break;
+ case '-': // undefined (no path leads to the key)
+ case 'q': // undefined (no trusted path leads to the key)
+ userID->setValidity( KPGP_VALIDITY_UNDEFINED );
+ break;
+ case 'n': // don't trust this key at all
+ userID->setValidity( KPGP_VALIDITY_NEVER );
+ break;
+ case 'm': // the key is marginally trusted
+ userID->setValidity( KPGP_VALIDITY_MARGINAL );
+ break;
+ case 'f': // the key is fully trusted
+ userID->setValidity( KPGP_VALIDITY_FULL );
+ break;
+ case 'u': // the key is ultimately trusted (secret key available)
+ userID->setValidity( KPGP_VALIDITY_ULTIMATE );
+ break;
+ default:
+ kdDebug(5100) << "Unknown trust value\n";
+ }
+ }
+ break;
+ case 3: // these fields are empty
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ case 9:
+ break;
+ case 10: // User-ID
+ QCString uid = output.mid( pos, pos2-pos );
+ // replace "\xXX" with the corresponding character;
+ // other escaped characters, i.e. \n, \r etc., are ignored
+ // because they shouldn't appear in user IDs
+ for ( int idx = 0 ; (idx = uid.find( "\\x", idx )) >= 0 ; ++idx ) {
+ char str[2] = "x";
+ str[0] = (char) QString( uid.mid( idx + 2, 2 ) ).toShort( 0, 16 );
+ uid.replace( idx, 4, str );
+ }
+ QString uidString = QString::fromUtf8( uid.data() );
+ // check whether uid was utf-8 encoded
+ bool isUtf8 = true;
+ for ( unsigned int i = 0; i + 1 < uidString.length(); ++i ) {
+ if ( uidString[i].unicode() == 0xdbff &&
+ uidString[i+1].row() == 0xde ) {
+ // we found a non-Unicode character (see QString::fromUtf8())
+ isUtf8 = false;
+ break;
+ }
+ }
+ if( !isUtf8 ) {
+ // The user id isn't utf-8 encoded. It was most likely
+ // created with PGP which either used latin1 or koi8-r.
+ kdDebug(5100) << "User Id '" << uid
+ << "' doesn't seem to be utf-8 encoded." << endl;
+
+ // We determine the ratio between non-ASCII and ASCII chars.
+ // A koi8-r user id should have lots of non-ASCII chars.
+ int nonAsciiCount = 0, asciiCount = 0;
+
+ // We only look at the first part of the user id (i. e. everything
+ // before the email address resp. before a comment)
+ for( signed char* ch = (signed char*)uid.data();
+ *ch && ( *ch != '(' ) && ( *ch != '<' );
+ ++ch ) {
+ if( ( ( *ch >= 'A' ) && ( *ch <= 'Z' ) )
+ || ( ( *ch >= 'a' ) && ( *ch <= 'z' ) ) )
+ ++asciiCount;
+ else if( *ch < 0 )
+ ++nonAsciiCount;
+ }
+ kdDebug(5100) << "ascii-nonAscii ratio : " << asciiCount
+ << ":" << nonAsciiCount << endl;
+ if( nonAsciiCount > asciiCount ) {
+ // assume koi8-r encoding
+ kdDebug(5100) << "Assume koi8-r encoding." << endl;
+ QTextCodec *codec = QTextCodec::codecForName("KOI8-R");
+ uidString = codec->toUnicode( uid.data() );
+ // check the case of the first two characters to find out
+ // whether the user id is probably CP1251 encoded (for some
+ // reason in CP1251 the lower case characters have smaller
+ // codes than the upper case characters, so if the first char
+ // of the koi8-r decoded user id is lower case and the second
+ // char is upper case then it's likely that the user id is
+ // CP1251 encoded)
+ if( ( uidString.length() >= 2 )
+ && ( uidString[0].lower() == uidString[0] )
+ && ( uidString[1].upper() == uidString[1] ) ) {
+ // koi8-r decoded user id has inverted case, so assume
+ // CP1251 encoding
+ kdDebug(5100) << "No, it doesn't seem to be koi8-r. "
+ "Use CP 1251 instead." << endl;
+ QTextCodec *codec = QTextCodec::codecForName("CP1251");
+ uidString = codec->toUnicode( uid.data() );
+ }
+ }
+ else {
+ // assume latin1 encoding
+ kdDebug(5100) << "Assume latin1 encoding." << endl;
+ uidString = QString::fromLatin1( uid.data() );
+ }
+ }
+ userID->setText( uidString );
+ break;
+ }
+ pos = pos2 + 1;
+ pos2 = output.find( ':', pos );
+ }
+
+ // user IDs are printed in UTF-8 by gpg (if one uses --with-colons)
+ key->addUserID( userID );
+ }
+ else if( !strncmp( output.data() + index, "fpr:", 4 ) )
+ { // line contains a fingerprint
+ // Example: fpr:::::::::17AFBAAF21064E513F037E6E63CB691DFAEBD5FC:
+
+ if (key == 0) // invalid key data
+ break;
+
+ // search the fingerprint (it's in the 10th field)
+ int pos = index + 4;
+ for( int i = 0; i < 8; i++ )
+ pos = output.find( ':', pos ) + 1;
+ int pos2 = output.find( ':', pos );
+
+ key->setFingerprint( keyID, output.mid( pos, pos2-pos ) );
+ }
+ index = eol + 1;
+ }
+
+ //kdDebug(5100) << "finished parsing key data\n";
+
+ offset = index;
+
+ return key;
+}
+
+
+KeyList
+BaseG::parseKeyList( const QCString& output, bool secretKeys )
+{
+ KeyList keys;
+ Key *key = 0;
+ int offset;
+
+ // search start of key data
+ if( !strncmp( output.data(), "pub:", 4 )
+ || !strncmp( output.data(), "sec:", 4 ) )
+ offset = 0;
+ else {
+ if( secretKeys )
+ offset = output.find( "\nsec:" );
+ else
+ offset = output.find( "\npub:" );
+ if( offset == -1 )
+ return keys;
+ else
+ offset++;
+ }
+
+ do {
+ key = parseKeyData( output, offset );
+ if( key != 0 )
+ keys.append( key );
+ }
+ while( key != 0 );
+
+ //kdDebug(5100) << "finished parsing keys" << endl;
+
+ return keys;
+}
+
+
+} // namespace Kpgp
diff --git a/libkpgp/kpgpblock.cpp b/libkpgp/kpgpblock.cpp
new file mode 100644
index 00000000..8bf55119
--- /dev/null
+++ b/libkpgp/kpgpblock.cpp
@@ -0,0 +1,136 @@
+/*
+ kpgpblock.cpp
+
+ Copyright (C) 2001,2002 the KPGP authors
+ See file AUTHORS.kpgp for details
+
+ This file is part of KPGP, the KDE PGP/GnuPG support library.
+
+ KPGP is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include "kpgpblock.h"
+#include "kpgp.h"
+
+#include <string.h>
+
+namespace Kpgp {
+
+Block::Block( const QCString& str )
+ : mText(str), mProcessedText(), mError(),
+ mSignatureUserId(), mSignatureKeyId(), mSignatureDate(),
+ mRequiredKey(), mEncryptedFor(),
+ mStatus(0), mHasBeenProcessed(false), mType(NoPgpBlock)
+{
+ mEncryptedFor.setAutoDelete( true );
+}
+
+Block::~Block()
+{
+}
+
+void
+Block::reset()
+{
+ mProcessedText = QCString();
+ mError = QCString();
+ mSignatureUserId = QString::null;
+ mSignatureKeyId = QCString();
+ mSignatureDate = QCString();
+ mRequiredKey = QCString();
+ mEncryptedFor.clear();
+ mStatus = 0;
+ mHasBeenProcessed = false;
+}
+
+void
+Block::clear()
+{
+ reset();
+ mText = QCString();
+ mType = NoPgpBlock;
+}
+
+BlockType
+Block::determineType() const
+{
+ if( !strncmp( mText.data(), "-----BEGIN PGP ", 15 ) )
+ {
+ if( !strncmp( mText.data() + 15, "SIGNED", 6 ) )
+ return ClearsignedBlock;
+ else if( !strncmp( mText.data() + 15, "SIGNATURE", 9 ) )
+ return SignatureBlock;
+ else if( !strncmp( mText.data() + 15, "PUBLIC", 6 ) )
+ return PublicKeyBlock;
+ else if( !strncmp( mText.data() + 15, "PRIVATE", 7 ) ||
+ !strncmp( mText.data() + 15, "SECRET", 6 ) )
+ return PrivateKeyBlock;
+ else if( !strncmp( mText.data() + 15, "MESSAGE", 7 ) )
+ {
+ if( !strncmp( mText.data() + 22, ", PART", 6 ) )
+ return MultiPgpMessageBlock;
+ else
+ return PgpMessageBlock;
+ }
+ else if( !strncmp( mText.data() + 15, "ARMORED FILE", 12 ) )
+ return PgpMessageBlock;
+ else
+ return UnknownBlock;
+ }
+ else
+ return NoPgpBlock;
+}
+
+bool
+Block::decrypt()
+{
+ Kpgp::Module *pgp = Kpgp::Module::getKpgp();
+
+ if( pgp == 0 )
+ return false;
+
+ return pgp->decrypt( *this );
+}
+
+bool
+Block::verify()
+{
+ Kpgp::Module *pgp = Kpgp::Module::getKpgp();
+
+ if( pgp == 0 )
+ return false;
+
+ return pgp->verify( *this );
+}
+
+Kpgp::Result
+Block::clearsign( const QCString& keyId, const QCString& charset )
+{
+ Kpgp::Module *pgp = Kpgp::Module::getKpgp();
+
+ if( pgp == 0 )
+ return Kpgp::Failure;
+
+ return pgp->clearsign( *this, keyId, charset );
+}
+
+Kpgp::Result
+Block::encrypt( const QStringList& receivers, const QCString& keyId,
+ const bool sign, const QCString& charset )
+{
+ Kpgp::Module *pgp = Kpgp::Module::getKpgp();
+
+ if( pgp == 0 )
+ return Kpgp::Failure;
+
+ return pgp->encrypt( *this, receivers, keyId, sign, charset );
+}
+
+} // namespace Kpgp
diff --git a/libkpgp/kpgpblock.h b/libkpgp/kpgpblock.h
new file mode 100644
index 00000000..32978771
--- /dev/null
+++ b/libkpgp/kpgpblock.h
@@ -0,0 +1,356 @@
+/*
+ kpgpblock.h
+
+ Copyright (C) 2001,2002 the KPGP authors
+ See file AUTHORS.kpgp for details
+
+ This file is part of KPGP, the KDE PGP/GnuPG support library.
+
+ KPGP is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#ifndef KPGPBLOCK_H
+#define KPGPBLOCK_H
+
+#include <qcstring.h>
+#include <qstring.h>
+#include <qstrlist.h>
+
+#include <kdepimmacros.h>
+
+//#include <qstringlist.h>
+class QStringList;
+
+#include "kpgp.h"
+
+namespace Kpgp {
+
+typedef enum {
+ UnknownBlock = -1, // BEGIN PGP ???
+ NoPgpBlock = 0,
+ PgpMessageBlock = 1, // BEGIN PGP MESSAGE
+ MultiPgpMessageBlock = 2, // BEGIN PGP MESSAGE, PART X[/Y]
+ SignatureBlock = 3, // BEGIN PGP SIGNATURE
+ ClearsignedBlock = 4, // BEGIN PGP SIGNED MESSAGE
+ PublicKeyBlock = 5, // BEGIN PGP PUBLIC KEY BLOCK
+ PrivateKeyBlock = 6 // BEGIN PGP PRIVATE KEY BLOCK (PGP 2.x: ...SECRET...)
+} BlockType;
+
+typedef enum {
+ OK = 0x0000,
+ CLEARTEXT = 0x0000,
+ RUN_ERR = 0x0001,
+ ERROR = 0x0001,
+ ENCRYPTED = 0x0002,
+ SIGNED = 0x0004,
+ GOODSIG = 0x0008,
+ ERR_SIGNING = 0x0010,
+ UNKNOWN_SIG = 0x0020,
+ BADPHRASE = 0x0040,
+ BADKEYS = 0x0080,
+ NO_SEC_KEY = 0x0100,
+ MISSINGKEY = 0x0200,
+ CANCEL = 0x8000
+} MessageStatus;
+
+class Base;
+class Module;
+
+ /*
+ * BEGIN PGP MESSAGE
+ * Used for signed, encrypted, or compressed files.
+ *
+ * BEGIN PGP PUBLIC KEY BLOCK
+ * Used for armoring public keys
+ *
+ * BEGIN PGP PRIVATE KEY BLOCK (PGP 2.x: BEGIN PGP SECRET KEY BLOCK)
+ * Used for armoring private keys
+ *
+ * BEGIN PGP MESSAGE, PART X/Y
+ * Used for multi-part messages, where the armor is split amongst Y
+ * parts, and this is the Xth part out of Y.
+ *
+ * BEGIN PGP MESSAGE, PART X
+ * Used for multi-part messages, where this is the Xth part of an
+ * unspecified number of parts. Requires the MESSAGE-ID Armor
+ * Header to be used.
+ *
+ * BEGIN PGP SIGNATURE
+ * Used for detached signatures, OpenPGP/MIME signatures, and
+ * signatures following clearsigned messages. Note that PGP 2.x
+ * uses BEGIN PGP MESSAGE for detached signatures.
+ *
+ * BEGIN PGP SIGNED MESSAGE
+ * Used for cleartext signed messages.
+ */
+class KDE_EXPORT Block
+{
+ public:
+
+ Block( const QCString& str = QCString() );
+ ~Block();
+
+ QCString text() const;
+ void setText( const QCString& str );
+
+ void setProcessedText( const QCString& str );
+
+ int status() const;
+ void setStatus( const int status );
+
+ BlockType type();
+
+ /** is the message encrypted ? */
+ bool isEncrypted() const;
+
+ /** is the message signed by someone */
+ bool isSigned() const;
+
+ /** is the signature good ? */
+ bool goodSignature() const;
+
+ /** returns the primary user id of the signer or a null string if we
+ don't have the public key of the signer */
+ QString signatureUserId() const;
+ void setSignatureUserId( const QString& userId );
+
+ /** keyID of signer */
+ QCString signatureKeyId() const;
+ void setSignatureKeyId( const QCString& keyId );
+
+ /** date of the signature
+ WARNING: Will most likely be changed to QDateTime */
+ QCString signatureDate() const;
+ void setSignatureDate( const QCString& date );
+
+ /** the persons who can decrypt the message */
+ const QStrList encryptedFor() const;
+
+ /** shows the secret key which is needed
+ to decrypt the message */
+ QString requiredKey() const;
+ void setRequiredKey( const QCString& keyId );
+
+ QString requiredUserId() const;
+ void setRequiredUserId( const QString& userId );
+
+ QCString error() const;
+ void setError( const QCString& str );
+
+ /** Resets all information about this OpenPGP block */
+ void reset();
+
+ /** decrypts this OpenPGP block if the passphrase is good.
+ returns false otherwise */
+ bool decrypt();
+
+ /** tries to verify this (clearsigned) OpenPGP block */
+ bool verify();
+
+ /** clearsigns this OpenPGP block with the key corresponding to the
+ given key id. The charset is needed to display the text correctly.
+ Returns
+ false if there was an unresolvable error or if signing was canceled
+ true if everything is o.k.
+ */
+ Kpgp::Result clearsign( const QCString& keyId,
+ const QCString& charset = QCString() );
+
+ /** encrypts this OpenPGP block for a list of persons. if sign is true then
+ the message is signed with the key corresponding to the given key id.
+ Returns
+ false if there was an unresolvable error or if encryption was canceled
+ true if everything is o.k.
+ */
+ Kpgp::Result encrypt( const QStringList& receivers, const QCString& keyId,
+ const bool sign, const QCString& charset = QCString() );
+
+ private:
+ void clear();
+
+ BlockType determineType() const;
+
+ QCString mText;
+ QCString mProcessedText;
+ QCString mError;
+ QString mSignatureUserId;
+ QCString mSignatureKeyId;
+ QCString mSignatureDate;
+ QCString mRequiredKey;
+ QString mRequiredUserId;
+ QStrList mEncryptedFor;
+ int mStatus;
+ bool mHasBeenProcessed;
+ BlockType mType;
+};
+
+// -- inlined member functions ---------------------------------------------
+
+inline QCString
+Block::text() const
+{
+ if( mHasBeenProcessed )
+ return mProcessedText;
+ else
+ return mText;
+}
+
+inline void
+Block::setText( const QCString& str )
+{
+ clear();
+ mText = str;
+}
+
+inline void
+Block::setProcessedText( const QCString& str )
+{
+ mProcessedText = str;
+ mHasBeenProcessed = true;
+}
+
+inline QCString
+Block::error() const
+{
+ return mError;
+}
+
+inline void
+Block::setError( const QCString& str )
+{
+ mError = str;
+}
+
+inline int
+Block::status() const
+{
+ return mStatus;
+}
+
+inline void
+Block::setStatus( const int status )
+{
+ mStatus = status;
+}
+
+inline BlockType
+Block::type()
+{
+ if( mType == NoPgpBlock )
+ mType = determineType();
+ return mType;
+}
+
+inline QString
+Block::signatureUserId() const
+{
+ return mSignatureUserId;
+}
+
+inline void
+Block::setSignatureUserId( const QString& userId )
+{
+ mSignatureUserId = userId;
+}
+
+inline QCString
+Block::signatureKeyId() const
+{
+ return mSignatureKeyId;
+}
+
+inline void
+Block::setSignatureKeyId( const QCString& keyId )
+{
+ mSignatureKeyId = keyId;
+}
+
+inline QCString
+Block::signatureDate() const
+{
+ return mSignatureDate;
+}
+
+inline void
+Block::setSignatureDate( const QCString& date )
+{
+ mSignatureDate = date;
+}
+
+inline QString
+Block::requiredKey() const
+{
+ return mRequiredKey;
+}
+
+inline void
+Block::setRequiredKey( const QCString& keyId )
+{
+ mRequiredKey = keyId;
+}
+
+inline QString
+Block::requiredUserId() const
+{
+ return mRequiredUserId;
+}
+
+inline void
+Block::setRequiredUserId( const QString& userId )
+{
+ mRequiredUserId = userId;
+}
+
+inline const QStrList
+Block::encryptedFor() const
+{
+ return mEncryptedFor;
+}
+
+inline bool
+Block::isEncrypted() const
+{
+ if( mStatus & ENCRYPTED )
+ return true;
+ return false;
+}
+
+inline bool
+Block::isSigned() const
+{
+ if( mStatus & SIGNED )
+ return true;
+ return false;
+}
+
+inline bool
+Block::goodSignature() const
+{
+ if( mStatus & GOODSIG )
+ return true;
+ return false;
+}
+
+/*
+inline bool
+Block::unknownSigner() const
+{
+ if( mStatus & UNKNOWN_SIG )
+ return true;
+ return false;
+}
+*/
+
+// -------------------------------------------------------------------------
+
+} // namespace Kpgp
+
+#endif
+
diff --git a/libkpgp/kpgpkey.cpp b/libkpgp/kpgpkey.cpp
new file mode 100644
index 00000000..d0d2e4ec
--- /dev/null
+++ b/libkpgp/kpgpkey.cpp
@@ -0,0 +1,260 @@
+/*
+ kpgpkey.cpp
+
+ Copyright (C) 2001,2002 the KPGP authors
+ See file AUTHORS.kpgp for details
+
+ This file is part of KPGP, the KDE PGP/GnuPG support library.
+
+ KPGP is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include "kpgpkey.h"
+#include "kdebug.h"
+
+namespace Kpgp {
+
+/* member functions of Kpgp::KeyIDList --------------------------------- */
+
+/** Converts from a KeyIDList to a QStringList.
+*/
+QStringList KeyIDList::toStringList() const
+{
+ QStringList res;
+ for( KeyIDList::ConstIterator it = begin(); it != end(); ++it ) {
+ res << (*it).data();
+ }
+ return res;
+}
+
+/** Converts from a QStringList to a KeyIDList.
+*/
+KeyIDList KeyIDList::fromStringList( const QStringList& l )
+{
+ KeyIDList res;
+ for( QStringList::ConstIterator it = l.begin(); it != l.end(); ++it ) {
+ res << (*it).local8Bit();
+ }
+ return res;
+}
+
+/* member functions of Kpgp::UserID ------------------------------------ */
+
+UserID::UserID(const QString& str, const Validity validity,
+ const bool revoked, const bool invalid)
+{
+ mText = str;
+ mValidity = validity;
+ mRevoked = revoked;
+ mInvalid = invalid;
+}
+
+
+/* member functions of Kpgp::Subkey ------------------------------------ */
+
+Subkey::Subkey(const KeyID& keyID, const bool secret)
+{
+ mSecret = secret;
+ mKeyID = keyID;
+
+ mRevoked = false;
+ mExpired = false;
+ mDisabled = false;
+ mInvalid = false;
+ mCanEncrypt = false;
+ mCanSign = false;
+ mCanCertify = false;
+ mKeyAlgo = 0;
+ mKeyLen = 0;
+ mFingerprint = 0;
+ mTimestamp = 0;
+ mExpiration = 0;
+}
+
+
+/* member functions of Kpgp::Key --------------------------------------- */
+
+Key::Key(const KeyID& keyid, const QString& uid, const bool secret) :
+ mSubkeys(), mUserIDs()
+{
+ mSecret = secret;
+ if (!keyid.isEmpty())
+ addSubkey(keyid, secret);
+ if (!uid.isEmpty())
+ addUserID(uid);
+
+ mRevoked = false;
+ mExpired = false;
+ mDisabled = false;
+ mInvalid = false;
+ mCanEncrypt = false;
+ mCanSign = false;
+ mCanCertify = false;
+
+ mEncryptPref = UnknownEncryptPref;
+}
+
+Key::~Key()
+{
+ //kdDebug(5100) << "Kpgp::Key: Deleting key " << primaryUserID() << endl;
+ mUserIDs.setAutoDelete(true);
+ mUserIDs.clear();
+ mSubkeys.setAutoDelete(true);
+ mSubkeys.clear();
+}
+
+void
+Key::clear()
+{
+ mSecret = false;
+ mRevoked = false;
+ mExpired = false;
+ mDisabled = false;
+ mInvalid = false;
+ mCanEncrypt = false;
+ mCanSign = false;
+ mCanCertify = false;
+
+ mEncryptPref = UnknownEncryptPref;
+
+ mSubkeys.setAutoDelete(true);
+ mSubkeys.clear();
+ mUserIDs.setAutoDelete(true);
+ mUserIDs.clear();
+}
+
+Validity
+Key::keyTrust() const
+{
+ Validity trust = KPGP_VALIDITY_UNKNOWN;
+
+ for( UserIDListIterator it(mUserIDs); it.current(); ++it )
+ {
+ if( (*it)->validity() > trust )
+ trust = (*it)->validity();
+ }
+
+ return trust;
+}
+
+Validity
+Key::keyTrust( const QString& uid ) const
+{
+ Validity trust = KPGP_VALIDITY_UNKNOWN;
+
+ if( uid.isEmpty() )
+ return trust;
+
+ for( UserIDListIterator it(mUserIDs); it.current(); ++it )
+ {
+ if( (*it)->text() == uid )
+ trust = (*it)->validity();
+ }
+
+ return trust;
+}
+
+void
+Key::cloneKeyTrust( const Key* key )
+{
+ if( !key )
+ return;
+
+ for( UserIDListIterator it(mUserIDs); it.current(); ++it )
+ {
+ (*it)->setValidity( key->keyTrust( (*it)->text() ) );
+ }
+}
+
+bool
+Key::isValid() const
+{
+ return ( !mRevoked && !mExpired && !mDisabled && !mInvalid );
+}
+
+
+bool
+Key::isValidEncryptionKey() const
+{
+ return ( !mRevoked && !mExpired && !mDisabled && !mInvalid && mCanEncrypt );
+}
+
+
+bool
+Key::isValidSigningKey() const
+{
+ return ( !mRevoked && !mExpired && !mDisabled && !mInvalid && mCanSign );
+}
+
+
+void Key::addUserID(const QString &uid, const Validity validity,
+ const bool revoked, const bool invalid)
+{
+ if (!uid.isEmpty()) {
+ UserID *userID = new UserID(uid, validity, revoked, invalid);
+ mUserIDs.append(userID);
+ }
+}
+
+bool Key::matchesUserID(const QString& str, bool cs)
+{
+ if (str.isEmpty() || mUserIDs.isEmpty())
+ return false;
+
+ for (UserIDListIterator it(mUserIDs); it.current(); ++it) {
+ if (((*it)->text().find(str, 0, cs)) != -1)
+ return true;
+ }
+
+ return false;
+}
+
+void Key::addSubkey(const KeyID& keyID, const bool secret)
+{
+ if (!keyID.isEmpty()) {
+ Subkey *key = new Subkey(keyID, secret);
+ mSubkeys.append(key);
+ }
+}
+
+Subkey *Key::getSubkey(const KeyID& keyID)
+{
+ if (keyID.isEmpty() || mSubkeys.isEmpty())
+ return 0;
+
+ // is the given key ID a long (16 chars) or a short (8 chars) key ID?
+ bool longKeyID = (keyID.length() == 16);
+
+ for (SubkeyListIterator it(mSubkeys); it.current(); ++it) {
+ if (longKeyID) {
+ if ((*it)->longKeyID() == keyID)
+ return (*it);
+ }
+ else {
+ if ((*it)->keyID() == keyID)
+ return (*it);
+ }
+ }
+
+ return 0;
+}
+
+void Key::setFingerprint(const KeyID& keyID, const QCString &fpr)
+{
+ Subkey *key;
+ if ((key = getSubkey(keyID)) != 0) {
+ key->setFingerprint(fpr);
+ }
+ else
+ kdDebug(5006) << "Error: Can't set fingerprint. A subkey with key ID 0x"
+ << keyID << " doesn't exist." << endl;
+}
+
+} // namespace Kpgp
diff --git a/libkpgp/kpgpkey.h b/libkpgp/kpgpkey.h
new file mode 100644
index 00000000..fd92f5a1
--- /dev/null
+++ b/libkpgp/kpgpkey.h
@@ -0,0 +1,780 @@
+/*
+ kpgpkey.h
+
+ Copyright (C) 2001,2002 the KPGP authors
+ See file AUTHORS.kpgp for details
+
+ This file is part of KPGP, the KDE PGP/GnuPG support library.
+
+ KPGP is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#ifndef KPGPKEY_H
+#define KPGPKEY_H
+
+#include <time.h>
+
+#include <qcstring.h>
+#include <qstring.h>
+#include <qstringlist.h>
+#include <qvaluelist.h>
+
+namespace Kpgp {
+
+/** These are the possible validity values for a PGP user id and for the owner
+ trust.
+ */
+typedef enum
+{ // this is copied from gpgme.h which is a part of GPGME
+ KPGP_VALIDITY_UNKNOWN = 0, // the trust hasn't been determined
+ KPGP_VALIDITY_UNDEFINED = 1, // trust is undefined
+ KPGP_VALIDITY_NEVER = 2,
+ KPGP_VALIDITY_MARGINAL = 3,
+ KPGP_VALIDITY_FULL = 4,
+ KPGP_VALIDITY_ULTIMATE = 5
+} Validity;
+
+/** These are the possible preferences for encryption.
+ */
+typedef enum
+{
+ NeverEncrypt = -1,
+ UnknownEncryptPref = 0,
+ AlwaysEncrypt = 1,
+ AlwaysEncryptIfPossible = 2,
+ AlwaysAskForEncryption = 3,
+ AskWheneverPossible = 4
+} EncryptPref;
+
+
+typedef QCString KeyID;
+
+class KeyIDList : public QValueList<KeyID>
+{
+ public:
+ KeyIDList() { }
+ KeyIDList( const KeyIDList& l ) : QValueList<KeyID>(l) { }
+ KeyIDList( const QValueList<KeyID>& l ) : QValueList<KeyID>(l) { }
+ KeyIDList( const KeyID& i ) { append(i); }
+
+ QStringList toStringList() const;
+
+ static KeyIDList fromStringList( const QStringList& );
+};
+
+/** This class is used to store information about a user id of a PGP key.
+ */
+class UserID
+{
+ public:
+ /** Constructs a new user id with the given values. */
+ UserID(const QString& str,
+ const Validity validity = KPGP_VALIDITY_UNKNOWN,
+ const bool revoked = false,
+ const bool invalid = false);
+ ~UserID() {};
+
+ /** Returns the text of the user id. */
+ QString text() const;
+
+ /** Returns true if the user id has been revoked. */
+ bool revoked() const;
+
+ /** Returns true if the user id is invalid. */
+ bool invalid() const;
+
+ /** Returns the validity of resp. the trust in the user id. */
+ Validity validity() const;
+
+ /** Sets the text of the user id to <em>str</em>. */
+ void setText(const QString& str);
+
+ /** Sets the flag if the user id has been revoked to <em>revoked</em>. */
+ void setRevoked(const bool revoked);
+
+ /** Sets the flag if the user id is invalid to <em>invalid</em>. */
+ void setInvalid(const bool invalid);
+
+ /** Sets the validity of resp. the trust in the user id to
+ <em>validity</em>. */
+ void setValidity(const Validity validity);
+
+ protected:
+ bool mRevoked : 1;
+ bool mInvalid : 1;
+ Validity mValidity;
+ QString mText;
+};
+
+typedef QPtrList<UserID> UserIDList;
+typedef QPtrListIterator<UserID> UserIDListIterator;
+
+inline QString UserID::text() const
+{
+ return mText;
+}
+
+inline bool UserID::revoked() const
+{
+ return mRevoked;
+}
+
+inline bool UserID::invalid() const
+{
+ return mInvalid;
+}
+
+inline Validity UserID::validity() const
+{
+ return mValidity;
+}
+
+inline void UserID::setText(const QString& str)
+{
+ mText = str;
+}
+
+inline void UserID::setRevoked(const bool revoked)
+{
+ mRevoked = revoked;
+}
+
+inline void UserID::setInvalid(const bool invalid)
+{
+ mInvalid = invalid;
+}
+
+inline void UserID::setValidity(const Validity validity)
+{
+ mValidity = validity;
+}
+
+
+/** This class is used to store information about a subkey of a PGP key.
+ */
+class Subkey
+{
+ public:
+ /** Constructs a new subkey with the given key ID. */
+ Subkey(const KeyID& keyID, const bool secret = false);
+ ~Subkey() {};
+
+ /** Returns true if the subkey is a secret subkey. */
+ bool secret() const;
+
+ /** Returns true if the subkey has been revoked. */
+ bool revoked() const;
+
+ /** Returns true if the subkey has expired. */
+ bool expired() const;
+
+ /** Returns true if the subkey has been disabled. */
+ bool disabled() const;
+
+ /** Returns true if the subkey is invalid. */
+ bool invalid() const;
+
+ /** Returns true if the subkey can be used to encrypt data. */
+ bool canEncrypt() const;
+
+ /** Returns true if the subkey can be used to sign data. */
+ bool canSign() const;
+
+ /** Returns true if the subkey can be used to certify keys. */
+ bool canCertify() const;
+
+ /** Returns the key algorithm of the subkey. */
+ unsigned int keyAlgorithm() const;
+
+ /** Returns the length of the subkey in bits. */
+ unsigned int keyLength() const;
+
+ /** Returns the long 64 bit key ID of the subkey if it's available.
+ Otherwise the short 32 bit key ID is returned. */
+ KeyID longKeyID() const;
+
+ /** Returns the (short) 32 bit key ID of the subkey. */
+ KeyID keyID() const;
+
+ /** Returns the fingerprint of the subkey. */
+ QCString fingerprint() const;
+
+ /** Returns the creation date of the subkey. */
+ time_t creationDate() const;
+
+ /** Returns the expiration date of the subkey. */
+ time_t expirationDate() const;
+
+ /** Sets the flag if the subkey is a secret subkey to <em>secret</em>. */
+ void setSecret(const bool secret);
+
+ /** Sets the flag if the subkey has been revoked to <em>revoked</em>. */
+ void setRevoked(const bool revoked);
+
+ /** Sets the flag if the subkey has expired to <em>expired</em>. */
+ void setExpired(const bool expired);
+
+ /** Sets the flag if the subkey has been disabled to <em>disabled</em>. */
+ void setDisabled(const bool disabled);
+
+ /** Sets the flag if the subkey is invalid to <em>invalid</em>. */
+ void setInvalid(const bool invalid);
+
+ /** Sets the flag if the subkey can be used to encrypt data to
+ <em>canEncrypt</em>. */
+ void setCanEncrypt(const bool canEncrypt);
+
+ /** Sets the flag if the subkey can be used to sign data to
+ <em>canSign</em>. */
+ void setCanSign(const bool canSign);
+
+ /** Sets the flag if the subkey can be used to certify keys to
+ <em>canCertify</em>. */
+ void setCanCertify(const bool canCertify);
+
+ /** Sets the key algorithm of the subkey to <em>keyAlgo</em>. */
+ void setKeyAlgorithm(const unsigned int keyAlgo);
+
+ /** Sets the key length of the subkey to <em>keyLen</em> bits. */
+ void setKeyLength(const unsigned int keyLen);
+
+ /** Sets the key ID of the subkey to <em>keyID</em>. */
+ void setKeyID(const KeyID& keyID);
+
+ /** Sets the fingerprint of the subkey to <em>fingerprint</em>. */
+ void setFingerprint(const QCString& fingerprint);
+
+ /** Sets the creation date of the subkey to <em>creationDate</em> seconds
+ since Epoch. */
+ void setCreationDate(const time_t creationDate);
+
+ /** Sets the expiration date of the subkey to <em>expirationDate</em> seconds
+ since Epoch. */
+ void setExpirationDate(const time_t expirationDate);
+
+ protected:
+ bool mSecret : 1;
+ /* various flags */
+ bool mRevoked : 1;
+ bool mExpired : 1;
+ bool mDisabled : 1;
+ bool mInvalid : 1;
+ bool mCanEncrypt : 1;
+ bool mCanSign : 1;
+ bool mCanCertify : 1;
+
+ unsigned int mKeyAlgo;
+ unsigned int mKeyLen;
+ KeyID mKeyID;
+ QCString mFingerprint;
+ time_t mTimestamp; /* -1 for invalid, 0 for not available */
+ time_t mExpiration; /* -1 for never, 0 for not available */
+};
+
+inline bool Subkey::secret() const
+{
+ return mSecret;
+}
+
+inline bool Subkey::revoked() const
+{
+ return mRevoked;
+}
+
+inline bool Subkey::expired() const
+{
+ return mExpired;
+}
+
+inline bool Subkey::disabled() const
+{
+ return mDisabled;
+}
+
+inline bool Subkey::invalid() const
+{
+ return mInvalid;
+}
+
+inline bool Subkey::canEncrypt() const
+{
+ return mCanEncrypt;
+}
+
+inline bool Subkey::canSign() const
+{
+ return mCanSign;
+}
+
+inline bool Subkey::canCertify() const
+{
+ return mCanCertify;
+}
+
+inline unsigned int Subkey::keyAlgorithm() const
+{
+ return mKeyAlgo;
+}
+
+inline unsigned int Subkey::keyLength() const
+{
+ return mKeyLen;
+}
+
+inline KeyID Subkey::longKeyID() const
+{
+ return mKeyID;
+}
+
+inline KeyID Subkey::keyID() const
+{
+ return mKeyID.right(8);
+}
+
+inline QCString Subkey::fingerprint() const
+{
+ return mFingerprint;
+}
+
+inline time_t Subkey::creationDate() const
+{
+ return mTimestamp;
+}
+
+inline time_t Subkey::expirationDate() const
+{
+ return mExpiration;
+}
+
+inline void Subkey::setSecret(const bool secret)
+{
+ mSecret = secret;
+}
+
+inline void Subkey::setRevoked(const bool revoked)
+{
+ mRevoked = revoked;
+}
+
+inline void Subkey::setExpired(const bool expired)
+{
+ mExpired = expired;
+}
+
+inline void Subkey::setDisabled(const bool disabled)
+{
+ mDisabled = disabled;
+}
+
+inline void Subkey::setInvalid(const bool invalid)
+{
+ mInvalid = invalid;
+}
+
+inline void Subkey::setCanEncrypt(const bool canEncrypt)
+{
+ mCanEncrypt = canEncrypt;
+}
+
+inline void Subkey::setCanSign(const bool canSign)
+{
+ mCanSign = canSign;
+}
+
+inline void Subkey::setCanCertify(const bool canCertify)
+{
+ mCanCertify = canCertify;
+}
+
+inline void Subkey::setKeyAlgorithm(const unsigned int keyAlgo)
+{
+ mKeyAlgo = keyAlgo;
+}
+
+inline void Subkey::setKeyLength(const unsigned int keyLen)
+{
+ mKeyLen = keyLen;
+}
+
+inline void Subkey::setKeyID(const KeyID& keyID)
+{
+ mKeyID = keyID;
+}
+
+inline void Subkey::setFingerprint(const QCString& fingerprint)
+{
+ mFingerprint = fingerprint;
+}
+
+inline void Subkey::setCreationDate(const time_t creationDate)
+{
+ mTimestamp = creationDate;
+}
+
+inline void Subkey::setExpirationDate(const time_t expirationDate)
+{
+ mExpiration = expirationDate;
+}
+
+typedef QPtrList<Subkey> SubkeyList;
+typedef QPtrListIterator<Subkey> SubkeyListIterator;
+
+
+/** This class is used to store information about a PGP key.
+ */
+class Key
+{
+ public:
+ /** Constructs a new PGP key with <em>keyid</em> as key ID of the
+ primary key and <em>uid</em> as primary user ID. */
+ Key( const KeyID& keyid = KeyID(),
+ const QString& uid = QString::null,
+ const bool secret = false);
+ ~Key();
+
+ /** Clears/resets all key data. */
+ void clear();
+
+ /** Returns true if the key is a secret key. */
+ bool secret() const;
+
+ /** Returns true if the key has been revoked. */
+ bool revoked() const;
+
+ /** Returns true if the key has expired. */
+ bool expired() const;
+
+ /** Returns true if the key has been disabled. */
+ bool disabled() const;
+
+ /** Returns true if the key is invalid. */
+ bool invalid() const;
+
+ /** Returns true if the key can be used to encrypt data. */
+ bool canEncrypt() const;
+
+ /** Returns true if the key can be used to sign data. */
+ bool canSign() const;
+
+ /** Returns true if the key can be used to certify keys. */
+ bool canCertify() const;
+
+ /** Sets the flag if the key is a secret key to <em>secret</em>. */
+ void setSecret(const bool secret);
+
+ /** Sets the flag if the key has been revoked to <em>revoked</em>. */
+ void setRevoked(const bool revoked);
+
+ /** Sets the flag if the key has expired to <em>expired</em>. */
+ void setExpired(const bool expired);
+
+ /** Sets the flag if the key has been disabled to <em>disabled</em>. */
+ void setDisabled(const bool disabled);
+
+ /** Sets the flag if the key is invalid to <em>invalid</em>. */
+ void setInvalid(const bool invalid);
+
+ /** Sets the flag if the key can be used to encrypt data to
+ <em>canEncrypt</em>. */
+ void setCanEncrypt(const bool canEncrypt);
+
+ /** Sets the flag if the key can be used to sign data to
+ <em>canSign</em>. */
+ void setCanSign(const bool canSign);
+
+ /** Sets the flag if the key can be used to certify keys to
+ <em>canCertify</em>. */
+ void setCanCertify(const bool canCertify);
+
+
+ /** Returns the encryption preference for this key. */
+ EncryptPref encryptionPreference();
+
+ /** Sets the encryption preference for this key to <em>encrPref</em>. */
+ void setEncryptionPreference( const EncryptPref encrPref );
+
+
+ /** Returns the primary user ID or a null string if there are no
+ user IDs. */
+ QString primaryUserID() const;
+
+ /** Returns the key ID of the primary key or a null string if there
+ are no subkeys. */
+ KeyID primaryKeyID() const;
+
+ /** Returns the fingerprint of the primary key or a null string if there
+ are no subkeys. */
+ QCString primaryFingerprint() const;
+
+ /** Returns true if there are no user IDs or no subkeys.*/
+ bool isNull() const;
+
+ /** Returns the creation date of the primary subkey.
+ */
+ time_t creationDate() const;
+
+ /** Returns the trust value of this key. This is the maximal trust value
+ of any of the user ids of this key.
+ */
+ Validity keyTrust() const;
+
+ /** Returns the trust value for the given user id of this key.
+ */
+ Validity keyTrust( const QString& uid ) const;
+
+ /** Set the validity values for the user ids to the validity values of
+ the given key. This is useful after rereading a key without expensive
+ trust checking.
+ */
+ void cloneKeyTrust( const Key* key );
+
+ /** Returns true if the key is valid, i.e. not revoked, expired, disabled
+ or invalid.
+ */
+ bool isValid() const;
+
+ /** Returns true if the key is a valid encryption key. The trust is not
+ checked.
+ */
+ bool isValidEncryptionKey() const;
+
+ /** Returns true if the key is a valid signing key. The trust is not checked.
+ */
+ bool isValidSigningKey() const;
+
+ /** Returns the list of userIDs. */
+ const UserIDList userIDs() const;
+
+ /** Returns the list of subkeys. */
+ const SubkeyList subkeys() const;
+
+ /** Adds a user ID with the given values to the key if <em>uid</em> isn't
+ an empty string. */
+ void addUserID(const QString& uid,
+ const Validity validity = KPGP_VALIDITY_UNKNOWN,
+ const bool revoked = false,
+ const bool invalid = false);
+
+ /** Adds the given user ID to the key. */
+ void addUserID(const UserID *userID);
+
+ /** Returns true if the given string matches one of the user IDs.
+ The match is case sensitive if <em>cs</em> is true or case insensitive
+ if <em>cs</em> is false. */
+ bool matchesUserID(const QString& str, bool cs = true);
+
+ /** Adds a subkey with the given values to the key if <em>keyID</em> isn't
+ an empty string. */
+ void addSubkey(const KeyID& keyID, const bool secret = false);
+
+ /** Adds the given subkey to the key. */
+ void addSubkey(const Subkey *subkey);
+
+ /** Returns a pointer to the subkey with the given key ID. */
+ Subkey *getSubkey(const KeyID& keyID);
+
+ /** Sets the fingerprint of the given subkey to <em>fpr</em>. */
+ void setFingerprint(const KeyID& keyID, const QCString& fpr);
+
+ protected:
+ bool mSecret : 1;
+ /* global flags */
+ bool mRevoked : 1;
+ bool mExpired : 1;
+ bool mDisabled : 1;
+ bool mInvalid : 1;
+ bool mCanEncrypt : 1;
+ bool mCanSign : 1;
+ bool mCanCertify : 1;
+
+ EncryptPref mEncryptPref;
+
+ SubkeyList mSubkeys;
+ UserIDList mUserIDs;
+};
+
+inline bool Key::secret() const
+{
+ return mSecret;
+}
+
+inline bool Key::revoked() const
+{
+ return mRevoked;
+}
+
+inline bool Key::expired() const
+{
+ return mExpired;
+}
+
+inline bool Key::disabled() const
+{
+ return mDisabled;
+}
+
+inline bool Key::invalid() const
+{
+ return mInvalid;
+}
+
+inline bool Key::canEncrypt() const
+{
+ return mCanEncrypt;
+}
+
+inline bool Key::canSign() const
+{
+ return mCanSign;
+}
+
+inline bool Key::canCertify() const
+{
+ return mCanCertify;
+}
+
+inline void Key::setSecret(const bool secret)
+{
+ mSecret = secret;
+}
+
+inline void Key::setRevoked(const bool revoked)
+{
+ mRevoked = revoked;
+}
+
+inline void Key::setExpired(const bool expired)
+{
+ mExpired = expired;
+}
+
+inline void Key::setDisabled(const bool disabled)
+{
+ mDisabled = disabled;
+}
+
+inline void Key::setInvalid(const bool invalid)
+{
+ mInvalid = invalid;
+}
+
+inline void Key::setCanEncrypt(const bool canEncrypt)
+{
+ mCanEncrypt = canEncrypt;
+}
+
+inline void Key::setCanSign(const bool canSign)
+{
+ mCanSign = canSign;
+}
+
+inline void Key::setCanCertify(const bool canCertify)
+{
+ mCanCertify = canCertify;
+}
+
+inline EncryptPref Key::encryptionPreference()
+{
+ return mEncryptPref;
+}
+
+inline void Key::setEncryptionPreference( const EncryptPref encrPref )
+{
+ mEncryptPref = encrPref;
+}
+
+inline QString Key::primaryUserID() const
+{
+ UserID *uid = mUserIDs.getFirst();
+
+ if (uid)
+ return uid->text();
+ else
+ return QString::null;
+}
+
+inline KeyID Key::primaryKeyID() const
+{
+ Subkey *key = mSubkeys.getFirst();
+
+ if (key)
+ return key->keyID();
+ else
+ return KeyID();
+}
+
+inline QCString Key::primaryFingerprint() const
+{
+ Subkey *key = mSubkeys.getFirst();
+
+ if (key)
+ return key->fingerprint();
+ else
+ return QCString();
+}
+
+inline const UserIDList Key::userIDs() const
+{
+ return mUserIDs;
+}
+
+inline const SubkeyList Key::subkeys() const
+{
+ return mSubkeys;
+}
+
+inline bool Key::isNull() const
+{
+ return (mUserIDs.isEmpty() || mSubkeys.isEmpty());
+}
+
+inline time_t Key::creationDate() const
+{
+ if( !mSubkeys.isEmpty() )
+ return mSubkeys.getFirst()->creationDate();
+ else
+ return -1;
+}
+
+inline void Key::addUserID(const UserID *userID)
+{
+ if (userID)
+ mUserIDs.append(userID);
+}
+
+inline void Key::addSubkey(const Subkey *subkey)
+{
+ if (subkey)
+ mSubkeys.append(subkey);
+}
+
+
+
+typedef QPtrList<Key> KeyListBase;
+typedef QPtrListIterator<Key> KeyListIterator;
+
+class KeyList : public KeyListBase
+{
+ public:
+ ~KeyList()
+ { clear(); }
+
+ private:
+ int compareItems( QPtrCollection::Item s1, QPtrCollection::Item s2 )
+ {
+ // sort case insensitively by the primary User IDs
+ return QString::compare((static_cast<Key*>(s1))->primaryUserID().lower(),
+ (static_cast<Key*>(s2))->primaryUserID().lower());
+ }
+};
+
+} // namespace Kpgp
+
+#endif
diff --git a/libkpgp/kpgpui.cpp b/libkpgp/kpgpui.cpp
new file mode 100644
index 00000000..7483bb0c
--- /dev/null
+++ b/libkpgp/kpgpui.cpp
@@ -0,0 +1,1694 @@
+/*
+ kpgpui.cpp
+
+ Copyright (C) 2001,2002 the KPGP authors
+ See file AUTHORS.kpgp for details
+
+ This file is part of KPGP, the KDE PGP/GnuPG support library.
+
+ KPGP is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+//#include <stdio.h>
+
+#include <qvgroupbox.h>
+#include <qvbox.h>
+#include <qlabel.h>
+#include <qwhatsthis.h>
+#include <qtooltip.h>
+#include <qapplication.h>
+#include <qtextcodec.h>
+#include <qdatetime.h>
+#include <qpixmap.h>
+#include <qlayout.h>
+#include <qtimer.h>
+#include <qpopupmenu.h>
+#include <qregexp.h>
+
+#include <klocale.h>
+#include <kpassdlg.h>
+#include <kcharsets.h>
+#include <kseparator.h>
+#include <kiconloader.h>
+#include <klistview.h>
+#include <kconfigbase.h>
+#include <kconfig.h>
+#include <kprogress.h>
+#include <kapplication.h>
+#include <kwin.h>
+#if KDE_IS_VERSION( 3, 1, 90 )
+#include <kglobalsettings.h>
+#endif
+
+#include "kpgp.h"
+#include "kpgpui.h"
+#include "kpgpkey.h"
+
+#include <assert.h>
+#include <string.h> // for memcpy(3)
+
+const int Kpgp::KeySelectionDialog::sCheckSelectionDelay = 250;
+
+namespace Kpgp {
+
+PassphraseDialog::PassphraseDialog( QWidget *parent,
+ const QString &caption, bool modal,
+ const QString &keyID )
+ :KDialogBase( parent, 0, modal, caption, Ok|Cancel )
+{
+ QHBox *hbox = makeHBoxMainWidget();
+ hbox->setSpacing( spacingHint() );
+ hbox->setMargin( marginHint() );
+
+ QLabel *label = new QLabel(hbox);
+ label->setPixmap( BarIcon("pgp-keys") );
+
+ QWidget *rightArea = new QWidget( hbox );
+ QVBoxLayout *vlay = new QVBoxLayout( rightArea, 0, spacingHint() );
+
+ if (keyID.isNull())
+ label = new QLabel(i18n("Please enter your OpenPGP passphrase:"),rightArea);
+ else
+ label = new QLabel(i18n("Please enter the OpenPGP passphrase for\n\"%1\":").arg(keyID),
+ rightArea);
+ lineedit = new KPasswordEdit( rightArea );
+ lineedit->setEchoMode(QLineEdit::Password);
+ lineedit->setMinimumWidth( fontMetrics().maxWidth()*20 );
+ lineedit->setFocus();
+ connect( lineedit, SIGNAL(returnPressed()), this, SLOT(slotOk()) );
+
+ vlay->addWidget( label );
+ vlay->addWidget( lineedit );
+
+ disableResize();
+}
+
+
+PassphraseDialog::~PassphraseDialog()
+{
+}
+
+const char * PassphraseDialog::passphrase()
+{
+ return lineedit->password();
+}
+
+
+// ------------------------------------------------------------------------
+// Forbidden accels for KMail: AC GH OP
+// for KNode: ACE H O
+Config::Config( QWidget *parent, const char *name, bool encrypt )
+ : QWidget( parent, name ), pgp( Module::getKpgp() )
+{
+ QGroupBox * group;
+ QLabel * label;
+ QString msg;
+
+
+ QVBoxLayout *topLayout = new QVBoxLayout( this, 0, KDialog::spacingHint() );
+
+ group = new QVGroupBox( i18n("Warning"), this );
+ group->layout()->setSpacing( KDialog::spacingHint() );
+ // (mmutz) work around Qt label bug in 3.0.0 (and possibly later):
+ // 1. Don't use rich text: No <qt><b>...</b></qt>
+ label = new QLabel( i18n("Please check if encryption really "
+ "works before you start using it seriously. Also note that attachments "
+ "are not encrypted by the PGP/GPG module."), group );
+ // 2. instead, set the font to bold:
+ QFont labelFont = label->font();
+ labelFont.setBold( true );
+ label->setFont( labelFont );
+ // 3. and activate wordwarp:
+ label->setAlignment( AlignLeft|WordBreak );
+ // end; to remove the workaround, add <qt><b>..</b></qt> around the
+ // text and remove lines QFont... -> label->setAlignment(...).
+ topLayout->addWidget( group );
+
+ group = new QVGroupBox( i18n("Encryption Tool"), this );
+ group->layout()->setSpacing( KDialog::spacingHint() );
+
+ QHBox * hbox = new QHBox( group );
+ label = new QLabel( i18n("Select encryption tool to &use:"), hbox );
+ toolCombo = new QComboBox( false, hbox );
+ toolCombo->insertStringList( QStringList()
+ << i18n("Autodetect")
+ << i18n("GnuPG - Gnu Privacy Guard")
+ << i18n("PGP Version 2.x")
+ << i18n("PGP Version 5.x")
+ << i18n("PGP Version 6.x")
+ << i18n("Do not use any encryption tool") );
+ label->setBuddy( toolCombo );
+ hbox->setStretchFactor( toolCombo, 1 );
+ connect( toolCombo, SIGNAL( activated( int ) ),
+ this, SIGNAL( changed( void ) ) );
+ // This is the place to add a KURLRequester to be used for asking
+ // the user for the path to the executable...
+ topLayout->addWidget( group );
+
+ mpOptionsGroupBox = new QVGroupBox( i18n("Options"), this );
+ mpOptionsGroupBox->layout()->setSpacing( KDialog::spacingHint() );
+ storePass = new QCheckBox( i18n("&Keep passphrase in memory"),
+ mpOptionsGroupBox );
+ connect( storePass, SIGNAL( toggled( bool ) ),
+ this, SIGNAL( changed( void ) ) );
+ msg = i18n( "<qt><p>When this option is enabled, the passphrase of your "
+ "private key will be remembered by the application as long "
+ "as the application is running. Thus you will only have to "
+ "enter the passphrase once.</p><p>Be aware that this could be a "
+ "security risk. If you leave your computer, others "
+ "can use it to send signed messages and/or read your encrypted "
+ "messages. If a core dump occurs, the contents of your RAM will "
+ "be saved onto disk, including your passphrase.</p>"
+ "<p>Note that when using KMail, this setting only applies "
+ "if you are not using gpg-agent. It is also ignored "
+ "if you are using crypto plugins.</p></qt>" );
+ QWhatsThis::add( storePass, msg );
+ if( encrypt ) {
+ encToSelf = new QCheckBox( i18n("Always encr&ypt to self"),
+ mpOptionsGroupBox );
+ connect( encToSelf, SIGNAL( toggled( bool ) ),
+ this, SIGNAL( changed( void ) ) );
+
+ msg = i18n( "<qt><p>When this option is enabled, the message/file "
+ "will not only be encrypted with the receiver's public key, "
+ "but also with your key. This will enable you to decrypt the "
+ "message/file at a later time. This is generally a good idea."
+ "</p></qt>" );
+ QWhatsThis::add( encToSelf, msg );
+ }
+ else
+ encToSelf = 0;
+ showCipherText = new QCheckBox( i18n("&Show signed/encrypted text after "
+ "composing"),
+ mpOptionsGroupBox );
+ connect( showCipherText, SIGNAL( toggled( bool ) ),
+ this, SIGNAL( changed( void ) ) );
+
+ msg = i18n( "<qt><p>When this option is enabled, the signed/encrypted text "
+ "will be shown in a separate window, enabling you to know how "
+ "it will look before it is sent. This is a good idea when "
+ "you are verifying that your encryption system works.</p></qt>" );
+ QWhatsThis::add( showCipherText, msg );
+ if( encrypt ) {
+ showKeyApprovalDlg = new QCheckBox( i18n("Always show the encryption "
+ "keys &for approval"),
+ mpOptionsGroupBox );
+ connect( showKeyApprovalDlg, SIGNAL( toggled( bool ) ),
+ this, SIGNAL( changed( void ) ) );
+ msg = i18n( "<qt><p>When this option is enabled, the application will "
+ "always show you a list of public keys from which you can "
+ "choose the one it will use for encryption. If it is off, "
+ "the application will only show the dialog if it cannot find "
+ "the right key or if there are several which could be used. "
+ "</p></qt>" );
+ QWhatsThis::add( showKeyApprovalDlg, msg );
+}
+ else
+ showKeyApprovalDlg = 0;
+
+ topLayout->addWidget( mpOptionsGroupBox );
+
+ topLayout->addStretch(1);
+
+ setValues(); // is this needed by KNode, b/c for KMail, it's not.
+}
+
+
+Config::~Config()
+{
+}
+
+void
+Config::setValues()
+{
+ // set default values
+ storePass->setChecked( pgp->storePassPhrase() );
+ if( 0 != encToSelf )
+ encToSelf->setChecked( pgp->encryptToSelf() );
+ showCipherText->setChecked( pgp->showCipherText() );
+ if( 0 != showKeyApprovalDlg )
+ showKeyApprovalDlg->setChecked( pgp->showKeyApprovalDlg() );
+
+ int type = 0;
+ switch (pgp->pgpType) {
+ // translate Kpgp::Module enum to combobox' entries:
+ default:
+ case Module::tAuto: type = 0; break;
+ case Module::tGPG: type = 1; break;
+ case Module::tPGP2: type = 2; break;
+ case Module::tPGP5: type = 3; break;
+ case Module::tPGP6: type = 4; break;
+ case Module::tOff: type = 5; break;
+ }
+ toolCombo->setCurrentItem( type );
+}
+
+void
+Config::applySettings()
+{
+ pgp->setStorePassPhrase(storePass->isChecked());
+ if( 0 != encToSelf )
+ pgp->setEncryptToSelf(encToSelf->isChecked());
+ pgp->setShowCipherText(showCipherText->isChecked());
+ if( 0 != showKeyApprovalDlg )
+ pgp->setShowKeyApprovalDlg( showKeyApprovalDlg->isChecked() );
+
+ Module::PGPType type;
+ switch ( toolCombo->currentItem() ) {
+ // convert combobox entry indices to Kpgp::Module constants:
+ default:
+ case 0: type = Module::tAuto; break;
+ case 1: type = Module::tGPG; break;
+ case 2: type = Module::tPGP2; break;
+ case 3: type = Module::tPGP5; break;
+ case 4: type = Module::tPGP6; break;
+ case 5: type = Module::tOff; break;
+ }
+ pgp->pgpType = type;
+
+ pgp->writeConfig(true);
+}
+
+
+
+// ------------------------------------------------------------------------
+KeySelectionDialog::KeySelectionDialog( const KeyList& keyList,
+ const QString& title,
+ const QString& text,
+ const KeyIDList& keyIds,
+ const bool rememberChoice,
+ const unsigned int allowedKeys,
+ const bool extendedSelection,
+ QWidget *parent, const char *name,
+ bool modal )
+ : KDialogBase( parent, name, modal, title, Default|Ok|Cancel, Ok ),
+ mRememberCB( 0 ),
+ mAllowedKeys( allowedKeys ),
+ mCurrentContextMenuItem( 0 )
+{
+ if ( kapp )
+ KWin::setIcons( winId(), kapp->icon(), kapp->miniIcon() );
+ Kpgp::Module *pgp = Kpgp::Module::getKpgp();
+ KConfig *config = pgp->getConfig();
+ KConfigGroup dialogConfig( config, "Key Selection Dialog" );
+
+ QSize defaultSize( 580, 400 );
+ QSize dialogSize = dialogConfig.readSizeEntry( "Dialog size", &defaultSize );
+
+ resize( dialogSize );
+
+ mCheckSelectionTimer = new QTimer( this, "mCheckSelectionTimer" );
+ mStartSearchTimer = new QTimer( this, "mStartSearchTimer" );
+
+ // load the key status icons
+ mKeyGoodPix = new QPixmap( UserIcon("key_ok") );
+ mKeyBadPix = new QPixmap( UserIcon("key_bad") );
+ mKeyUnknownPix = new QPixmap( UserIcon("key_unknown") );
+ mKeyValidPix = new QPixmap( UserIcon("key") );
+
+ QFrame *page = makeMainWidget();
+ QVBoxLayout *topLayout = new QVBoxLayout( page, 0, spacingHint() );
+
+ if( !text.isEmpty() ) {
+ QLabel *label = new QLabel( page );
+ label->setText( text );
+ topLayout->addWidget( label );
+ }
+
+ QHBoxLayout * hlay = new QHBoxLayout( topLayout ); // inherits spacing
+ QLineEdit * le = new QLineEdit( page );
+ hlay->addWidget( new QLabel( le, i18n("&Search for:"), page ) );
+ hlay->addWidget( le, 1 );
+ le->setFocus();
+
+ connect( le, SIGNAL(textChanged(const QString&)),
+ this, SLOT(slotSearch(const QString&)) );
+ connect( mStartSearchTimer, SIGNAL(timeout()), SLOT(slotFilter()) );
+
+ mListView = new KListView( page );
+ mListView->addColumn( i18n("Key ID") );
+ mListView->addColumn( i18n("User ID") );
+ mListView->setAllColumnsShowFocus( true );
+ mListView->setResizeMode( QListView::LastColumn );
+ mListView->setRootIsDecorated( true );
+ mListView->setShowSortIndicator( true );
+ mListView->setSorting( 1, true ); // sort by User ID
+ mListView->setShowToolTips( true );
+ if( extendedSelection ) {
+ mListView->setSelectionMode( QListView::Extended );
+ //mListView->setSelectionMode( QListView::Multi );
+ }
+ topLayout->addWidget( mListView, 10 );
+
+ if (rememberChoice) {
+ mRememberCB = new QCheckBox( i18n("Remember choice"), page );
+ topLayout->addWidget( mRememberCB );
+ QWhatsThis::add(mRememberCB,
+ i18n("<qt><p>If you check this box your choice will "
+ "be stored and you will not be asked again."
+ "</p></qt>"));
+ }
+
+ initKeylist( keyList, keyIds );
+
+ QListViewItem *lvi;
+ if( extendedSelection ) {
+ lvi = mListView->currentItem();
+ slotCheckSelection();
+ }
+ else {
+ lvi = mListView->selectedItem();
+ slotCheckSelection( lvi );
+ }
+ // make sure that the selected item is visible
+ // (ensureItemVisible(...) doesn't work correctly in Qt 3.0.0)
+ if( lvi != 0 )
+ mListView->center( mListView->contentsX(), mListView->itemPos( lvi ) );
+
+ if( extendedSelection ) {
+ connect( mCheckSelectionTimer, SIGNAL( timeout() ),
+ this, SLOT( slotCheckSelection() ) );
+ connect( mListView, SIGNAL( selectionChanged() ),
+ this, SLOT( slotSelectionChanged() ) );
+ }
+ else {
+ connect( mListView, SIGNAL( selectionChanged( QListViewItem* ) ),
+ this, SLOT( slotSelectionChanged( QListViewItem* ) ) );
+ }
+ connect( mListView, SIGNAL( doubleClicked ( QListViewItem *, const QPoint &, int ) ), this, SLOT( accept() ) );
+
+ connect( mListView, SIGNAL( contextMenuRequested( QListViewItem*,
+ const QPoint&, int ) ),
+ this, SLOT( slotRMB( QListViewItem*, const QPoint&, int ) ) );
+
+ setButtonText( KDialogBase::Default, i18n("&Reread Keys") );
+ connect( this, SIGNAL( defaultClicked() ),
+ this, SLOT( slotRereadKeys() ) );
+}
+
+
+KeySelectionDialog::~KeySelectionDialog()
+{
+ Kpgp::Module *pgp = Kpgp::Module::getKpgp();
+ KConfig *config = pgp->getConfig();
+ KConfigGroup dialogConfig( config, "Key Selection Dialog" );
+ dialogConfig.writeEntry( "Dialog size", size() );
+ config->sync();
+ delete mKeyGoodPix;
+ delete mKeyBadPix;
+ delete mKeyUnknownPix;
+ delete mKeyValidPix;
+}
+
+
+KeyID KeySelectionDialog::key() const
+{
+ if( mListView->isMultiSelection() || mKeyIds.isEmpty() )
+ return KeyID();
+ else
+ return mKeyIds.first();
+}
+
+
+void KeySelectionDialog::initKeylist( const KeyList& keyList,
+ const KeyIDList& keyIds )
+{
+ QListViewItem* firstSelectedItem = 0;
+ mKeyIds.clear();
+ mListView->clear();
+
+ // build a list of all public keys
+ for( KeyListIterator it( keyList ); it.current(); ++it ) {
+ KeyID curKeyId = (*it)->primaryKeyID();
+
+ QListViewItem* primaryUserID = new QListViewItem( mListView, curKeyId,
+ (*it)->primaryUserID() );
+
+ // select and open the given key
+ if( keyIds.findIndex( curKeyId ) != -1 ) {
+ if( 0 == firstSelectedItem ) {
+ firstSelectedItem = primaryUserID;
+ }
+ mListView->setSelected( primaryUserID, true );
+ mKeyIds.append( curKeyId );
+ }
+ primaryUserID->setOpen( false );
+
+ // set icon for this key
+ switch( keyValidity( *it ) ) {
+ case 0: // the key's validity can't be determined
+ primaryUserID->setPixmap( 0, *mKeyUnknownPix );
+ break;
+ case 1: // key is valid but not trusted
+ primaryUserID->setPixmap( 0, *mKeyValidPix );
+ break;
+ case 2: // key is valid and trusted
+ primaryUserID->setPixmap( 0, *mKeyGoodPix );
+ break;
+ case -1: // key is invalid
+ primaryUserID->setPixmap( 0, *mKeyBadPix );
+ break;
+ }
+
+ QListViewItem* childItem;
+
+ childItem = new QListViewItem( primaryUserID, "",
+ i18n( "Fingerprint: %1" )
+ .arg( beautifyFingerprint( (*it)->primaryFingerprint() ) ) );
+ if( primaryUserID->isSelected() && mListView->isMultiSelection() ) {
+ mListView->setSelected( childItem, true );
+ }
+
+ childItem = new QListViewItem( primaryUserID, "", keyInfo( *it ) );
+ if( primaryUserID->isSelected() && mListView->isMultiSelection() ) {
+ mListView->setSelected( childItem, true );
+ }
+
+ UserIDList userIDs = (*it)->userIDs();
+ UserIDListIterator uidit( userIDs );
+ if( *uidit ) {
+ ++uidit; // skip the primary user ID
+ for( ; *uidit; ++uidit ) {
+ childItem = new QListViewItem( primaryUserID, "", (*uidit)->text() );
+ if( primaryUserID->isSelected() && mListView->isMultiSelection() ) {
+ mListView->setSelected( childItem, true );
+ }
+ }
+ }
+ }
+
+ if( 0 != firstSelectedItem ) {
+ mListView->setCurrentItem( firstSelectedItem );
+ }
+}
+
+
+QString KeySelectionDialog::keyInfo( const Kpgp::Key *key ) const
+{
+ QString status, remark;
+ if( key->revoked() ) {
+ status = i18n("Revoked");
+ }
+ else if( key->expired() ) {
+ status = i18n("Expired");
+ }
+ else if( key->disabled() ) {
+ status = i18n("Disabled");
+ }
+ else if( key->invalid() ) {
+ status = i18n("Invalid");
+ }
+ else {
+ Validity keyTrust = key->keyTrust();
+ switch( keyTrust ) {
+ case KPGP_VALIDITY_UNDEFINED:
+ status = i18n("Undefined trust");
+ break;
+ case KPGP_VALIDITY_NEVER:
+ status = i18n("Untrusted");
+ break;
+ case KPGP_VALIDITY_MARGINAL:
+ status = i18n("Marginally trusted");
+ break;
+ case KPGP_VALIDITY_FULL:
+ status = i18n("Fully trusted");
+ break;
+ case KPGP_VALIDITY_ULTIMATE:
+ status = i18n("Ultimately trusted");
+ break;
+ case KPGP_VALIDITY_UNKNOWN:
+ default:
+ status = i18n("Unknown");
+ }
+ if( key->secret() ) {
+ remark = i18n("Secret key available");
+ }
+ else if( !key->canEncrypt() ) {
+ remark = i18n("Sign only key");
+ }
+ else if( !key->canSign() ) {
+ remark = i18n("Encryption only key");
+ }
+ }
+
+ QDateTime dt;
+ dt.setTime_t( key->creationDate() );
+ if( remark.isEmpty() ) {
+ return " " + i18n("creation date and status of an OpenPGP key",
+ "Creation date: %1, Status: %2")
+ .arg( KGlobal::locale()->formatDate( dt.date(), true ) )
+ .arg( status );
+ }
+ else {
+ return " " + i18n("creation date, status and remark of an OpenPGP key",
+ "Creation date: %1, Status: %2 (%3)")
+ .arg( KGlobal::locale()->formatDate( dt.date(), true ) )
+ .arg( status )
+ .arg( remark );
+ }
+}
+
+QString KeySelectionDialog::beautifyFingerprint( const QCString& fpr ) const
+{
+ QCString result;
+
+ if( 40 == fpr.length() ) {
+ // convert to this format:
+ // 0000 1111 2222 3333 4444 5555 6666 7777 8888 9999
+ result.fill( ' ', 50 );
+ memcpy( result.data() , fpr.data() , 4 );
+ memcpy( result.data() + 5, fpr.data() + 4, 4 );
+ memcpy( result.data() + 10, fpr.data() + 8, 4 );
+ memcpy( result.data() + 15, fpr.data() + 12, 4 );
+ memcpy( result.data() + 20, fpr.data() + 16, 4 );
+ memcpy( result.data() + 26, fpr.data() + 20, 4 );
+ memcpy( result.data() + 31, fpr.data() + 24, 4 );
+ memcpy( result.data() + 36, fpr.data() + 28, 4 );
+ memcpy( result.data() + 41, fpr.data() + 32, 4 );
+ memcpy( result.data() + 46, fpr.data() + 36, 4 );
+ }
+ else if( 32 == fpr.length() ) {
+ // convert to this format:
+ // 00 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF
+ result.fill( ' ', 48 );
+ memcpy( result.data() , fpr.data() , 2 );
+ memcpy( result.data() + 3, fpr.data() + 2, 2 );
+ memcpy( result.data() + 6, fpr.data() + 4, 2 );
+ memcpy( result.data() + 9, fpr.data() + 6, 2 );
+ memcpy( result.data() + 12, fpr.data() + 8, 2 );
+ memcpy( result.data() + 15, fpr.data() + 10, 2 );
+ memcpy( result.data() + 18, fpr.data() + 12, 2 );
+ memcpy( result.data() + 21, fpr.data() + 14, 2 );
+ memcpy( result.data() + 25, fpr.data() + 16, 2 );
+ memcpy( result.data() + 28, fpr.data() + 18, 2 );
+ memcpy( result.data() + 31, fpr.data() + 20, 2 );
+ memcpy( result.data() + 34, fpr.data() + 22, 2 );
+ memcpy( result.data() + 37, fpr.data() + 24, 2 );
+ memcpy( result.data() + 40, fpr.data() + 26, 2 );
+ memcpy( result.data() + 43, fpr.data() + 28, 2 );
+ memcpy( result.data() + 46, fpr.data() + 30, 2 );
+ }
+ else { // unknown length of fingerprint
+ result = fpr;
+ }
+
+ return result;
+}
+
+int KeySelectionDialog::keyValidity( const Kpgp::Key *key ) const
+{
+ if( 0 == key ) {
+ return -1;
+ }
+
+ if( ( mAllowedKeys & EncrSignKeys ) == EncryptionKeys ) {
+ // only encryption keys are allowed
+ if( ( mAllowedKeys & ValidKeys ) && !key->isValidEncryptionKey() ) {
+ // only valid encryption keys are allowed
+ return -1;
+ }
+ else if( !key->canEncrypt() ) {
+ return -1;
+ }
+ }
+ else if( ( mAllowedKeys & EncrSignKeys ) == SigningKeys ) {
+ // only signing keys are allowed
+ if( ( mAllowedKeys & ValidKeys ) && !key->isValidSigningKey() ) {
+ // only valid signing keys are allowed
+ return -1;
+ }
+ else if( !key->canSign() ) {
+ return -1;
+ }
+ }
+ else if( ( mAllowedKeys & ValidKeys ) && !key->isValid() ) {
+ // only valid keys are allowed
+ return -1;
+ }
+
+ // check the key's trust
+ int val = 0;
+ Validity keyTrust = key->keyTrust();
+ switch( keyTrust ) {
+ case KPGP_VALIDITY_NEVER:
+ val = -1;
+ break;
+ case KPGP_VALIDITY_MARGINAL:
+ case KPGP_VALIDITY_FULL:
+ case KPGP_VALIDITY_ULTIMATE:
+ val = 2;
+ break;
+ case KPGP_VALIDITY_UNDEFINED:
+ if( mAllowedKeys & TrustedKeys ) {
+ // only trusted keys are allowed
+ val = -1;
+ }
+ else {
+ val = 1;
+ }
+ break;
+ case KPGP_VALIDITY_UNKNOWN:
+ default:
+ val = 0;
+ }
+
+ return val;
+}
+
+
+void KeySelectionDialog::updateKeyInfo( const Kpgp::Key* key,
+ QListViewItem* lvi ) const
+{
+ if( 0 == lvi ) {
+ return;
+ }
+
+ if( lvi->parent() != 0 ) {
+ lvi = lvi->parent();
+ }
+
+ if( 0 == key ) {
+ // the key doesn't exist anymore -> delete it from the list view
+ while( lvi->firstChild() ) {
+ kdDebug(5100) << "Deleting '" << lvi->firstChild()->text( 1 ) << "'\n";
+ delete lvi->firstChild();
+ }
+ kdDebug(5100) << "Deleting key 0x" << lvi->text( 0 ) << " ("
+ << lvi->text( 1 ) << ")\n";
+ delete lvi;
+ lvi = 0;
+ return;
+ }
+
+ // update the icon for this key
+ switch( keyValidity( key ) ) {
+ case 0: // the key's validity can't be determined
+ lvi->setPixmap( 0, *mKeyUnknownPix );
+ break;
+ case 1: // key is valid but not trusted
+ lvi->setPixmap( 0, *mKeyValidPix );
+ break;
+ case 2: // key is valid and trusted
+ lvi->setPixmap( 0, *mKeyGoodPix );
+ break;
+ case -1: // key is invalid
+ lvi->setPixmap( 0, *mKeyBadPix );
+ break;
+ }
+
+ // update the key info for this key
+ // the key info is identified by a leading space; this shouldn't be
+ // a problem because User Ids shouldn't start with a space
+ for( lvi = lvi->firstChild(); lvi; lvi = lvi->nextSibling() ) {
+ if( lvi->text( 1 ).at(0) == ' ' ) {
+ lvi->setText( 1, keyInfo( key ) );
+ break;
+ }
+ }
+}
+
+
+int
+KeySelectionDialog::keyAdmissibility( QListViewItem* lvi,
+ TrustCheckMode trustCheckMode ) const
+{
+ // Return:
+ // -1 = key must not be chosen,
+ // 0 = not enough information to decide whether the give key is allowed
+ // or not,
+ // 1 = key can be chosen
+
+ if( mAllowedKeys == AllKeys ) {
+ return 1;
+ }
+
+ Kpgp::Module *pgp = Kpgp::Module::getKpgp();
+
+ if( 0 == pgp ) {
+ return 0;
+ }
+
+ KeyID keyId = getKeyId( lvi );
+ Kpgp::Key* key = pgp->publicKey( keyId );
+
+ if( 0 == key ) {
+ return 0;
+ }
+
+ int val = 0;
+ if( trustCheckMode == ForceTrustCheck ) {
+ key = pgp->rereadKey( keyId, true );
+ updateKeyInfo( key, lvi );
+ val = keyValidity( key );
+ }
+ else {
+ val = keyValidity( key );
+ if( ( trustCheckMode == AllowExpensiveTrustCheck ) && ( 0 == val ) ) {
+ key = pgp->rereadKey( keyId, true );
+ updateKeyInfo( key, lvi );
+ val = keyValidity( key );
+ }
+ }
+
+ switch( val ) {
+ case -1: // key is not usable
+ return -1;
+ break;
+ case 0: // key status unknown
+ return 0;
+ break;
+ case 1: // key is valid, but untrusted
+ if( mAllowedKeys & TrustedKeys ) {
+ // only trusted keys are allowed
+ return -1;
+ }
+ return 1;
+ break;
+ case 2: // key is trusted
+ return 1;
+ break;
+ default:
+ kdDebug( 5100 ) << "Error: Invalid key status value.\n";
+ }
+
+ return 0;
+}
+
+
+KeyID
+KeySelectionDialog::getKeyId( const QListViewItem* lvi ) const
+{
+ KeyID keyId;
+
+ if( 0 != lvi ) {
+ if( 0 != lvi->parent() ) {
+ keyId = lvi->parent()->text(0).local8Bit();
+ }
+ else {
+ keyId = lvi->text(0).local8Bit();
+ }
+ }
+
+ return keyId;
+}
+
+
+void KeySelectionDialog::slotRereadKeys()
+{
+ Kpgp::Module *pgp = Kpgp::Module::getKpgp();
+
+ if( 0 == pgp ) {
+ return;
+ }
+
+ KeyList keys;
+
+ if( PublicKeys & mAllowedKeys ) {
+ pgp->readPublicKeys( true );
+ keys = pgp->publicKeys();
+ }
+ else {
+ pgp->readSecretKeys( true );
+ keys = pgp->secretKeys();
+ }
+
+ // save the current position of the contents
+ int offsetY = mListView->contentsY();
+
+ if( mListView->isMultiSelection() ) {
+ disconnect( mListView, SIGNAL( selectionChanged() ),
+ this, SLOT( slotSelectionChanged() ) );
+ }
+ else {
+ disconnect( mListView, SIGNAL( selectionChanged( QListViewItem * ) ),
+ this, SLOT( slotSelectionChanged( QListViewItem * ) ) );
+ }
+
+ initKeylist( keys, KeyIDList( mKeyIds ) );
+ slotFilter();
+
+ if( mListView->isMultiSelection() ) {
+ connect( mListView, SIGNAL( selectionChanged() ),
+ this, SLOT( slotSelectionChanged() ) );
+ slotSelectionChanged();
+ }
+ else {
+ connect( mListView, SIGNAL( selectionChanged( QListViewItem * ) ),
+ this, SLOT( slotSelectionChanged( QListViewItem * ) ) );
+ }
+
+ // restore the saved position of the contents
+ mListView->setContentsPos( 0, offsetY );
+}
+
+
+void KeySelectionDialog::slotSelectionChanged( QListViewItem * lvi )
+{
+ slotCheckSelection( lvi );
+}
+
+
+void KeySelectionDialog::slotSelectionChanged()
+{
+ kdDebug(5100) << "KeySelectionDialog::slotSelectionChanged()\n";
+
+ // (re)start the check selection timer. Checking the selection is delayed
+ // because else drag-selection doesn't work very good (checking key trust
+ // is slow).
+ mCheckSelectionTimer->start( sCheckSelectionDelay );
+}
+
+
+void KeySelectionDialog::slotCheckSelection( QListViewItem* plvi /* = 0 */ )
+{
+ kdDebug(5100) << "KeySelectionDialog::slotCheckSelection()\n";
+
+ if( !mListView->isMultiSelection() ) {
+ mKeyIds.clear();
+ KeyID keyId = getKeyId( plvi );
+ if( !keyId.isEmpty() ) {
+ mKeyIds.append( keyId );
+ enableButtonOK( 1 == keyAdmissibility( plvi, AllowExpensiveTrustCheck ) );
+ }
+ else {
+ enableButtonOK( false );
+ }
+ }
+ else {
+ mCheckSelectionTimer->stop();
+
+ // As we might change the selection, we have to disconnect the slot
+ // to prevent recursion
+ disconnect( mListView, SIGNAL( selectionChanged() ),
+ this, SLOT( slotSelectionChanged() ) );
+
+ KeyIDList newKeyIdList;
+ QValueList<QListViewItem*> keysToBeChecked;
+
+ bool keysAllowed = true;
+ enum { UNKNOWN, SELECTED, DESELECTED } userAction = UNKNOWN;
+ // Iterate over the tree to find selected keys.
+ for( QListViewItem *lvi = mListView->firstChild();
+ 0 != lvi;
+ lvi = lvi->nextSibling() ) {
+ // We make sure that either all items belonging to a key are selected
+ // or unselected. As it's possible to select/deselect multiple keys at
+ // once in extended selection mode we have to figure out whether the user
+ // selected or deselected keys.
+
+ // First count the selected items of this key
+ int itemCount = 1 + lvi->childCount();
+ int selectedCount = lvi->isSelected() ? 1 : 0;
+ for( QListViewItem *clvi = lvi->firstChild();
+ 0 != clvi;
+ clvi = clvi->nextSibling() ) {
+ if( clvi->isSelected() ) {
+ ++selectedCount;
+ }
+ }
+
+ if( userAction == UNKNOWN ) {
+ // Figure out whether the user selected or deselected this key
+ // Remark: A selected count of 0 doesn't mean anything since in
+ // extended selection mode a normal left click deselects
+ // the not clicked items.
+ if( 0 < selectedCount ) {
+ if( -1 == mKeyIds.findIndex( lvi->text(0).local8Bit() ) ) {
+ // some items of this key are selected and the key wasn't selected
+ // before => the user selected something
+ kdDebug(5100) << "selectedCount: "<<selectedCount<<"/"<<itemCount
+ <<" --- User selected key "<<lvi->text(0)<<endl;
+ userAction = SELECTED;
+ }
+ else if( ( itemCount > selectedCount ) &&
+ ( -1 != mKeyIds.findIndex( lvi->text(0).local8Bit() ) ) ) {
+ // some items of this key are unselected and the key was selected
+ // before => the user deselected something
+ kdDebug(5100) << "selectedCount: "<<selectedCount<<"/"<<itemCount
+ <<" --- User deselected key "<<lvi->text(0)<<endl;
+ userAction = DESELECTED;
+ }
+ }
+ }
+ if( itemCount == selectedCount ) {
+ // add key to the list of selected keys
+ KeyID keyId = lvi->text(0).local8Bit();
+ newKeyIdList.append( keyId );
+ int admissibility = keyAdmissibility( lvi, NoExpensiveTrustCheck );
+ if( -1 == admissibility ) {
+ keysAllowed = false;
+ }
+ else if ( 0 == admissibility ) {
+ keysToBeChecked.append( lvi );
+ }
+ }
+ else if( 0 < selectedCount ) {
+ // not all items of this key are selected or unselected. change this
+ // according to the user's action
+ if( userAction == SELECTED ) {
+ // select all items of this key
+ mListView->setSelected( lvi, true );
+ for( QListViewItem *clvi = lvi->firstChild();
+ 0 != clvi;
+ clvi = clvi->nextSibling() ) {
+ mListView->setSelected( clvi, true );
+ }
+ // add key to the list of selected keys
+ KeyID keyId = lvi->text(0).local8Bit();
+ newKeyIdList.append( keyId );
+ int admissibility = keyAdmissibility( lvi, NoExpensiveTrustCheck );
+ if( -1 == admissibility ) {
+ keysAllowed = false;
+ }
+ else if ( 0 == admissibility ) {
+ keysToBeChecked.append( lvi );
+ }
+ }
+ else { // userAction == DESELECTED
+ // deselect all items of this key
+ mListView->setSelected( lvi, false );
+ for( QListViewItem *clvi = lvi->firstChild();
+ 0 != clvi;
+ clvi = clvi->nextSibling() ) {
+ mListView->setSelected( clvi, false );
+ }
+ }
+ }
+ }
+ kdDebug(5100) << "Selected keys: " << newKeyIdList.toStringList().join(", ") << endl;
+ mKeyIds = newKeyIdList;
+ if( !keysToBeChecked.isEmpty() ) {
+ keysAllowed = keysAllowed && checkKeys( keysToBeChecked );
+ }
+ enableButtonOK( keysAllowed );
+
+ connect( mListView, SIGNAL( selectionChanged() ),
+ this, SLOT( slotSelectionChanged() ) );
+ }
+}
+
+
+bool KeySelectionDialog::checkKeys( const QValueList<QListViewItem*>& keys ) const
+{
+ KProgressDialog* pProgressDlg = 0;
+ bool keysAllowed = true;
+ kdDebug(5100) << "Checking keys...\n";
+
+ pProgressDlg = new KProgressDialog( 0, 0, i18n("Checking Keys"),
+ i18n("Checking key 0xMMMMMMMM..."),
+ true );
+ pProgressDlg->setAllowCancel( false );
+ pProgressDlg->progressBar()->setTotalSteps( keys.count() );
+ pProgressDlg->setMinimumDuration( 1000 );
+ pProgressDlg->show();
+
+ for( QValueList<QListViewItem*>::ConstIterator it = keys.begin();
+ it != keys.end();
+ ++it ) {
+ kdDebug(5100) << "Checking key 0x" << getKeyId( *it ) << "...\n";
+ pProgressDlg->setLabel( i18n("Checking key 0x%1...")
+ .arg( getKeyId( *it ) ) );
+ kapp->processEvents();
+ keysAllowed = keysAllowed && ( -1 != keyAdmissibility( *it, AllowExpensiveTrustCheck ) );
+ pProgressDlg->progressBar()->advance( 1 );
+ kapp->processEvents();
+ }
+
+ delete pProgressDlg;
+ pProgressDlg = 0;
+
+ return keysAllowed;
+}
+
+
+void KeySelectionDialog::slotRMB( QListViewItem* lvi, const QPoint& pos, int )
+{
+ if( !lvi ) {
+ return;
+ }
+
+ mCurrentContextMenuItem = lvi;
+
+ QPopupMenu menu(this);
+ menu.insertItem( i18n( "Recheck Key" ), this, SLOT( slotRecheckKey() ) );
+ menu.exec( pos );
+}
+
+
+void KeySelectionDialog::slotRecheckKey()
+{
+ if( 0 != mCurrentContextMenuItem ) {
+ // force rereading the key
+ keyAdmissibility( mCurrentContextMenuItem, ForceTrustCheck );
+ // recheck the selection
+ slotCheckSelection( mCurrentContextMenuItem );
+ }
+}
+
+void KeySelectionDialog::slotOk()
+{
+ if( mCheckSelectionTimer->isActive() ) {
+ slotCheckSelection();
+ }
+ mStartSearchTimer->stop();
+ accept();
+}
+
+
+void KeySelectionDialog::slotCancel()
+{
+ mCheckSelectionTimer->stop();
+ mStartSearchTimer->stop();
+ mKeyIds.clear();
+ reject();
+}
+
+void KeySelectionDialog::slotSearch( const QString & text )
+{
+ mSearchText = text.stripWhiteSpace().upper();
+ mStartSearchTimer->start( sCheckSelectionDelay, true /*single-shot*/ );
+}
+
+void KeySelectionDialog::slotFilter()
+{
+ if ( mSearchText.isEmpty() ) {
+ showAllItems();
+ return;
+ }
+
+ // OK, so we need to filter:
+ QRegExp keyIdRegExp( "(?:0x)?[A-F0-9]{1,8}", false /*case-insens.*/ );
+ if ( keyIdRegExp.exactMatch( mSearchText ) ) {
+ if ( mSearchText.startsWith( "0X" ) )
+ // search for keyID only:
+ filterByKeyID( mSearchText.mid( 2 ) );
+ else
+ // search for UID and keyID:
+ filterByKeyIDOrUID( mSearchText );
+ } else {
+ // search in UID:
+ filterByUID( mSearchText );
+ }
+}
+
+void KeySelectionDialog::filterByKeyID( const QString & keyID )
+{
+ assert( keyID.length() <= 8 );
+ assert( !keyID.isEmpty() ); // regexp in slotFilter should prevent these
+ if ( keyID.isEmpty() )
+ showAllItems();
+ else
+ for ( QListViewItem * item = mListView->firstChild() ; item ; item = item->nextSibling() )
+ item->setVisible( item->text( 0 ).upper().startsWith( keyID ) );
+}
+
+void KeySelectionDialog::filterByKeyIDOrUID( const QString & str )
+{
+ assert( !str.isEmpty() );
+
+ // match beginnings of words:
+ QRegExp rx( "\\b" + QRegExp::escape( str ), false );
+
+ for ( QListViewItem * item = mListView->firstChild() ; item ; item = item->nextSibling() )
+ item->setVisible( item->text( 0 ).upper().startsWith( str )
+ || rx.search( item->text( 1 ) ) >= 0
+ || anyChildMatches( item, rx ) );
+
+}
+
+void KeySelectionDialog::filterByUID( const QString & str )
+{
+ assert( !str.isEmpty() );
+
+ // match beginnings of words:
+ QRegExp rx( "\\b" + QRegExp::escape( str ), false );
+
+ for ( QListViewItem * item = mListView->firstChild() ; item ; item = item->nextSibling() )
+ item->setVisible( rx.search( item->text( 1 ) ) >= 0
+ || anyChildMatches( item, rx ) );
+}
+
+
+bool KeySelectionDialog::anyChildMatches( const QListViewItem * item, QRegExp & rx ) const
+{
+ if ( !item )
+ return false;
+
+ QListViewItem * stop = item->nextSibling(); // It's OK if stop is NULL...
+
+ for ( QListViewItemIterator it( item->firstChild() ) ; it.current() && it.current() != stop ; ++it )
+ if ( rx.search( it.current()->text( 1 ) ) >= 0 ) {
+ //item->setOpen( true ); // do we want that?
+ return true;
+ }
+ return false;
+}
+
+void KeySelectionDialog::showAllItems()
+{
+ for ( QListViewItem * item = mListView->firstChild() ; item ; item = item->nextSibling() )
+ item->setVisible( true );
+}
+
+// ------------------------------------------------------------------------
+KeyRequester::KeyRequester( QWidget * parent, bool multipleKeys,
+ unsigned int allowedKeys, const char * name )
+ : QWidget( parent, name ),
+ mDialogCaption( i18n("OpenPGP Key Selection") ),
+ mDialogMessage( i18n("Please select an OpenPGP key to use.") ),
+ mMulti( multipleKeys ),
+ mAllowedKeys( allowedKeys ),
+ d( 0 )
+{
+ QHBoxLayout * hlay = new QHBoxLayout( this, 0, KDialog::spacingHint() );
+
+ // the label where the key id is to be displayed:
+ mLabel = new QLabel( this );
+ mLabel->setFrameStyle( QFrame::Panel | QFrame::Sunken );
+
+ // the button to unset any key:
+ mEraseButton = new QPushButton( this );
+ mEraseButton->setAutoDefault( false );
+ mEraseButton->setSizePolicy( QSizePolicy( QSizePolicy::Minimum,
+ QSizePolicy::Minimum ) );
+ mEraseButton->setPixmap( SmallIcon( "clear_left" ) );
+ QToolTip::add( mEraseButton, i18n("Clear") );
+
+ // the button to call the KeySelectionDialog:
+ mDialogButton = new QPushButton( i18n("Change..."), this );
+ mDialogButton->setAutoDefault( false );
+
+ hlay->addWidget( mLabel, 1 );
+ hlay->addWidget( mEraseButton );
+ hlay->addWidget( mDialogButton );
+
+ connect( mEraseButton, SIGNAL(clicked()), SLOT(slotEraseButtonClicked()) );
+ connect( mDialogButton, SIGNAL(clicked()), SLOT(slotDialogButtonClicked()) );
+
+ setSizePolicy( QSizePolicy( QSizePolicy::MinimumExpanding,
+ QSizePolicy::Fixed ) );
+}
+
+KeyRequester::~KeyRequester() {
+
+}
+
+KeyIDList KeyRequester::keyIDs() const {
+ return mKeys;
+}
+
+void KeyRequester::setKeyIDs( const KeyIDList & keyIDs ) {
+ mKeys = keyIDs;
+ if ( mKeys.empty() ) {
+ mLabel->clear();
+ return;
+ }
+ if ( mKeys.size() > 1 )
+ setMultipleKeysEnabled( true );
+
+ QString s = mKeys.toStringList().join(", ");
+
+ mLabel->setText( s );
+ QToolTip::remove( mLabel );
+ QToolTip::add( mLabel, s );
+}
+
+void KeyRequester::slotDialogButtonClicked() {
+ Module * pgp = Module::getKpgp();
+
+ if ( !pgp ) {
+ kdWarning() << "Kpgp::KeyRequester::slotDialogButtonClicked(): No pgp module found!" << endl;
+ return;
+ }
+
+ setKeyIDs( keyRequestHook( pgp ) );
+ emit changed();
+}
+
+void KeyRequester::slotEraseButtonClicked() {
+ mKeys.clear();
+ mLabel->clear();
+ emit changed();
+}
+
+void KeyRequester::setDialogCaption( const QString & caption ) {
+ mDialogCaption = caption;
+}
+
+void KeyRequester::setDialogMessage( const QString & msg ) {
+ mDialogMessage = msg;
+}
+
+bool KeyRequester::isMultipleKeysEnabled() const {
+ return mMulti;
+}
+
+void KeyRequester::setMultipleKeysEnabled( bool multi ) {
+ if ( multi == mMulti ) return;
+
+ if ( !multi && mKeys.size() > 1 )
+ mKeys.erase( ++mKeys.begin(), mKeys.end() );
+
+ mMulti = multi;
+}
+
+int KeyRequester::allowedKeys() const {
+ return mAllowedKeys;
+}
+
+void KeyRequester::setAllowedKeys( int allowedKeys ) {
+ mAllowedKeys = allowedKeys;
+}
+
+
+PublicKeyRequester::PublicKeyRequester( QWidget * parent, bool multi,
+ unsigned int allowed, const char * name )
+ : KeyRequester( parent, multi, allowed & ~SecretKeys, name )
+{
+
+}
+
+PublicKeyRequester::~PublicKeyRequester() {
+
+}
+
+KeyIDList PublicKeyRequester::keyRequestHook( Module * pgp ) const {
+ assert( pgp );
+ return pgp->selectPublicKeys( mDialogCaption, mDialogMessage, mKeys, QString::null, mAllowedKeys );
+}
+
+SecretKeyRequester::SecretKeyRequester( QWidget * parent, bool multi,
+ unsigned int allowed, const char * name )
+ : KeyRequester( parent, multi, allowed & ~PublicKeys, name )
+{
+
+}
+
+SecretKeyRequester::~SecretKeyRequester() {
+
+}
+
+KeyIDList SecretKeyRequester::keyRequestHook( Module * pgp ) const {
+ assert( pgp );
+
+ KeyID keyID = mKeys.first();
+ keyID = pgp->selectSecretKey( mDialogCaption, mDialogMessage, keyID );
+
+ return KeyIDList() << keyID;
+}
+
+
+
+// ------------------------------------------------------------------------
+KeyApprovalDialog::KeyApprovalDialog( const QStringList& addresses,
+ const QValueVector<KeyIDList>& keyIDs,
+ const int allowedKeys,
+ QWidget *parent, const char *name,
+ bool modal )
+ : KDialogBase( parent, name, modal, i18n("Encryption Key Approval"),
+ Ok|Cancel, Ok ),
+ mKeys( keyIDs ),
+ mAllowedKeys( allowedKeys ),
+ mPrefsChanged( false )
+{
+ Kpgp::Module *pgp = Kpgp::Module::getKpgp();
+
+ if( pgp == 0 )
+ return;
+
+ // ##### error handling
+ // if( addresses.isEmpty() || keyList.isEmpty() ||
+ // addresses.count()+1 != keyList.count() )
+ // do something;
+
+ QFrame *page = makeMainWidget();
+ QVBoxLayout *topLayout = new QVBoxLayout( page, 0, KDialog::spacingHint() );
+
+ QLabel *label = new QLabel( i18n("The following keys will be used for "
+ "encryption:"),
+ page );
+ topLayout->addWidget( label );
+
+ QScrollView* sv = new QScrollView( page );
+ sv->setResizePolicy( QScrollView::AutoOneFit );
+ topLayout->addWidget( sv );
+ QVBox* bigvbox = new QVBox( sv->viewport() );
+ bigvbox->setMargin( KDialog::marginHint() );
+ bigvbox->setSpacing( KDialog::spacingHint() );
+ sv->addChild( bigvbox );
+
+ QButtonGroup *mChangeButtonGroup = new QButtonGroup( bigvbox );
+ mChangeButtonGroup->hide();
+ mAddressLabels.resize( addresses.count() );
+ mKeyIdsLabels.resize( keyIDs.size() );
+ //mKeyIdListBoxes.resize( keyIDs.size() );
+ mEncrPrefCombos.resize( addresses.count() );
+
+ // the sender's key
+ if( pgp->encryptToSelf() ) {
+ mEncryptToSelf = 1;
+ QHBox* hbox = new QHBox( bigvbox );
+ new QLabel( i18n("Your keys:"), hbox );
+ QLabel* keyidsL = new QLabel( hbox );
+ if( keyIDs[0].isEmpty() ) {
+ keyidsL->setText( i18n("<none> means 'no key'", "<none>") );
+ }
+ else {
+ keyidsL->setText( "0x" + keyIDs[0].toStringList().join( "\n0x" ) );
+ }
+ keyidsL->setFrameStyle( QFrame::Panel | QFrame::Sunken );
+ /*
+ QListBox* keyidLB = new QListBox( hbox );
+ if( keyIDs[0].isEmpty() ) {
+ keyidLB->insertItem( i18n("<none>") );
+ }
+ else {
+ keyidLB->insertStringList( keyIDs[0].toStringList() );
+ }
+ keyidLB->setSelectionMode( QListBox::NoSelection );
+ keyidLB->setFrameStyle( QFrame::Panel | QFrame::Sunken );
+ */
+ QPushButton *button = new QPushButton( i18n("Change..."), hbox );
+ mChangeButtonGroup->insert( button );
+ button->setAutoDefault( false );
+ hbox->setStretchFactor( keyidsL, 10 );
+ mKeyIdsLabels.insert( 0, keyidsL );
+ //hbox->setStretchFactor( keyidLB, 10 );
+ //mKeyIdListBoxes.insert( 0, keyidLB );
+
+ new KSeparator( Horizontal, bigvbox );
+ }
+ else {
+ mEncryptToSelf = 0;
+ // insert dummy KeyIdListBox
+ mKeyIdsLabels.insert( 0, 0 );
+ //mKeyIdListBoxes.insert( 0, 0 );
+ }
+
+ QStringList::ConstIterator ait;
+ QValueVector<KeyIDList>::const_iterator kit;
+ int i;
+ for( ait = addresses.begin(), kit = keyIDs.begin(), i = 0;
+ ( ait != addresses.end() ) && ( kit != keyIDs.end() );
+ ++ait, ++kit, ++i ) {
+ if( i == 0 ) {
+ ++kit; // skip the sender's key id
+ }
+ else {
+ new KSeparator( Horizontal, bigvbox );
+ }
+
+ QHBox *hbox = new QHBox( bigvbox );
+ new QLabel( i18n("Recipient:"), hbox );
+ QLabel *addressL = new QLabel( *ait, hbox );
+ hbox->setStretchFactor( addressL, 10 );
+ mAddressLabels.insert( i, addressL );
+
+ hbox = new QHBox( bigvbox );
+ new QLabel( i18n("Encryption keys:"), hbox );
+ QLabel* keyidsL = new QLabel( hbox );
+ if( (*kit).isEmpty() ) {
+ keyidsL->setText( i18n("<none> means 'no key'", "<none>") );
+ }
+ else {
+ keyidsL->setText( "0x" + (*kit).toStringList().join( "\n0x" ) );
+ }
+ keyidsL->setFrameStyle( QFrame::Panel | QFrame::Sunken );
+ /*
+ QListBox* keyidLB = new QListBox( hbox );
+ if( (*kit).isEmpty() ) {
+ keyidLB->insertItem( i18n("<none>") );
+ }
+ else {
+ keyidLB->insertStringList( (*kit).toStringList() );
+ }
+ keyidLB->setSelectionMode( QListBox::NoSelection );
+ keyidLB->setFrameStyle( QFrame::Panel | QFrame::Sunken );
+ */
+ QPushButton *button = new QPushButton( i18n("Change..."), hbox );
+ mChangeButtonGroup->insert( button );
+ button->setAutoDefault( false );
+ hbox->setStretchFactor( keyidsL, 10 );
+ mKeyIdsLabels.insert( i + 1, keyidsL );
+ //hbox->setStretchFactor( keyidLB, 10 );
+ //mKeyIdListBoxes.insert( i + 1, keyidLB );
+
+ hbox = new QHBox( bigvbox );
+ new QLabel( i18n("Encryption preference:"), hbox );
+ QComboBox *encrPrefCombo = new QComboBox( hbox );
+ encrPrefCombo->insertItem( i18n("<none>") );
+ encrPrefCombo->insertItem( i18n("Never Encrypt with This Key") );
+ encrPrefCombo->insertItem( i18n("Always Encrypt with This Key") );
+ encrPrefCombo->insertItem( i18n("Encrypt Whenever Encryption is Possible") );
+ encrPrefCombo->insertItem( i18n("Always Ask") );
+ encrPrefCombo->insertItem( i18n("Ask Whenever Encryption is Possible") );
+
+ EncryptPref encrPref = pgp->encryptionPreference( *ait );
+ switch( encrPref ) {
+ case NeverEncrypt:
+ encrPrefCombo->setCurrentItem( 1 );
+ break;
+ case AlwaysEncrypt:
+ encrPrefCombo->setCurrentItem( 2 );
+ break;
+ case AlwaysEncryptIfPossible:
+ encrPrefCombo->setCurrentItem( 3 );
+ break;
+ case AlwaysAskForEncryption:
+ encrPrefCombo->setCurrentItem( 4 );
+ break;
+ case AskWheneverPossible:
+ encrPrefCombo->setCurrentItem( 5 );
+ break;
+ default:
+ encrPrefCombo->setCurrentItem( 0 );
+ }
+ connect( encrPrefCombo, SIGNAL(activated(int)),
+ this, SLOT(slotPrefsChanged(int)) );
+ mEncrPrefCombos.insert( i, encrPrefCombo );
+ }
+ connect( mChangeButtonGroup, SIGNAL(clicked(int)),
+ this, SLOT(slotChangeEncryptionKey(int)) );
+
+ // calculate the optimal width for the dialog
+ int dialogWidth = marginHint()
+ + sv->frameWidth()
+ + bigvbox->sizeHint().width()
+ + sv->verticalScrollBar()->sizeHint().width()
+ + sv->frameWidth()
+ + marginHint()
+ + 2;
+ // calculate the optimal height for the dialog
+ int dialogHeight = marginHint()
+ + label->sizeHint().height()
+ + topLayout->spacing()
+ + sv->frameWidth()
+ + bigvbox->sizeHint().height()
+ + sv->horizontalScrollBar()->sizeHint().height()
+ + sv->frameWidth()
+ + topLayout->spacing()
+ + actionButton( KDialogBase::Cancel )->sizeHint().height()
+ + marginHint()
+ + 2;
+ // don't make the dialog too large
+ QRect desk = KGlobalSettings::desktopGeometry(this);
+ int screenWidth = desk.width();
+ if( dialogWidth > 3*screenWidth/4 )
+ dialogWidth = 3*screenWidth/4;
+ int screenHeight = desk.height();
+ if( dialogHeight > 7*screenHeight/8 )
+ dialogHeight = 7*screenHeight/8;
+
+ setInitialSize( QSize( dialogWidth, dialogHeight ) );
+}
+
+void
+KeyApprovalDialog::slotChangeEncryptionKey( int nr )
+{
+ Kpgp::Module *pgp = Kpgp::Module::getKpgp();
+
+ kdDebug(5100)<<"Key approval dialog size is "
+ <<width()<<"x"<<height()<<endl;
+
+ if( pgp == 0 )
+ return;
+
+ if( !mEncryptToSelf )
+ nr++;
+ KeyIDList keyIds = mKeys[nr];
+ if( nr == 0 ) {
+ keyIds = pgp->selectPublicKeys( i18n("Encryption Key Selection"),
+ i18n("if in your language something like "
+ "'key(s)' isn't possible please "
+ "use the plural in the translation",
+ "Select the key(s) which should "
+ "be used to encrypt the message "
+ "to yourself."),
+ keyIds,
+ "",
+ mAllowedKeys );
+ }
+ else {
+ keyIds = pgp->selectPublicKeys( i18n("Encryption Key Selection"),
+ i18n("if in your language something like "
+ "'key(s)' isn't possible please "
+ "use the plural in the translation",
+ "Select the key(s) which should "
+ "be used to encrypt the message "
+ "for\n%1")
+ .arg( mAddressLabels[nr-1]->text() ),
+ keyIds,
+ mAddressLabels[nr-1]->text(),
+ mAllowedKeys );
+ }
+ if( !keyIds.isEmpty() ) {
+ mKeys[nr] = keyIds;
+ QLabel* keyidsL = mKeyIdsLabels[nr];
+ keyidsL->setText( "0x" + keyIds.toStringList().join( "\n0x" ) );
+ /*
+ QListBox* qlb = mKeyIdListBoxes[nr];
+ qlb->clear();
+ qlb->insertStringList( keyIds.toStringList() );
+ */
+ }
+}
+
+
+void
+KeyApprovalDialog::slotOk()
+{
+ Kpgp::Module *pgp = Kpgp::Module::getKpgp();
+
+ if( pgp == 0 ) {
+ accept();
+ return;
+ }
+
+ if( mPrefsChanged ) {
+ // store the changed preferences
+ for( unsigned int i = 0; i < mAddressLabels.size(); i++ ) {
+ // traverse all Address and Encryption Preference widgets
+ EncryptPref encrPref;
+ switch( mEncrPrefCombos[i]->currentItem() ) {
+ case 1:
+ encrPref = NeverEncrypt;
+ break;
+ case 2:
+ encrPref = AlwaysEncrypt;
+ break;
+ case 3:
+ encrPref = AlwaysEncryptIfPossible;
+ break;
+ case 4:
+ encrPref = AlwaysAskForEncryption;
+ break;
+ case 5:
+ encrPref = AskWheneverPossible;
+ break;
+ default:
+ case 0:
+ encrPref = UnknownEncryptPref;
+ }
+ pgp->setEncryptionPreference( mAddressLabels[i]->text(), encrPref );
+ }
+ }
+
+ accept();
+}
+
+
+void
+KeyApprovalDialog::slotCancel()
+{
+ reject();
+}
+
+
+
+// ------------------------------------------------------------------------
+CipherTextDialog::CipherTextDialog( const QCString & text,
+ const QCString & charset, QWidget *parent,
+ const char *name, bool modal )
+ :KDialogBase( parent, name, modal, i18n("OpenPGP Information"), Ok|Cancel, Ok)
+{
+ // FIXME (post KDE2.2): show some more info, e.g. the output of GnuPG/PGP
+ QFrame *page = makeMainWidget();
+ QVBoxLayout *topLayout = new QVBoxLayout( page, 0, spacingHint() );
+
+ QLabel *label = new QLabel( page );
+ label->setText(i18n("Result of the last encryption/sign operation:"));
+ topLayout->addWidget( label );
+
+ mEditBox = new QMultiLineEdit( page );
+ mEditBox->setReadOnly(true);
+ topLayout->addWidget( mEditBox, 10 );
+
+ QString unicodeText;
+ if (charset.isEmpty())
+ unicodeText = QString::fromLocal8Bit(text.data());
+ else {
+ bool ok=true;
+ QTextCodec *codec = KGlobal::charsets()->codecForName(charset, ok);
+ if(!ok)
+ unicodeText = QString::fromLocal8Bit(text.data());
+ else
+ unicodeText = codec->toUnicode(text.data(), text.length());
+ }
+
+ mEditBox->setText(unicodeText);
+
+ setMinimumSize();
+}
+
+void CipherTextDialog::setMinimumSize()
+{
+ // this seems to force a layout of the entire document, so we get a
+ // a proper contentsWidth(). Is there a better way?
+ for ( int i = 0; i < mEditBox->paragraphs(); i++ )
+ (void) mEditBox->paragraphRect( i );
+
+ mEditBox->setMinimumHeight( mEditBox->fontMetrics().lineSpacing() * 25 );
+
+ int textWidth = mEditBox->contentsWidth() + 30;
+
+
+#if KDE_IS_VERSION( 3, 1, 90 )
+ int maxWidth = KGlobalSettings::desktopGeometry(parentWidget()).width()-100;
+#else
+ KConfig gc("kdeglobals", false, false);
+ gc.setGroup("Windows");
+ int maxWidth;
+ if (QApplication::desktop()->isVirtualDesktop() &&
+ gc.readBoolEntry("XineramaEnabled", true) &&
+ gc.readBoolEntry("XineramaPlacementEnabled", true)) {
+ maxWidth = QApplication::desktop()->screenGeometry(QApplication::desktop()->screenNumber(parentWidget())).width()-100;
+ } else {
+ maxWidth = QApplication::desktop()->geometry().width()-100;
+ }
+#endif
+
+ mEditBox->setMinimumWidth( QMIN( textWidth, maxWidth ) );
+}
+
+void KeyRequester::virtual_hook( int, void* ) {}
+
+void PublicKeyRequester::virtual_hook( int id, void* data ) {
+ base::virtual_hook( id, data );
+}
+
+void SecretKeyRequester::virtual_hook( int id, void* data ) {
+ base::virtual_hook( id, data );
+}
+
+} // namespace Kpgp
+
+
+
+#include "kpgpui.moc"
diff --git a/libkpgp/kpgpui.h b/libkpgp/kpgpui.h
new file mode 100644
index 00000000..eab9a53e
--- /dev/null
+++ b/libkpgp/kpgpui.h
@@ -0,0 +1,343 @@
+/* -*- c++ -*-
+ kpgpui.h
+
+ Copyright (C) 2001,2002 the KPGP authors
+ See file AUTHORS.kpgp for details
+
+ This file is part of KPGP, the KDE PGP/GnuPG support library.
+
+ KPGP is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#ifndef KPGPUI_H
+#define KPGPUI_H
+
+#include <kdialogbase.h> // base class of all dialogs here
+#include <qwidget.h> // base class of Config
+#include <qcheckbox.h> // used in inlined methods
+#include <kdebug.h> // used in inlined methods
+#include <qcstring.h> // used in return-by-value
+#include <qstring.h> // is a member in KeyRequester
+#include <qvaluevector.h> // used in KeyApprovalDialog
+
+#include "kpgp.h"
+
+#include <kdepimmacros.h>
+
+class QString;
+class QRegExp;
+class QCString;
+class QCheckBox; // needed by Config, KeySelectionDialog
+class QMultiLineEdit; // needed by CipherTextDialog
+class QComboBox; // needed by Config
+class QPixmap; // needed by KeySelectionDialog
+class QPushButton; // needed by KeyRequester
+class QTimer; // needed by KeySelectionDialog
+
+class KListView; // needed by KeySelectionDialog
+class KPasswordEdit; // needed by PassphraseDialog
+
+namespace Kpgp {
+
+class Module;
+class KeyList; // needed by KeySelectionDialog
+class Key; // needed by KeySelectionDialog
+class KeyIDList; // needed by KeySelectionDialog
+
+/** the passphrase dialog */
+class KDE_EXPORT PassphraseDialog : public KDialogBase
+{
+ Q_OBJECT
+
+ public:
+ PassphraseDialog( QWidget *parent=0, const QString &caption=QString::null,
+ bool modal=true, const QString &keyID=QString::null);
+ virtual ~PassphraseDialog();
+
+ const char * passphrase();
+
+ private:
+ KPasswordEdit *lineedit;
+};
+
+
+// -------------------------------------------------------------------------
+/** a widget for configuring the pgp interface. Can be included into
+ a tabdialog. This widget by itself does not provide an apply/cancel
+ button mechanism. */
+class KDE_EXPORT Config : public QWidget
+{
+ Q_OBJECT
+
+ public:
+ Config(QWidget *parent = 0, const char *name = 0, bool encrypt =true);
+ virtual ~Config();
+
+ virtual void setValues();
+ virtual void applySettings();
+ QGroupBox* optionsGroupBox() { return mpOptionsGroupBox; };
+ signals:
+ void changed();
+
+ protected:
+ Module *pgp;
+ QCheckBox *storePass;
+ QCheckBox *encToSelf;
+ QCheckBox *showCipherText;
+ QCheckBox *showKeyApprovalDlg;
+ QComboBox *toolCombo;
+ QGroupBox* mpOptionsGroupBox;
+};
+
+
+// -------------------------------------------------------------------------
+#define KeySelectionDialogSuper KDialogBase
+class KDE_EXPORT KeySelectionDialog: public KeySelectionDialogSuper
+{
+ Q_OBJECT
+
+ enum TrustCheckMode { NoExpensiveTrustCheck,
+ AllowExpensiveTrustCheck,
+ ForceTrustCheck
+ };
+
+ public:
+ /** allowedKeys: see kpgp.h
+ */
+ KeySelectionDialog( const KeyList& keyList,
+ const QString& title,
+ const QString& text = QString::null,
+ const KeyIDList& keyIds = KeyIDList(),
+ const bool rememberChoice = false,
+ const unsigned int allowedKeys = AllKeys,
+ const bool extendedSelection = false,
+ QWidget *parent=0, const char *name=0,
+ bool modal=true );
+ virtual ~KeySelectionDialog();
+
+ /** Returns the key ID of the selected key in single selection mode.
+ Otherwise it returns a null string. */
+ virtual KeyID key() const;
+
+ /** Returns a list of selected key IDs. */
+ virtual KeyIDList keys() const
+ { return mKeyIds; };
+
+ virtual bool rememberSelection() const
+ { if( mRememberCB )
+ return mRememberCB->isChecked();
+ else
+ return false;
+ };
+
+ protected slots:
+ virtual void slotRereadKeys();
+ virtual void slotSelectionChanged( QListViewItem* );
+ virtual void slotSelectionChanged();
+ virtual void slotCheckSelection( QListViewItem* = 0 );
+ virtual void slotRMB( QListViewItem*, const QPoint&, int );
+ virtual void slotRecheckKey();
+ virtual void slotOk();
+ virtual void slotCancel();
+ virtual void slotSearch( const QString & text );
+ virtual void slotFilter();
+
+ private:
+ void filterByKeyID( const QString & keyID );
+ void filterByKeyIDOrUID( const QString & keyID );
+ void filterByUID( const QString & uid );
+ void showAllItems();
+ bool anyChildMatches( const QListViewItem * item, QRegExp & rx ) const;
+
+ void initKeylist( const KeyList& keyList, const KeyIDList& keyIds );
+
+ QString keyInfo( const Kpgp::Key* ) const;
+
+ QString beautifyFingerprint( const QCString& ) const;
+
+ // Returns the key ID of the key the given QListViewItem belongs to
+ KeyID getKeyId( const QListViewItem* ) const;
+
+ // Returns: -1 = unusable, 0 = unknown, 1 = valid, but untrusted, 2 = trusted
+ int keyValidity( const Kpgp::Key* ) const;
+
+ // Updates the given QListViewItem with the data of the given key
+ void updateKeyInfo( const Kpgp::Key*, QListViewItem* ) const;
+
+ /** Checks if choosing the given key is allowed
+ Returns:
+ -1 = key must not be chosen,
+ 0 = not enough information to decide whether the give key is allowed
+ or not,
+ 1 = key can be chosen
+ */
+ int keyAdmissibility( QListViewItem*,
+ TrustCheckMode = NoExpensiveTrustCheck ) const;
+
+ // Perform expensive trust checks for the given keys
+ bool checkKeys( const QValueList<QListViewItem*>& ) const;
+
+ private:
+ KListView *mListView;
+ QCheckBox *mRememberCB;
+ QPixmap *mKeyGoodPix, *mKeyBadPix, *mKeyUnknownPix, *mKeyValidPix;
+ KeyIDList mKeyIds;
+ unsigned int mAllowedKeys;
+ QTimer* mCheckSelectionTimer;
+ QTimer* mStartSearchTimer;
+ QString mSearchText;
+ QListViewItem* mCurrentContextMenuItem;
+
+ static const int sCheckSelectionDelay;
+};
+
+class KDE_EXPORT KeyRequester: public QWidget
+{
+ Q_OBJECT
+
+public:
+ KeyRequester( QWidget * parent=0, bool multipleKeys=false,
+ unsigned int allowedKeys=AllKeys, const char * name=0 );
+ virtual ~KeyRequester();
+
+ KeyIDList keyIDs() const;
+ void setKeyIDs( const KeyIDList & keyIDs );
+
+ QPushButton * eraseButton() const { return mEraseButton; }
+ QPushButton * dialogButton() const { return mDialogButton; }
+
+ void setDialogCaption( const QString & caption );
+ void setDialogMessage( const QString & message );
+
+ bool isMultipleKeysEnabled() const;
+ void setMultipleKeysEnabled( bool enable );
+
+ int allowedKeys() const;
+ void setAllowedKeys( int allowed );
+
+protected:
+ /** Reimplement this to return a list of selected keys. */
+ virtual KeyIDList keyRequestHook( Module * pgp ) const = 0;
+
+protected:
+ QLabel * mLabel;
+ QPushButton * mEraseButton;
+ QPushButton * mDialogButton;
+ QString mDialogCaption, mDialogMessage;
+ bool mMulti;
+ int mAllowedKeys;
+ KeyIDList mKeys;
+
+protected slots:
+ void slotDialogButtonClicked();
+ void slotEraseButtonClicked();
+
+signals:
+ void changed();
+
+private:
+ class Private;
+ Private * d;
+protected:
+ virtual void virtual_hook( int, void* );
+};
+
+
+class KDE_EXPORT PublicKeyRequester : public KeyRequester {
+ Q_OBJECT
+public:
+ PublicKeyRequester( QWidget * parent=0, bool multipleKeys=false,
+ unsigned int allowedKeys=PublicKeys, const char * name=0 );
+ virtual ~PublicKeyRequester();
+
+protected:
+ KeyIDList keyRequestHook( Module * pgp ) const;
+
+private:
+ typedef KeyRequester base;
+ class Private;
+ Private * d;
+protected:
+ virtual void virtual_hook( int, void* );
+};
+
+
+class KDE_EXPORT SecretKeyRequester : public KeyRequester {
+ Q_OBJECT
+public:
+ SecretKeyRequester( QWidget * parent=0, bool multipleKeys=false,
+ unsigned int allowedKeys=SecretKeys, const char * name=0 );
+ virtual ~SecretKeyRequester();
+
+protected:
+ KeyIDList keyRequestHook( Module * pgp ) const;
+
+private:
+ typedef KeyRequester base;
+ class Private;
+ Private * d;
+protected:
+ virtual void virtual_hook( int, void* );
+};
+
+
+// -------------------------------------------------------------------------
+class KDE_EXPORT KeyApprovalDialog: public KDialogBase
+{
+ Q_OBJECT
+
+ public:
+ KeyApprovalDialog( const QStringList&,
+ const QValueVector<KeyIDList>&,
+ const int allowedKeys,
+ QWidget *parent = 0, const char *name = 0,
+ bool modal = true );
+ virtual ~KeyApprovalDialog() {};
+
+ QValueVector<KeyIDList> keys() const { return mKeys; };
+
+ bool preferencesChanged() const { return mPrefsChanged; }
+
+ protected slots:
+ void slotPrefsChanged( int ) { mPrefsChanged = true; };
+ void slotChangeEncryptionKey( int );
+ virtual void slotOk();
+ virtual void slotCancel();
+
+ private:
+ QValueVector<KeyIDList> mKeys;
+ int mAllowedKeys;
+ int mEncryptToSelf;
+ bool mPrefsChanged;
+ QPtrVector<QLabel> mAddressLabels;
+ QPtrVector<QLabel> mKeyIdsLabels;
+ //QPtrVector<QListBox> mKeyIdListBoxes;
+ QPtrVector<QComboBox> mEncrPrefCombos;
+};
+
+
+// -------------------------------------------------------------------------
+class KDE_EXPORT CipherTextDialog: public KDialogBase
+{
+ Q_OBJECT
+
+ public:
+ CipherTextDialog( const QCString & text, const QCString & charset=0,
+ QWidget *parent=0, const char *name=0, bool modal=true );
+ virtual ~CipherTextDialog() {};
+
+ private:
+ void setMinimumSize();
+ QMultiLineEdit *mEditBox;
+};
+
+} // namespace Kpgp
+
+#endif
diff --git a/libkpgp/pics/Makefile.am b/libkpgp/pics/Makefile.am
new file mode 100644
index 00000000..7db72de1
--- /dev/null
+++ b/libkpgp/pics/Makefile.am
@@ -0,0 +1,9 @@
+
+kmaildata_DATA = key_ok.png key_bad.png key_unknown.png key.png
+knodedata_DATA = $(kmaildata_DATA)
+
+kmaildatadir = $(kde_datadir)/kmail/pics
+knodedatadir = $(kde_datadir)/knode/pics
+
+EXTRA_DIST = $(kmaildata_DATA)
+
diff --git a/libkpgp/pics/key.png b/libkpgp/pics/key.png
new file mode 100644
index 00000000..2da8bd75
--- /dev/null
+++ b/libkpgp/pics/key.png
Binary files differ
diff --git a/libkpgp/pics/key_bad.png b/libkpgp/pics/key_bad.png
new file mode 100644
index 00000000..1e0f8ca1
--- /dev/null
+++ b/libkpgp/pics/key_bad.png
Binary files differ
diff --git a/libkpgp/pics/key_ok.png b/libkpgp/pics/key_ok.png
new file mode 100644
index 00000000..3fa7e6f0
--- /dev/null
+++ b/libkpgp/pics/key_ok.png
Binary files differ
diff --git a/libkpgp/pics/key_unknown.png b/libkpgp/pics/key_unknown.png
new file mode 100644
index 00000000..ee3f542e
--- /dev/null
+++ b/libkpgp/pics/key_unknown.png
Binary files differ