Rename to match new Horde_Crypt API
authorMichael M Slusarz <slusarz@curecanti.org>
Sun, 16 Nov 2008 22:58:38 +0000 (15:58 -0700)
committerMichael M Slusarz <slusarz@curecanti.org>
Sun, 16 Nov 2008 22:58:38 +0000 (15:58 -0700)
imp/lib/Crypt/PGP.php [deleted file]
imp/lib/Crypt/SMIME.php [deleted file]
imp/lib/Crypt/pgp.php [new file with mode: 0644]
imp/lib/Crypt/smime.php [new file with mode: 0644]

diff --git a/imp/lib/Crypt/PGP.php b/imp/lib/Crypt/PGP.php
deleted file mode 100644 (file)
index 8020b7c..0000000
+++ /dev/null
@@ -1,761 +0,0 @@
-<?php
-/**
- * @package IMP
- */
-
-/** Horde_Crypt_pgp */
-require_once 'Horde/Crypt/pgp.php';
-
-/**
- * Name of PGP public key field in addressbook.
- */
-define('IMP_PGP_PUBKEY_FIELD', 'pgpPublicKey');
-
-/**
- * The IMP_PGP:: class contains all functions related to handling
- * PGP messages within IMP.
- *
- * Copyright 2002-2008 The Horde Project (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (GPL). If you
- * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
- *
- * @author  Michael Slusarz <slusarz@horde.org>
- * @package IMP
- */
-class IMP_PGP extends Horde_Crypt_pgp {
-
-    /**
-     * Constructor
-     */
-    function IMP_PGP()
-    {
-        parent::Horde_Crypt_pgp(array('program' => $GLOBALS['conf']['utils']['gnupg'],
-                                      'temp' => Horde::getTempDir()));
-    }
-
-    /**
-     * Generate the personal Public/Private keypair and store in prefs.
-     *
-     * @param string $realname    See Horde_Crypt_pgp::
-     * @param string $email       See Horde_Crypt_pgp::
-     * @param string $passphrase  See Horde_Crypt_pgp::
-     * @param string $comment     See Horde_Crypt_pgp::
-     * @param string $keylength   See Horde_Crypt_pgp::
-     *
-     * @return PEAR_Error  Returns PEAR_Error object on error.
-     */
-    function generatePersonalKeys($name, $email, $passphrase, $comment = '',
-                                  $keylength = 1024)
-    {
-        $keys = $this->generateKey($name, $email, $passphrase, $comment, $keylength);
-        if (is_a($keys, 'PEAR_Error')) {
-            return $keys;
-        }
-
-        /* Store the keys in the user's preferences. */
-        $this->addPersonalPublicKey($keys['public']);
-        $this->addPersonalPrivateKey($keys['private']);
-    }
-
-    /**
-     * Add the personal public key to the prefs.
-     *
-     * @param mixed $public_key  The public key to add (either string or
-     *                           array).
-     */
-    function addPersonalPublicKey($public_key)
-    {
-        $GLOBALS['prefs']->setValue('pgp_public_key', (is_array($public_key)) ? implode('', $public_key) : $public_key);
-    }
-
-    /**
-     * Add the personal private key to the prefs.
-     *
-     * @param mixed $private_key  The private key to add (either string or
-     *                            array).
-     */
-    function addPersonalPrivateKey($private_key)
-    {
-        $GLOBALS['prefs']->setValue('pgp_private_key', (is_array($private_key)) ? implode('', $private_key) : $private_key);
-    }
-
-    /**
-     * Get the personal public key from the prefs.
-     *
-     * @return string  The personal PGP public key.
-     */
-    function getPersonalPublicKey()
-    {
-        return $GLOBALS['prefs']->getValue('pgp_public_key');
-    }
-
-    /**
-     * Get the personal private key from the prefs.
-     *
-     * @return string  The personal PGP private key.
-     */
-    function getPersonalPrivateKey()
-    {
-        return $GLOBALS['prefs']->getValue('pgp_private_key');
-    }
-
-    /**
-     * Deletes the specified personal keys from the prefs.
-     */
-    function deletePersonalKeys()
-    {
-        $GLOBALS['prefs']->setValue('pgp_public_key', '');
-        $GLOBALS['prefs']->setValue('pgp_private_key', '');
-
-        $this->unsetPassphrase();
-    }
-
-    /**
-     * Add a public key to an address book.
-     *
-     * @param string $public_key  An PGP public key.
-     *
-     * @return array  See Horde_Crypt_pgp::pgpPacketInformation()
-     *                Returns PEAR_Error or error.
-     */
-    function addPublicKey($public_key)
-    {
-        /* Make sure the key is valid. */
-        $key_info = $this->pgpPacketInformation($public_key);
-        if (!isset($key_info['signature'])) {
-            return PEAR::raiseError(_("Not a valid public key."), 'horde.error');
-        }
-
-        /* Remove the '_SIGNATURE' entry. */
-        unset($key_info['signature']['_SIGNATURE']);
-
-        /* Store all signatures that appear in the key. */
-        foreach ($key_info['signature'] as $id => $sig) {
-            /* Check to make sure the key does not already exist in ANY
-             * address book and remove the id from the key_info for a correct
-             * output. */
-            $result = $this->getPublicKey($sig['email'], null, false);
-            if (!is_a($result, 'PEAR_Error') && !empty($result)) {
-                unset($key_info['signature'][$id]);
-                continue;
-            }
-
-            /* Add key to the user's address book. */
-            $result = $GLOBALS['registry']->call('contacts/addField', array($sig['email'], $sig['name'], IMP_PGP_PUBKEY_FIELD, $public_key, $GLOBALS['prefs']->getValue('add_source')));
-            if (is_a($result, 'PEAR_Error')) {
-                return $result;
-            }
-        }
-
-        return $key_info;
-    }
-
-    /**
-     * Retrieves a public key by e-mail.
-     *
-     * First, the key will be attempted to be retrieved from a user's address
-     * book(s).
-     * Second, if unsuccessful, the key is attempted to be retrieved via a
-     * public PGP keyserver.
-     *
-     * @param string $address      The e-mail address to search by.
-     * @param string $fingerprint  The fingerprint of the user's key.
-     * @param boolean $server      Whether to check the publick key servers for
-     *                             the key.
-     *
-     * @return string  The PGP public key requested. Returns PEAR_Error object
-     *                 on error.
-     */
-    function getPublicKey($address, $fingerprint = null, $server = true)
-    {
-        $result = false;
-
-        /* If there is a cache driver configured, try to get the public key
-         * from the cache. */
-        if (!empty($GLOBALS['conf']['cache']['driver'])) {
-            $cache = &Horde_Cache::singleton($GLOBALS['conf']['cache']['driver'], Horde::getDriverConfig('cache', $GLOBALS['conf']['cache']['driver']));
-            if (is_a($cache, 'PEAR_Error')) {
-                Horde::fatal($cache, __FILE__, __LINE__);
-            } else {
-                $result = $cache->get("PGPpublicKey_" . $address . $fingerprint, 3600);
-            }
-        }
-
-        if ($result) {
-            Horde::logMessage('PGPpublicKey: ' . serialize($result), __FILE__, __LINE__, PEAR_LOG_DEBUG);
-            return $result;
-        }
-
-        /* Try retrieving by e-mail only first. */
-        $params = IMP_Compose::getAddressSearchParams();
-        $result = $GLOBALS['registry']->call('contacts/getField', array($address, IMP_PGP_PUBKEY_FIELD, $params['sources'], false, true));
-
-        /* TODO: Retrieve by ID. */
-
-        /* See if the address points to the user's public key. */
-        if (is_a($result, 'PEAR_Error')) {
-            require_once 'Horde/Identity.php';
-            $identity = &Identity::singleton(array('imp', 'imp'));
-            $personal_pubkey = $this->getPersonalPublicKey();
-            if (!empty($personal_pubkey) && $identity->hasAddress($address)) {
-                $result = $personal_pubkey;
-            }
-        }
-
-        /* Try retrieving via a PGP public keyserver. */
-        if ($server && is_a($result, 'PEAR_Error')) {
-            $result = $this->getFromPublicKeyserver($fingerprint, $address);
-        }
-
-        /* Return now, if no public key found at all. */
-        if (is_a($result, 'PEAR_Error')) {
-            return $result;
-        }
-
-        /* If more than one public key is returned, just return the first in
-         * the array. There is no way of knowing which is the "preferred" key,
-         * if the keys are different. */
-        if (is_array($result)) {
-            reset($result);
-        }
-
-        /* If there is a cache driver configured and a cache object exists,
-         * store the public key in the cache. */
-        if (is_object($cache)) {
-            $cache->set("PGPpublicKey_" . $address . $fingerprint, $result, 3600);
-        }
-
-        return $result;
-    }
-
-    /**
-     * Retrieves all public keys from a user's address book(s).
-     *
-     * @return array  All PGP public keys available. Returns PEAR_Error object
-     *                on error.
-     */
-    function listPublicKeys()
-    {
-        $params = IMP_Compose::getAddressSearchParams();
-        return (empty($params['sources'])) ? array() : $GLOBALS['registry']->call('contacts/getAllAttributeValues', array(IMP_PGP_PUBKEY_FIELD, $params['sources']));
-    }
-
-    /**
-     * Deletes a public key from a user's address book(s) by e-mail.
-     *
-     * @param string $email  The e-mail address to delete.
-     *
-     * @return PEAR_Error  Returns PEAR_Error object on error.
-     */
-    function deletePublicKey($email)
-    {
-        $params = IMP_Compose::getAddressSearchParams();
-        return $GLOBALS['registry']->call('contacts/deleteField', array($email, IMP_PGP_PUBKEY_FIELD, $params['sources']));
-    }
-
-    /**
-     * Parse a message into its PGP components.
-     *
-     * @param string $text  See Horde_Crypt_pgp::parsePGPData()
-     *
-     * @return array  Returns an array of Horde_Mime_Part objects.
-     *                If there was no PGP data, returns false.
-     */
-    function &parseMessage($text)
-    {
-        $result = $this->parsePGPData($text);
-        if (empty($result) ||
-            ((count($result) == 1) && ($result[0]['type'] == PGP_ARMOR_TEXT))) {
-            $result = false;
-            return $result;
-        }
-
-        $return_array = array();
-
-        reset($result);
-        do {
-            $block = current($result);
-            $temp_part = new Horde_Mime_Part();
-            $temp_part->setContents(implode("\n", $block['data']));
-
-            /* Since private keys should NEVER be sent across email (in fact,
-               there is no MIME type to handle them) we will render them, if
-               someone is foolish enough to send one, in simple text. */
-            if (($block['type'] == PGP_ARMOR_TEXT) ||
-                ($block['type'] == PGP_ARMOR_PRIVATE_KEY)) {
-                $temp_part->setType('text/plain');
-            } elseif ($block['type'] == PGP_ARMOR_PUBLIC_KEY) {
-                $temp_part->setType('application/pgp-keys');
-            } elseif ($block['type'] == PGP_ARMOR_MESSAGE) {
-                $temp_part->setType('application/pgp-encrypted');
-            } elseif ($block['type'] == PGP_ARMOR_SIGNED_MESSAGE) {
-                $temp_part->setType('application/pgp-signature');
-                if (($block = next($result))) {
-                    if (!empty($block) && ($block['type'] == PGP_ARMOR_SIGNATURE)) {
-                        $temp_part->appendContents("\n" . implode("\n", $block['data']));
-                    }
-                }
-            } elseif ($block['type'] == PGP_ARMOR_SIGNATURE) {
-                continue;
-            }
-
-            $return_array[] = $temp_part;
-        } while (next($result));
-
-        return $return_array;
-    }
-
-    /**
-     * Renders a text message with PGP components.
-     *
-     * @param Horde_Mime_Part &$part    The part containing the data to
-     *                                  render.
-     * @param IMP_Contents &$contents  The IMP_Contents:: module to use to
-     *                                  output the text.
-     *
-     * @return string  Returns the rendered text.
-     *                 If there was no PGP data, returns false.
-     */
-    function parseMessageOutput(&$part, &$contents)
-    {
-        if (!($parts = &$this->parseMessage($part->getContents()))) {
-            return false;
-        }
-
-        $text = '';
-
-        $base_ob = &$contents->getBaseObjectPtr();
-        $addr = $base_ob->getFromAddress();
-
-        $message = new Horde_Mime_Message();
-        foreach ($parts as $val) {
-            $message->addPart($val);
-        }
-
-        $mc = new IMP_Contents($message, array('download' => 'download_attach', 'view' => 'view_attach'), array(&$contents));
-        $message->buildMessage();
-
-        foreach ($message->getParts() as $val) {
-            /* If the part appears to be nothing but empty space, don't
-               display it. */
-            if (($val->getBytes() < 5) &&
-                !(rtrim($val->getContents()))) {
-                continue;
-            }
-            $v = &$mc->getMIMEViewer($val);
-            if (!is_a($v, 'IMP_Horde_Mime_Viewer_pgp')) {
-                $text .= $mc->formatStatusMsg(_("The message below has not been digitally signed or encrypted with PGP."), Horde::img('alerts/warning.png', _("Warning"), '', $GLOBALS['registry']->getImageDir('horde')));
-            }
-            $text .= $mc->renderMIMEPart($val);
-        }
-
-        return $text;
-    }
-
-    /**
-     * Returns the signed data only for a plaintext signed Horde_Mime_Part.
-     *
-     * @param Horde_Mime_Part $mime_part  The object with a plaintext PGP
-     *                                    signed message in the contents.
-     *
-     * @return string  The contents of the signed message.
-     */
-    function getSignedMessage(&$mime_part)
-    {
-        $msg = '';
-
-        /* Just output signed data - remove all PGP headers. */
-        $result = $this->parsePGPData($mime_part->getContents());
-        foreach ($result as $block) {
-            if ($block['type'] == PGP_ARMOR_SIGNED_MESSAGE) {
-                $headerSeen = false;
-                $headerDone = false;
-                foreach ($block['data'] as $line) {
-                    if ($headerDone) {
-                        $msg .= $line . "\n";
-                    } elseif (strpos($line, "-----") === 0) {
-                        $headerSeen = true;
-                        continue;
-                    } elseif ($headerSeen) {
-                        /* There are some versions of GnuPG (like Version:
-                           GnuPG v1.2.1 (MingW32)) which separate headers from
-                           content with a line containing a blank, but this
-                           isn't RFC conforming, so this isn't handled.
-                           It results in a good signature with an empty
-                           message.
-                           The wrong code would be:
-                           elseif (empty($line) || (strcmp($line, ' ') == 0))
-                         */
-                        $line = trim($line);
-                        if (empty($line)) {
-                            $headerDone = true;
-                        }
-                    }
-                }
-            }
-        }
-
-        return rtrim($msg);
-    }
-
-    /**
-     * Get a public key via a public PGP keyserver.
-     *
-     * @param string $fingerprint  The fingerprint of the requested key.
-     * @param string $address      The email address of the requested key.
-     *
-     * @return string  See Horde_Crypt_pgp::getPublicKeyserver()
-     */
-    function getFromPublicKeyserver($fingerprint, $address = null)
-    {
-        return $this->_keyserverConnect($fingerprint, 'get', $address);
-    }
-
-    /**
-     * Send a public key to a public PGP keyserver.
-     *
-     * @param string $pubkey  The PGP public key.
-     *
-     * @return string  See Horde_Crypt_pgp::putPublicKeyserver()
-     */
-    function sendToPublicKeyserver($pubkey)
-    {
-        return $this->_keyserverConnect($pubkey, 'put');
-    }
-
-    /**
-     * Connect to the keyservers
-     *
-     * @access private
-     *
-     * @param string $data        The data to send to the keyserver.
-     * @param string $method      The method to use - either 'get' or 'put'.
-     * @param string $additional  Any additional data.
-     *
-     * @return string  See Horde_Crypt_pgp::getPublicKeyserver()  -or-
-     *                     Horde_Crypt_pgp::putPublicKeyserver().
-     */
-    function _keyserverConnect($data, $method, $additional = null)
-    {
-        global $conf;
-
-        if (!empty($conf['utils']['gnupg_keyserver'])) {
-            $timeout = (empty($conf['utils']['gnupg_timeout'])) ? PGP_KEYSERVER_TIMEOUT : $conf['utils']['gnupg_timeout'];
-            if ($method == 'get') {
-                foreach ($conf['utils']['gnupg_keyserver'] as $server) {
-                    $result = $this->getPublicKeyserver($data, $server, $timeout, $additional);
-                    if (!is_a($result, 'PEAR_Error')) {
-                        return $result;
-                    }
-                }
-                return $result;
-            } else {
-                return $this->putPublicKeyserver($data, $conf['utils']['gnupg_keyserver'][0], $timeout);
-            }
-        } else {
-            return PEAR::raiseError(_("Public PGP keyserver support has been disabled."), 'horde.warning');
-        }
-    }
-
-    /**
-     * Verifies a signed message with a given public key.
-     *
-     * @param string $text       The text to verify.
-     * @param string $address    E-mail address of public key.
-     * @param string $signature  A PGP signature block.
-     *
-     * @return string  See Horde_Crypt_pgp::decryptSignature()  -or-
-     *                     Horde_Crypt_pgp::decryptDetachedSignature().
-     */
-    function verifySignature($text, $address, $signature = '')
-    {
-        $fingerprint = null;
-
-        /* Get fingerprint of key. */
-        if (!empty($signature)) {
-            $packet_info = $this->pgpPacketInformation($signature);
-            if (isset($packet_info['fingerprint'])) {
-                $fingerprint = $packet_info['fingerprint'];
-            }
-        } else {
-            $fingerprint = $this->getSignersKeyID($text);
-        }
-
-        $public_key = $this->getPublicKey($address, $fingerprint);
-        if (is_a($public_key, 'PEAR_Error')) {
-            return $public_key;
-        }
-
-        if (!empty($signature)) {
-            $options = array('type' => 'detached-signature', 'signature' => $signature);
-        } else {
-            $options = array('type' => 'signature');
-        }
-        $options['pubkey'] = $public_key;
-
-        /* decrypt() returns a PEAR_Error object on error. */
-        return $this->decrypt($text, $options);
-    }
-
-    /**
-     * Decrypt a message with user's public/private keypair or a passphrase.
-     *
-     * @param string $text             The text to decrypt.
-     * @param boolean $symmetric_hint  Whether the text has been encrypted
-     *                                 symmetrically. If null, we try to find
-     *                                 out.
-     * @param boolean $passphrase      Whether a passphrase has to be used.
-     *
-     * @return string  The decrypted message. Returns PEAR_Error object on
-     *                 error.
-     */
-    function decryptMessage($text, $symmetric_hint = null, $passphrase = true)
-    {
-        if (is_null($symmetric_hint)) {
-            $symmetric_hint = $this->encryptedSymmetrically($text);
-        }
-
-        /* decrypt() returns a PEAR_Error object on error. */
-        if (!$passphrase) {
-            return $this->decrypt($text, array('type' => 'message', 'no_passphrase' => true));
-        } elseif ($symmetric_hint) {
-            return $this->decrypt($text, array('type' => 'message', 'passphrase' => $this->getSymmetricPassphrase()));
-        } else {
-            return $this->decrypt($text, array('type' => 'message', 'pubkey' => $this->getPersonalPublicKey(), 'privkey' => $this->getPersonalPrivateKey(), 'passphrase' => $this->getPassphrase()));
-        }
-    }
-
-    /**
-     * Gets the user's passphrase from the session cache.
-     *
-     * @return string  The passphrase, if set.
-     */
-    function getPassphrase()
-    {
-        if (isset($_SESSION['imp']['pgp_passphrase'])) {
-            return Secret::read(IMP::getAuthKey(), $_SESSION['imp']['pgp_passphrase']);
-        }
-    }
-
-    /**
-     * Gets the user's passphrase for symmetric encryption from the session
-     * cache.
-     *
-     * @return string  The passphrase, if set.
-     */
-    function getSymmetricPassphrase()
-    {
-        if (isset($_SESSION['imp']['pgp_sym_passphrase'])) {
-            return Secret::read(IMP::getAuthKey(), $_SESSION['imp']['pgp_sym_passphrase']);
-        }
-    }
-
-    /**
-     * Store's the user's passphrase in the session cache.
-     *
-     * @param string $passphrase  The user's passphrase.
-     *
-     * @return boolean  Returns true if correct passphrase, false if incorrect.
-     */
-    function storePassphrase($passphrase)
-    {
-        if ($this->verifyPassphrase($this->getPersonalPublicKey(), $this->getPersonalPrivateKey(), $passphrase) === false) {
-            return false;
-        }
-
-        $_SESSION['imp']['pgp_passphrase'] = Secret::write(IMP::getAuthKey(), $passphrase);
-        return true;
-    }
-
-    /**
-     * Store's the user's passphrase for symmetric encryption in the session
-     * cache.
-     *
-     * @param string $passphrase  The user's passphrase.
-     *
-     * @return boolean  True
-     */
-    function storeSymmetricPassphrase($passphrase)
-    {
-        $_SESSION['imp']['pgp_sym_passphrase'] = Secret::write(IMP::getAuthKey(), $passphrase);
-        return true;
-    }
-
-    /**
-     * Clear the passphrase from the session cache.
-     */
-    function unsetPassphrase()
-    {
-        unset($_SESSION['imp']['pgp_passphrase']);
-    }
-
-    /**
-     * Clear the passphrase for symmetric encryption from the session cache.
-     */
-    function unsetSymmetricPassphrase()
-    {
-        unset($_SESSION['imp']['pgp_sym_passphrase']);
-    }
-
-    /**
-     * Generates the javascript code for saving public keys.
-     *
-     * @param Horde_Mime_Part &$mime_part  The part containing the public key.
-     * @param string $cache                The part identifier.
-     *
-     * @return string  The URL for saving public keys.
-     */
-    function savePublicKeyURL(&$mime_part, $cache = null)
-    {
-        if (empty($cache)) {
-            require_once 'Horde/SessionObjects.php';
-            $cacheSess = &Horde_SessionObjects::singleton();
-            $oid = $cacheSess->storeOid($mime_part);
-        }
-
-        return $this->getJSOpenWinCode('save_attachment_public_key', false, array('mimecache' => $oid));
-    }
-
-    /**
-     * Print out the link for the javascript PGP popup.
-     *
-     * @param string $actionid  The ActionID to perform.
-     * @param mixed $reload     If true, reload base window on close. If text,
-     *                          run this JS on close. If false, don't do
-     *                          anything on close.
-     * @param array $params     Additional parameters needed for the reload
-     *                          page.
-     *
-     * @return string  The javascript link.
-     */
-    function getJSOpenWinCode($actionid, $reload = true, $params = array())
-    {
-        $params['actionID'] = $actionid;
-        if (!empty($reload)) {
-            if (is_bool($reload)) {
-                $params['reload'] = html_entity_decode(Util::removeParameter(Horde::selfUrl(true), array('actionID')));
-            } else {
-                require_once 'Horde/SessionObjects.php';
-                $cacheSess = &Horde_SessionObjects::singleton();
-                $params['passphrase_action'] = $cacheSess->storeOid($reload, false);
-            }
-        }
-
-        return IMP::popupIMPString('pgp.php', $params, 450, 200);
-    }
-
-    /**
-     * Provide the list of parameters needed for signing a message.
-     *
-     * @access private
-     *
-     * @return array  The list of parameters needed by encrypt().
-     */
-    function _signParameters()
-    {
-        return array('pubkey' => $this->getPersonalPublicKey(), 'privkey' => $this->getPersonalPrivateKey(), 'passphrase' => $this->getPassphrase());
-    }
-
-    /**
-     * Provide the list of parameters needed for encrypting a message.
-     *
-     * @access private
-     *
-     * @param array $addresses    The e-mail address of the keys to use for
-     *                            encryption.
-     * @param boolean $symmetric  Whether to encrypt symmetrically.
-     *
-     * @return array  The list of parameters needed by encrypt().
-     *                Returns PEAR_Error on error.
-     */
-    function _encryptParameters($addresses, $symmetric = false)
-    {
-        if ($symmetric) {
-            return array('symmetric' => true,
-                         'passphrase' => $this->getSymmetricPassphrase());
-        }
-
-        $addr_list = array();
-
-        foreach ($addresses as $val) {
-            $addrOb = Horde_Mime_Address::bareAddress($val, $_SESSION['imp']['maildomain'], true);
-            $key_addr = array_pop($addrOb);
-
-            /* Get the public key for the address. */
-            $public_key = $this->getPublicKey($key_addr);
-            if (is_a($public_key, 'PEAR_Error')) {
-                return $public_key;
-            }
-            $addr_list[$key_addr] = $public_key;
-        }
-
-        return array('recips' => $addr_list);
-    }
-
-    /**
-     * Sign a Horde_Mime_Part using PGP using IMP default parameters.
-     *
-     * @param Horde_Mime_Part $mime_part  The object to sign.
-     *
-     * @return Horde_Mime_Part  See Horde_Crypt_pgp::signMIMEPart(). Returns
-     *                          PEAR_Error object on error.
-     */
-    function IMPsignMIMEPart($mime_part)
-    {
-        return $this->signMIMEPart($mime_part, $this->_signParameters());
-    }
-
-    /**
-     * Encrypt a Horde_Mime_Part using PGP using IMP default parameters.
-     *
-     * @param Horde_Mime_Part $mime_part  The object to encrypt.
-     * @param array $addresses      The e-mail address of the keys to use for
-     *                              encryption.
-     * @param boolean $symmetric    Whether to encrypt symmetrically.
-     *
-     * @return Horde_Mime_Part  See Horde_Crypt_pgp::encryptMIMEPart(). Returns
-     *                    PEAR_Error object on error.
-     */
-    function IMPencryptMIMEPart($mime_part, $addresses, $symmetric = false)
-    {
-        $params = $this->_encryptParameters($addresses, $symmetric);
-        if (is_a($params, 'PEAR_Error')) {
-            return $params;
-        }
-        return $this->encryptMIMEPart($mime_part, $params);
-    }
-
-    /**
-     * Sign and Encrypt a Horde_Mime_Part using PGP using IMP default parameters.
-     *
-     * @param Horde_Mime_Part $mime_part  The object to sign and encrypt.
-     * @param array $addresses      The e-mail address of the keys to use for
-     *                              encryption.
-     * @param boolean $symmetric    Whether to encrypt symmetrically.
-     *
-     * @return Horde_Mime_Part  See Horde_Crypt_pgp::signAndencryptMIMEPart().
-     *                    Returns PEAR_Error object on error.
-     */
-    function IMPsignAndEncryptMIMEPart($mime_part, $addresses,
-                                       $symmetric = false)
-    {
-        $encrypt_params = $this->_encryptParameters($addresses, $symmetric);
-        if (is_a($encrypt_params, 'PEAR_Error')) {
-            return $encrypt_params;
-        }
-        return $this->signAndEncryptMIMEPart($mime_part, $this->_signParameters(), $encrypt_params);
-    }
-
-    /**
-     * Generate a Horde_Mime_Part object, in accordance with RFC 2015/3156, that
-     * contains the user's public key.
-     *
-     * @return Horde_Mime_Part  See Horde_Crypt_pgp::publicKeyMIMEPart().
-     */
-    function publicKeyMIMEPart()
-    {
-        return parent::publicKeyMIMEPart($this->getPersonalPublicKey());
-    }
-
-}
diff --git a/imp/lib/Crypt/SMIME.php b/imp/lib/Crypt/SMIME.php
deleted file mode 100644 (file)
index 7c2494b..0000000
+++ /dev/null
@@ -1,473 +0,0 @@
-<?php
-
-require_once 'Horde/Crypt/smime.php';
-
-/**
- * Name of the S/MIME public key field in addressbook.
- */
-define('IMP_SMIME_PUBKEY_FIELD', 'smimePublicKey');
-
-/**
- * The IMP_SMIME:: class contains all functions related to handling
- * S/MIME messages within IMP.
- *
- * Copyright 2002-2008 The Horde Project (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (GPL). If you
- * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
- *
- * @author  Mike Cochrane <mike@graftonhall.co.nz>
- * @package IMP
- */
-class IMP_SMIME extends Horde_Crypt_smime {
-
-    /**
-     * Constructor.
-     */
-    function IMP_SMIME()
-    {
-        parent::Horde_Crypt_smime(array('temp' => Horde::getTempDir()));
-    }
-
-    /**
-     * Add the personal public key to the prefs.
-     *
-     * @param mixed $key  The public key to add (either string or array).
-     */
-    function addPersonalPublicKey($key)
-    {
-        $GLOBALS['prefs']->setValue('smime_public_key', (is_array($key)) ? implode('', $key) : $key);
-    }
-
-    /**
-     * Add the personal private key to the prefs.
-     *
-     * @param mixed $key  The private key to add (either string or array).
-     */
-    function addPersonalPrivateKey($key)
-    {
-        $GLOBALS['prefs']->setValue('smime_private_key', (is_array($key)) ? implode('', $key) : $key);
-    }
-
-    /**
-     * Add the list of additional certs to the prefs.
-     *
-     * @param mixed $key  The private key to add (either string or array).
-     */
-    function addAdditionalCert($key)
-    {
-        $GLOBALS['prefs']->setValue('smime_additional_cert', (is_array($key)) ? implode('', $key) : $key);
-    }
-
-    /**
-     * Get the personal public key from the prefs.
-     *
-     * @return string  The personal S/MIME public key.
-     */
-    function getPersonalPublicKey()
-    {
-        return $GLOBALS['prefs']->getValue('smime_public_key');
-    }
-
-    /**
-     * Get the personal private key from the prefs.
-     *
-     * @return string  The personal S/MIME private key.
-     */
-    function getPersonalPrivateKey()
-    {
-        return $GLOBALS['prefs']->getValue('smime_private_key');
-    }
-
-    /**
-     * Get any additional certificates from the prefs.
-     *
-     * @return string  Additional signing certs for inclusion.
-     */
-    function getAdditionalCert()
-    {
-        return $GLOBALS['prefs']->getValue('smime_additional_cert');
-    }
-
-    /**
-     * Deletes the specified personal keys from the prefs.
-     */
-    function deletePersonalKeys()
-    {
-        $GLOBALS['prefs']->setValue('smime_public_key', '');
-        $GLOBALS['prefs']->setValue('smime_private_key', '');
-        $GLOBALS['prefs']->setValue('smime_additional_cert', '');
-        $this->unsetPassphrase();
-    }
-
-    /**
-     * Add a public key to an address book.
-     *
-     * @param string $cert  A public certificate to add.
-     *
-     * @return boolean  True on successful add.
-     *                  Returns PEAR_Error or error.
-     */
-    function addPublicKey($cert)
-    {
-        /* Make sure the certificate is valid. */
-        $key_info = openssl_x509_parse($cert);
-        if (!is_array($key_info) || !isset($key_info['subject'])) {
-            return PEAR::raiseError(_("Not a valid public key."), 'horde.error');
-        }
-
-        /* Add key to the user's address book. */
-        $email = $this->getEmailFromKey($cert);
-        if ($email === null) {
-            return PEAR::raiseError(_("No email information located in the public key."), 'horde.error');
-        }
-
-        /* Get the name corresponding to this key. */
-        if (isset($key_info['subject']['CN'])) {
-            $name = $key_info['subject']['CN'];
-        } elseif (isset($key_info['subject']['OU'])) {
-            $name = $key_info['subject']['OU'];
-        } else {
-            return PEAR::raiseError(_("Not a valid public key."), 'horde.error');
-        }
-
-        $res = $GLOBALS['registry']->call('contacts/addField', array($email, $name, IMP_SMIME_PUBKEY_FIELD, $cert, $GLOBALS['prefs']->getValue('add_source')));
-        if (is_a($res, 'PEAR_Error')) {
-            return $res;
-        }
-
-        return $key_info;
-    }
-
-    /**
-     * Returns the params needed to encrypt a message being sent to the
-     * specified email address.
-     *
-     * @param string $address  The e-mail address of the recipient.
-     *
-     * @return array  The list of parameters needed by encrypt().
-     *                Returns PEAR_Error object on error.
-     */
-    function _encryptParameters($address)
-    {
-        /* We can only encrypt if we are sending to a single person. */
-        $addrOb = Horde_Mime_Address::bareAddress($address, $_SESSION['imp']['maildomain'], true);
-        $key_addr = array_pop($addrOb);
-
-        $public_key = $this->getPublicKey($key_addr);
-        if (is_a($public_key, 'PEAR_Error')) {
-            return $public_key;
-        }
-
-        return array('type' => 'message', 'pubkey' => $public_key, 'email'  => $address);
-    }
-
-    /**
-     * Retrieves a public key by e-mail.
-     * The key will be retrieved from a user's address book(s).
-     *
-     * @param string $address  The e-mail address to search for.
-     *
-     * @return string  The S/MIME public key requested.
-     *                 Returns PEAR_Error object on error.
-     */
-    function getPublicKey($address)
-    {
-        $params = IMP_Compose::getAddressSearchParams();
-        $key = $GLOBALS['registry']->call('contacts/getField', array($address, IMP_SMIME_PUBKEY_FIELD, $params['sources'], false, true));
-
-        /* See if the address points to the user's public key. */
-        if (is_a($key, 'PEAR_Error')) {
-            require_once 'Horde/Identity.php';
-            $identity = &Identity::singleton(array('imp', 'imp'));
-            $personal_pubkey = $this->getPersonalPublicKey();
-            if (!empty($personal_pubkey) && $identity->hasAddress($address)) {
-                return $personal_pubkey;
-            }
-        }
-
-        /* If more than one public key is returned, just return the first in
-         * the array. There is no way of knowing which is the "preferred" key,
-         * if the keys are different. */
-        if (is_array($key)) {
-            return reset($key);
-        }
-
-        return $key;
-    }
-
-    /**
-     * Retrieves all public keys from a user's address book(s).
-     *
-     * @return array  All PGP public keys available.
-     *                Returns PEAR_Error object on error.
-     */
-    function listPublicKeys()
-    {
-        $params = IMP_Compose::getAddressSearchParams();
-        return (empty($params['sources'])) ? array() : $GLOBALS['registry']->call('contacts/getAllAttributeValues', array(IMP_SMIME_PUBKEY_FIELD, $params['sources']));
-    }
-
-    /**
-     * Deletes a public key from a user's address book(s) by e-mail.
-     *
-     * @param string $email  The e-mail address to delete.
-     *
-     * @return PEAR_Error  Returns PEAR_Error object on error.
-     */
-    function deletePublicKey($email)
-    {
-        $params = IMP_Compose::getAddressSearchParams();
-        return $GLOBALS['registry']->call('contacts/deleteField', array($email, IMP_SMIME_PUBKEY_FIELD, $params['sources']));
-    }
-
-    /**
-     * Returns the parameters needed for signing a message.
-     *
-     * @access private
-     *
-     * @return array  The list of parameters needed by encrypt().
-     */
-    function _signParameters()
-    {
-        return array(
-            'type' => 'signature',
-            'pubkey' => $this->getPersonalPublicKey(),
-            'privkey' => $this->getPersonalPrivateKey(),
-            'passphrase' => $this->getPassphrase(),
-            'sigtype' => 'detach',
-            'certs' => $this->getAdditionalCert()
-        );
-    }
-
-    /**
-     * Verifies a signed message with a given public key.
-     *
-     * @param string $text  The text to verify.
-     *
-     * @return stdClass  See Horde_Crypt_smime::verify().
-     */
-    function verifySignature($text)
-    {
-        return $this->verify($text, empty($GLOBALS['conf']['utils']['openssl_cafile']) ? array() : $GLOBALS['conf']['utils']['openssl_cafile']);
-    }
-
-
-    /**
-     * Decrypt a message with user's public/private keypair.
-     *
-     * @param string $text  The text to decrypt.
-     *
-     * @return string  See Horde_Crypt_smime::decrypt().
-     *                 Returns PEAR_Error object on error.
-     */
-    function decryptMessage($text)
-    {
-        /* decrypt() returns a PEAR_Error object on error. */
-        return $this->decrypt($text, array('type' => 'message', 'pubkey' => $this->getPersonalPublicKey(), 'privkey' => $this->getPersonalPrivateKey(), 'passphrase' => $this->getPassphrase()));
-    }
-
-    /**
-     * Gets the user's passphrase from the session cache.
-     *
-     * @return mixed  The passphrase, if set.  Returns false if the passphrase
-     *                has not been loaded yet.  Returns null if no passphrase
-     *                is needed.
-     */
-    function getPassphrase()
-    {
-        $private_key = $GLOBALS['prefs']->getValue('smime_private_key');
-        if (empty($private_key)) {
-            return false;
-        }
-
-        if (isset($_SESSION['imp']['smime']['passphrase'])) {
-            return Secret::read(IMP::getAuthKey(), $_SESSION['imp']['smime']['passphrase']);
-        } elseif (isset($_SESSION['imp']['smime']['null_passphrase'])) {
-            return ($_SESSION['imp']['smime']['null_passphrase']) ? null : false;
-        } else {
-            $res = $this->verifyPassphrase($private_key, null);
-            if (!isset($_SESSION['imp']['smime'])) {
-                $_SESSION['imp']['smime'] = array();
-            }
-            $_SESSION['imp']['smime']['null_passphrase'] = ($res) ? null : false;
-            return $_SESSION['imp']['smime']['null_passphrase'];
-        }
-    }
-
-    /**
-     * Store's the user's passphrase in the session cache.
-     *
-     * @param string $passphrase  The user's passphrase.
-     *
-     * @return boolean  Returns true if correct passphrase, false if incorrect.
-     */
-    function storePassphrase($passphrase)
-    {
-        if ($this->verifyPassphrase($this->getPersonalPrivateKey(), $passphrase) === false) {
-            return false;
-        }
-
-        if (!isset($_SESSION['imp']['smime'])) {
-            $_SESSION['imp']['smime'] = array();
-        }
-        $_SESSION['imp']['smime']['passphrase'] = Secret::write(IMP::getAuthKey(), $passphrase);
-
-        return true;
-    }
-
-    /**
-     * Clear the passphrase from the session cache.
-     */
-    function unsetPassphrase()
-    {
-        unset($_SESSION['imp']['smime']['null_passphrase']);
-        unset($_SESSION['imp']['smime']['passphrase']);
-    }
-
-    /**
-     * Generates the javascript code for saving public keys.
-     *
-     * @param MIME_Part $mime_part  The MIME_Part containing the public key.
-     *
-     * @return string  The URL for saving public keys.
-     */
-    function savePublicKeyURL($mime_part)
-    {
-        if (empty($cache)) {
-            require_once 'Horde/SessionObjects.php';
-            $cacheSess = &Horde_SessionObjects::singleton();
-            $oid = $cacheSess->storeOid($mime_part);
-        }
-
-        return $this->getJSOpenWinCode('save_attachment_public_key', false, array('cert' => $oid));
-    }
-
-    /**
-     * Print out the link for the javascript S/MIME popup.
-     *
-     * @param integer $actionid  The actionID to perform.
-     * @param mixed $reload      If true, reload base window on close. If text,
-     *                           run this JS on close. If false, don't do
-     *                           anything on close.
-     * @param array $params      Additional parameters needed for the reload
-     *                           page.
-     *
-     * @return string  The javascript link.
-     */
-    function getJSOpenWinCode($actionid, $reload = true, $params = array())
-    {
-        $params['actionID'] = $actionid;
-        if (!empty($reload)) {
-            if (is_bool($reload)) {
-                $params['reload'] = html_entity_decode(Util::removeParameter(Horde::selfUrl(true), array('actionID')));
-            } else {
-                require_once 'Horde/SessionObjects.php';
-                $cacheSess = &Horde_SessionObjects::singleton();
-                $params['passphrase_action'] = $cacheSess->storeOid($reload, false);
-            }
-        }
-
-        return IMP::popupIMPString('smime.php', $params, 450, 200);
-    }
-
-    /**
-     * Encrypt a MIME_Part using S/MIME using IMP defaults.
-     *
-     * @param MIME_Part $mime_part  The MIME_Part object to encrypt.
-     * @param mixed $to_address     The e-mail address of the key to use for
-     *                              encryption.
-     *
-     * @return MIME_Part  See Horde_Crypt_smime::encryptMIMEPart(). Returns
-     *                    PEAR_Error on error.
-     */
-    function IMPencryptMIMEPart($mime_part, $to_address)
-    {
-        $params = $this->_encryptParameters($to_address);
-        if (is_a($params, 'PEAR_Error')) {
-            return $params;
-        }
-        return $this->encryptMIMEPart($mime_part, $params);
-    }
-
-    /**
-     * Sign a MIME_Part using S/MIME using IMP defaults.
-     *
-     * @param MIME_Part $mime_part  The MIME_Part object to sign.
-     *
-     * @return MIME_Part  See Horde_Crypt_smime::signMIMEPart(). Returns
-     *                    PEAR_Error on error.
-     */
-    function IMPsignMIMEPart($mime_part)
-    {
-        return $this->signMIMEPart($mime_part, $this->_signParameters());
-    }
-
-    /**
-     * Sign and encrypt a MIME_Part using S/MIME using IMP defaults.
-     *
-     * @param MIME_Part $mime_part  The MIME_Part object to sign and encrypt.
-     * @param string $to_address    The e-mail address of the key to use for
-     *                              encryption.
-     *
-     * @return MIME_Part  See Horde_Crypt_smime::signAndencryptMIMEPart().
-     *                    Returns PEAR_Error on error.
-     */
-    function IMPsignAndEncryptMIMEPart($mime_part, $to_address)
-    {
-        $encrypt_params = $this->_encryptParameters($to_address);
-        if (is_a($encrypt_params, 'PEAR_Error')) {
-            return $encrypt_params;
-        }
-        return $this->signAndEncryptMIMEPart($mime_part, $this->_signParameters(), $encrypt_params);
-    }
-
-    /**
-     * Store the public/private/additional certificates in the preferences
-     * from a given PKCS 12 file.
-     *
-     * @param string $pkcs12    The PKCS 12 data.
-     * @param string $password  The password of the PKCS 12 file.
-     * @param string $pkpass    The password to use to encrypt the private key.
-     *
-     * @return boolean  True on success, PEAR_Error on error.
-     */
-    function addFromPKCS12($pkcs12, $password, $pkpass = null)
-    {
-        $openssl = IMP_SMIME::checkForOpenSSL();
-        if (is_a($openssl, 'PEAR_Error')) {
-            return $openssl;
-        }
-
-        $sslpath = (empty($GLOBALS['conf']['utils']['openssl_binary'])) ? null : $GLOBALS['conf']['utils']['openssl_binary'];
-        $params = array('sslpath' => $sslpath, 'password' => $password);
-        if (!empty($pkpass)) {
-            $params['newpassword'] = $pkpass;
-        }
-        $res = $this->parsePKCS12Data($pkcs12, $params);
-        if (is_a($res, 'PEAR_Error')) {
-            return $res;
-        }
-
-        $this->addPersonalPrivateKey($res->private);
-        $this->addPersonalPublicKey($res->public);
-        $this->addAdditionalCert($res->certs);
-
-        return true;
-    }
-
-    /**
-     * Extract the contents from signed S/MIME data.
-     *
-     * @param string $data  The signed S/MIME data.
-     *
-     * @return string  The contents embedded in the signed data.
-     *                 Returns PEAR_Error on error.
-     */
-    function extractSignedContents($data)
-    {
-        $sslpath = (empty($GLOBALS['conf']['utils']['openssl_binary'])) ? null : $GLOBALS['conf']['utils']['openssl_binary'];
-        return parent::extractSignedContents($data, $sslpath);
-    }
-
-}
diff --git a/imp/lib/Crypt/pgp.php b/imp/lib/Crypt/pgp.php
new file mode 100644 (file)
index 0000000..2b44829
--- /dev/null
@@ -0,0 +1,752 @@
+<?php
+/**
+ * The IMP_PGP:: class contains all functions related to handling
+ * PGP messages within IMP.
+ *
+ * Copyright 2002-2008 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author  Michael Slusarz <slusarz@horde.org>
+ * @package IMP
+ */
+class IMP_Horde_Crypt_pgp extends Horde_Crypt_pgp
+{
+    /* Name of PGP public key field in addressbook. */
+    const PUBKEY_FIELD = 'pgpPublicKey';
+
+    /**
+     * Constructor
+     */
+    function __construct()
+    {
+        parent::__construct(array(
+            'program' => $GLOBALS['conf']['utils']['gnupg'],
+            'temp' => Horde::getTempDir()
+        ));
+    }
+
+    /**
+     * Generate the personal Public/Private keypair and store in prefs.
+     *
+     * @param string $realname    See Horde_Crypt_pgp::
+     * @param string $email       See Horde_Crypt_pgp::
+     * @param string $passphrase  See Horde_Crypt_pgp::
+     * @param string $comment     See Horde_Crypt_pgp::
+     * @param string $keylength   See Horde_Crypt_pgp::
+     *
+     * @return PEAR_Error  Returns PEAR_Error object on error.
+     */
+    public function generatePersonalKeys($name, $email, $passphrase,
+                                         $comment = '', $keylength = 1024)
+    {
+        $keys = $this->generateKey($name, $email, $passphrase, $comment, $keylength);
+        if (is_a($keys, 'PEAR_Error')) {
+            return $keys;
+        }
+
+        /* Store the keys in the user's preferences. */
+        $this->addPersonalPublicKey($keys['public']);
+        $this->addPersonalPrivateKey($keys['private']);
+    }
+
+    /**
+     * Add the personal public key to the prefs.
+     *
+     * @param mixed $public_key  The public key to add (either string or
+     *                           array).
+     */
+    public function addPersonalPublicKey($public_key)
+    {
+        $GLOBALS['prefs']->setValue('pgp_public_key', (is_array($public_key)) ? implode('', $public_key) : $public_key);
+    }
+
+    /**
+     * Add the personal private key to the prefs.
+     *
+     * @param mixed $private_key  The private key to add (either string or
+     *                            array).
+     */
+    public function addPersonalPrivateKey($private_key)
+    {
+        $GLOBALS['prefs']->setValue('pgp_private_key', (is_array($private_key)) ? implode('', $private_key) : $private_key);
+    }
+
+    /**
+     * Get the personal public key from the prefs.
+     *
+     * @return string  The personal PGP public key.
+     */
+    public function getPersonalPublicKey()
+    {
+        return $GLOBALS['prefs']->getValue('pgp_public_key');
+    }
+
+    /**
+     * Get the personal private key from the prefs.
+     *
+     * @return string  The personal PGP private key.
+     */
+    public function getPersonalPrivateKey()
+    {
+        return $GLOBALS['prefs']->getValue('pgp_private_key');
+    }
+
+    /**
+     * Deletes the specified personal keys from the prefs.
+     */
+    public function deletePersonalKeys()
+    {
+        $GLOBALS['prefs']->setValue('pgp_public_key', '');
+        $GLOBALS['prefs']->setValue('pgp_private_key', '');
+
+        $this->unsetPassphrase();
+    }
+
+    /**
+     * Add a public key to an address book.
+     *
+     * @param string $public_key  An PGP public key.
+     *
+     * @return array  See Horde_Crypt_pgp::pgpPacketInformation()
+     *                Returns PEAR_Error or error.
+     */
+    public function addPublicKey($public_key)
+    {
+        /* Make sure the key is valid. */
+        $key_info = $this->pgpPacketInformation($public_key);
+        if (!isset($key_info['signature'])) {
+            return PEAR::raiseError(_("Not a valid public key."), 'horde.error');
+        }
+
+        /* Remove the '_SIGNATURE' entry. */
+        unset($key_info['signature']['_SIGNATURE']);
+
+        /* Store all signatures that appear in the key. */
+        foreach ($key_info['signature'] as $id => $sig) {
+            /* Check to make sure the key does not already exist in ANY
+             * address book and remove the id from the key_info for a correct
+             * output. */
+            $result = $this->getPublicKey($sig['email'], null, false);
+            if (!is_a($result, 'PEAR_Error') && !empty($result)) {
+                unset($key_info['signature'][$id]);
+                continue;
+            }
+
+            /* Add key to the user's address book. */
+            $result = $GLOBALS['registry']->call('contacts/addField', array($sig['email'], $sig['name'], self::PUBKEY_FIELD, $public_key, $GLOBALS['prefs']->getValue('add_source')));
+            if (is_a($result, 'PEAR_Error')) {
+                return $result;
+            }
+        }
+
+        return $key_info;
+    }
+
+    /**
+     * Retrieves a public key by e-mail.
+     *
+     * First, the key will be attempted to be retrieved from a user's address
+     * book(s).
+     * Second, if unsuccessful, the key is attempted to be retrieved via a
+     * public PGP keyserver.
+     *
+     * @param string $address      The e-mail address to search by.
+     * @param string $fingerprint  The fingerprint of the user's key.
+     * @param boolean $server      Whether to check the publick key servers for
+     *                             the key.
+     *
+     * @return string  The PGP public key requested. Returns PEAR_Error object
+     *                 on error.
+     */
+    public function getPublicKey($address, $fingerprint = null, $server = true)
+    {
+        $result = false;
+
+        /* If there is a cache driver configured, try to get the public key
+         * from the cache. */
+        if (!empty($GLOBALS['conf']['cache']['driver'])) {
+            $cache = &Horde_Cache::singleton($GLOBALS['conf']['cache']['driver'], Horde::getDriverConfig('cache', $GLOBALS['conf']['cache']['driver']));
+            if (is_a($cache, 'PEAR_Error')) {
+                Horde::fatal($cache, __FILE__, __LINE__);
+            } else {
+                $result = $cache->get("PGPpublicKey_" . $address . $fingerprint, 3600);
+            }
+        }
+
+        if ($result) {
+            Horde::logMessage('PGPpublicKey: ' . serialize($result), __FILE__, __LINE__, PEAR_LOG_DEBUG);
+            return $result;
+        }
+
+        /* Try retrieving by e-mail only first. */
+        $params = IMP_Compose::getAddressSearchParams();
+        $result = $GLOBALS['registry']->call('contacts/getField', array($address, self::PUBKEY_FIELD, $params['sources'], false, true));
+
+        /* TODO: Retrieve by ID. */
+
+        /* See if the address points to the user's public key. */
+        if (is_a($result, 'PEAR_Error')) {
+            require_once 'Horde/Identity.php';
+            $identity = &Identity::singleton(array('imp', 'imp'));
+            $personal_pubkey = $this->getPersonalPublicKey();
+            if (!empty($personal_pubkey) && $identity->hasAddress($address)) {
+                $result = $personal_pubkey;
+            }
+        }
+
+        /* Try retrieving via a PGP public keyserver. */
+        if ($server && is_a($result, 'PEAR_Error')) {
+            $result = $this->getFromPublicKeyserver($fingerprint, $address);
+        }
+
+        /* Return now, if no public key found at all. */
+        if (is_a($result, 'PEAR_Error')) {
+            return $result;
+        }
+
+        /* If more than one public key is returned, just return the first in
+         * the array. There is no way of knowing which is the "preferred" key,
+         * if the keys are different. */
+        if (is_array($result)) {
+            reset($result);
+        }
+
+        /* If there is a cache driver configured and a cache object exists,
+         * store the public key in the cache. */
+        if (is_object($cache)) {
+            $cache->set("PGPpublicKey_" . $address . $fingerprint, $result, 3600);
+        }
+
+        return $result;
+    }
+
+    /**
+     * Retrieves all public keys from a user's address book(s).
+     *
+     * @return array  All PGP public keys available. Returns PEAR_Error object
+     *                on error.
+     */
+    public function listPublicKeys()
+    {
+        $params = IMP_Compose::getAddressSearchParams();
+        return (empty($params['sources'])) ? array() : $GLOBALS['registry']->call('contacts/getAllAttributeValues', array(self::PUBKEY_FIELD, $params['sources']));
+    }
+
+    /**
+     * Deletes a public key from a user's address book(s) by e-mail.
+     *
+     * @param string $email  The e-mail address to delete.
+     *
+     * @return PEAR_Error  Returns PEAR_Error object on error.
+     */
+    public function deletePublicKey($email)
+    {
+        $params = IMP_Compose::getAddressSearchParams();
+        return $GLOBALS['registry']->call('contacts/deleteField', array($email, self::PUBKEY_FIELD, $params['sources']));
+    }
+
+    /**
+     * Parse a message into its PGP components.
+     *
+     * @param string $text  See Horde_Crypt_pgp::parsePGPData()
+     *
+     * @return array  Returns an array of Horde_Mime_Part objects.
+     *                If there was no PGP data, returns false.
+     */
+    public function &parseMessage($text)
+    {
+        $result = $this->parsePGPData($text);
+        if (empty($result) ||
+            ((count($result) == 1) && ($result[0]['type'] == self::ARMOR_TEXT))) {
+            $result = false;
+            return $result;
+        }
+
+        $return_array = array();
+
+        reset($result);
+        do {
+            $block = current($result);
+            $temp_part = new Horde_Mime_Part();
+            $temp_part->setContents(implode("\n", $block['data']));
+
+            /* Since private keys should NEVER be sent across email (in fact,
+               there is no MIME type to handle them) we will render them, if
+               someone is foolish enough to send one, in simple text. */
+            if (($block['type'] == self::ARMOR_TEXT) ||
+                ($block['type'] == self::ARMOR_PRIVATE_KEY)) {
+                $temp_part->setType('text/plain');
+            } elseif ($block['type'] == self::ARMOR_PUBLIC_KEY) {
+                $temp_part->setType('application/pgp-keys');
+            } elseif ($block['type'] == self::ARMOR_MESSAGE) {
+                $temp_part->setType('application/pgp-encrypted');
+            } elseif ($block['type'] == self::ARMOR_SIGNED_MESSAGE) {
+                $temp_part->setType('application/pgp-signature');
+                if (($block = next($result))) {
+                    if (!empty($block) && ($block['type'] == self::ARMOR_SIGNATURE)) {
+                        $temp_part->appendContents("\n" . implode("\n", $block['data']));
+                    }
+                }
+            } elseif ($block['type'] == self::ARMOR_SIGNATURE) {
+                continue;
+            }
+
+            $return_array[] = $temp_part;
+        } while (next($result));
+
+        return $return_array;
+    }
+
+    /**
+     * Renders a text message with PGP components.
+     *
+     * @param Horde_Mime_Part &$part    The part containing the data to
+     *                                  render.
+     * @param IMP_Contents &$contents  The IMP_Contents:: module to use to
+     *                                  output the text.
+     *
+     * @return string  Returns the rendered text.
+     *                 If there was no PGP data, returns false.
+     */
+    function parseMessageOutput(&$part, &$contents)
+    {
+        if (!($parts = &$this->parseMessage($part->getContents()))) {
+            return false;
+        }
+
+        $text = '';
+
+        // TODO
+        $base_ob = &$contents->getBaseObjectPtr();
+        $addr = $base_ob->getFromAddress();
+
+        $message = new Horde_Mime_Message();
+        foreach ($parts as $val) {
+            $message->addPart($val);
+        }
+
+        // TODO
+        $mc = new IMP_Contents($message, array('download' => 'download_attach', 'view' => 'view_attach'), array(&$contents));
+        $message->buildMessage();
+
+        foreach ($message->getParts() as $val) {
+            /* If the part appears to be nothing but empty space, don't
+               display it. */
+            if (($val->getBytes() < 5) &&
+                !(rtrim($val->getContents()))) {
+                continue;
+            }
+            $v = &$mc->getMIMEViewer($val);
+            if (!is_a($v, 'IMP_Horde_Mime_Viewer_pgp')) {
+                $text .= $mc->formatStatusMsg(_("The message below has not been digitally signed or encrypted with PGP."), Horde::img('alerts/warning.png', _("Warning"), '', $GLOBALS['registry']->getImageDir('horde')));
+            }
+            $text .= $mc->renderMIMEPart($val);
+        }
+
+        return $text;
+    }
+
+    /**
+     * Returns the signed data only for a plaintext signed Horde_Mime_Part.
+     *
+     * @param Horde_Mime_Part $mime_part  The object with a plaintext PGP
+     *                                    signed message in the contents.
+     *
+     * @return string  The contents of the signed message.
+     */
+    public function getSignedMessage(&$mime_part)
+    {
+        $msg = '';
+
+        /* Just output signed data - remove all PGP headers. */
+        $result = $this->parsePGPData($mime_part->getContents());
+        foreach ($result as $block) {
+            if ($block['type'] == self::ARMOR_SIGNED_MESSAGE) {
+                $headerSeen = false;
+                $headerDone = false;
+                foreach ($block['data'] as $line) {
+                    if ($headerDone) {
+                        $msg .= $line . "\n";
+                    } elseif (strpos($line, "-----") === 0) {
+                        $headerSeen = true;
+                        continue;
+                    } elseif ($headerSeen) {
+                        /* There are some versions of GnuPG (like Version:
+                           GnuPG v1.2.1 (MingW32)) which separate headers from
+                           content with a line containing a blank, but this
+                           isn't RFC conforming, so this isn't handled.
+                           It results in a good signature with an empty
+                           message.
+                           The wrong code would be:
+                           elseif (empty($line) || (strcmp($line, ' ') == 0))
+                         */
+                        $line = trim($line);
+                        if (empty($line)) {
+                            $headerDone = true;
+                        }
+                    }
+                }
+            }
+        }
+
+        return rtrim($msg);
+    }
+
+    /**
+     * Get a public key via a public PGP keyserver.
+     *
+     * @param string $fingerprint  The fingerprint of the requested key.
+     * @param string $address      The email address of the requested key.
+     *
+     * @return string  See Horde_Crypt_pgp::getPublicKeyserver()
+     */
+    public function getFromPublicKeyserver($fingerprint, $address = null)
+    {
+        return $this->_keyserverConnect($fingerprint, 'get', $address);
+    }
+
+    /**
+     * Send a public key to a public PGP keyserver.
+     *
+     * @param string $pubkey  The PGP public key.
+     *
+     * @return string  See Horde_Crypt_pgp::putPublicKeyserver()
+     */
+    public function sendToPublicKeyserver($pubkey)
+    {
+        return $this->_keyserverConnect($pubkey, 'put');
+    }
+
+    /**
+     * Connect to the keyservers
+     *
+     * @param string $data        The data to send to the keyserver.
+     * @param string $method      The method to use - either 'get' or 'put'.
+     * @param string $additional  Any additional data.
+     *
+     * @return string  See Horde_Crypt_pgp::getPublicKeyserver()  -or-
+     *                     Horde_Crypt_pgp::putPublicKeyserver().
+     */
+    protected function _keyserverConnect($data, $method, $additional = null)
+    {
+        global $conf;
+
+        if (!empty($conf['utils']['gnupg_keyserver'])) {
+            $timeout = (empty($conf['utils']['gnupg_timeout'])) ? PGP_KEYSERVER_TIMEOUT : $conf['utils']['gnupg_timeout'];
+            if ($method == 'get') {
+                foreach ($conf['utils']['gnupg_keyserver'] as $server) {
+                    $result = $this->getPublicKeyserver($data, $server, $timeout, $additional);
+                    if (!is_a($result, 'PEAR_Error')) {
+                        return $result;
+                    }
+                }
+                return $result;
+            } else {
+                return $this->putPublicKeyserver($data, $conf['utils']['gnupg_keyserver'][0], $timeout);
+            }
+        } else {
+            return PEAR::raiseError(_("Public PGP keyserver support has been disabled."), 'horde.warning');
+        }
+    }
+
+    /**
+     * Verifies a signed message with a given public key.
+     *
+     * @param string $text       The text to verify.
+     * @param string $address    E-mail address of public key.
+     * @param string $signature  A PGP signature block.
+     *
+     * @return string  See Horde_Crypt_pgp::decryptSignature()  -or-
+     *                     Horde_Crypt_pgp::decryptDetachedSignature().
+     */
+    public function verifySignature($text, $address, $signature = '')
+    {
+        $fingerprint = null;
+
+        /* Get fingerprint of key. */
+        if (!empty($signature)) {
+            $packet_info = $this->pgpPacketInformation($signature);
+            if (isset($packet_info['fingerprint'])) {
+                $fingerprint = $packet_info['fingerprint'];
+            }
+        } else {
+            $fingerprint = $this->getSignersKeyID($text);
+        }
+
+        $public_key = $this->getPublicKey($address, $fingerprint);
+        if (is_a($public_key, 'PEAR_Error')) {
+            return $public_key;
+        }
+
+        if (!empty($signature)) {
+            $options = array('type' => 'detached-signature', 'signature' => $signature);
+        } else {
+            $options = array('type' => 'signature');
+        }
+        $options['pubkey'] = $public_key;
+
+        /* decrypt() returns a PEAR_Error object on error. */
+        return $this->decrypt($text, $options);
+    }
+
+    /**
+     * Decrypt a message with user's public/private keypair or a passphrase.
+     *
+     * @param string $text             The text to decrypt.
+     * @param boolean $symmetric_hint  Whether the text has been encrypted
+     *                                 symmetrically. If null, we try to find
+     *                                 out.
+     * @param boolean $passphrase      Whether a passphrase has to be used.
+     *
+     * @return string  The decrypted message. Returns PEAR_Error object on
+     *                 error.
+     */
+    public function decryptMessage($text, $symmetric_hint = null,
+                                   $passphrase = true)
+    {
+        if (is_null($symmetric_hint)) {
+            $symmetric_hint = $this->encryptedSymmetrically($text);
+        }
+
+        /* decrypt() returns a PEAR_Error object on error. */
+        if (!$passphrase) {
+            return $this->decrypt($text, array('type' => 'message', 'no_passphrase' => true));
+        } elseif ($symmetric_hint) {
+            return $this->decrypt($text, array('type' => 'message', 'passphrase' => $this->getSymmetricPassphrase()));
+        } else {
+            return $this->decrypt($text, array('type' => 'message', 'pubkey' => $this->getPersonalPublicKey(), 'privkey' => $this->getPersonalPrivateKey(), 'passphrase' => $this->getPassphrase()));
+        }
+    }
+
+    /**
+     * Gets the user's passphrase from the session cache.
+     *
+     * @return string  The passphrase, if set.
+     */
+    public function getPassphrase()
+    {
+        if (isset($_SESSION['imp']['pgp_passphrase'])) {
+            return Secret::read(IMP::getAuthKey(), $_SESSION['imp']['pgp_passphrase']);
+        }
+    }
+
+    /**
+     * Gets the user's passphrase for symmetric encryption from the session
+     * cache.
+     *
+     * @return string  The passphrase, if set.
+     */
+    public function getSymmetricPassphrase()
+    {
+        if (isset($_SESSION['imp']['pgp_sym_passphrase'])) {
+            return Secret::read(IMP::getAuthKey(), $_SESSION['imp']['pgp_sym_passphrase']);
+        }
+    }
+
+    /**
+     * Store's the user's passphrase in the session cache.
+     *
+     * @param string $passphrase  The user's passphrase.
+     *
+     * @return boolean  Returns true if correct passphrase, false if incorrect.
+     */
+    public function storePassphrase($passphrase)
+    {
+        if ($this->verifyPassphrase($this->getPersonalPublicKey(), $this->getPersonalPrivateKey(), $passphrase) === false) {
+            return false;
+        }
+
+        $_SESSION['imp']['pgp_passphrase'] = Secret::write(IMP::getAuthKey(), $passphrase);
+        return true;
+    }
+
+    /**
+     * Store's the user's passphrase for symmetric encryption in the session
+     * cache.
+     *
+     * @param string $passphrase  The user's passphrase.
+     *
+     * @return boolean  True
+     */
+    public function storeSymmetricPassphrase($passphrase)
+    {
+        $_SESSION['imp']['pgp_sym_passphrase'] = Secret::write(IMP::getAuthKey(), $passphrase);
+        return true;
+    }
+
+    /**
+     * Clear the passphrase from the session cache.
+     */
+    public function unsetPassphrase()
+    {
+        unset($_SESSION['imp']['pgp_passphrase']);
+    }
+
+    /**
+     * Clear the passphrase for symmetric encryption from the session cache.
+     */
+    public function unsetSymmetricPassphrase()
+    {
+        unset($_SESSION['imp']['pgp_sym_passphrase']);
+    }
+
+    /**
+     * Generates the javascript code for saving public keys.
+     *
+     * @param Horde_Mime_Part &$mime_part  The part containing the public key.
+     * @param string $cache                The part identifier.
+     *
+     * @return string  The URL for saving public keys.
+     */
+    public function savePublicKeyURL(&$mime_part, $cache = null)
+    {
+        if (empty($cache)) {
+            require_once 'Horde/SessionObjects.php';
+            $cacheSess = &Horde_SessionObjects::singleton();
+            $oid = $cacheSess->storeOid($mime_part);
+        }
+
+        return $this->getJSOpenWinCode('save_attachment_public_key', false, array('mimecache' => $oid));
+    }
+
+    /**
+     * Print out the link for the javascript PGP popup.
+     *
+     * @param string $actionid  The ActionID to perform.
+     * @param mixed $reload     If true, reload base window on close. If text,
+     *                          run this JS on close. If false, don't do
+     *                          anything on close.
+     * @param array $params     Additional parameters needed for the reload
+     *                          page.
+     *
+     * @return string  The javascript link.
+     */
+    public function getJSOpenWinCode($actionid, $reload = true, $params = array())
+    {
+        $params['actionID'] = $actionid;
+        if (!empty($reload)) {
+            if (is_bool($reload)) {
+                $params['reload'] = html_entity_decode(Util::removeParameter(Horde::selfUrl(true), array('actionID')));
+            } else {
+                require_once 'Horde/SessionObjects.php';
+                $cacheSess = &Horde_SessionObjects::singleton();
+                $params['passphrase_action'] = $cacheSess->storeOid($reload, false);
+            }
+        }
+
+        return IMP::popupIMPString('pgp.php', $params, 450, 200);
+    }
+
+    /**
+     * Provide the list of parameters needed for signing a message.
+     *
+     * @return array  The list of parameters needed by encrypt().
+     */
+    protected function _signParameters()
+    {
+        return array('pubkey' => $this->getPersonalPublicKey(), 'privkey' => $this->getPersonalPrivateKey(), 'passphrase' => $this->getPassphrase());
+    }
+
+    /**
+     * Provide the list of parameters needed for encrypting a message.
+     *
+     * @param array $addresses    The e-mail address of the keys to use for
+     *                            encryption.
+     * @param boolean $symmetric  Whether to encrypt symmetrically.
+     *
+     * @return array  The list of parameters needed by encrypt().
+     *                Returns PEAR_Error on error.
+     */
+    protected function _encryptParameters($addresses, $symmetric = false)
+    {
+        if ($symmetric) {
+            return array('symmetric' => true,
+                         'passphrase' => $this->getSymmetricPassphrase());
+        }
+
+        $addr_list = array();
+
+        foreach ($addresses as $val) {
+            $addrOb = Horde_Mime_Address::bareAddress($val, $_SESSION['imp']['maildomain'], true);
+            $key_addr = array_pop($addrOb);
+
+            /* Get the public key for the address. */
+            $public_key = $this->getPublicKey($key_addr);
+            if (is_a($public_key, 'PEAR_Error')) {
+                return $public_key;
+            }
+            $addr_list[$key_addr] = $public_key;
+        }
+
+        return array('recips' => $addr_list);
+    }
+
+    /**
+     * Sign a Horde_Mime_Part using PGP using IMP default parameters.
+     *
+     * @param Horde_Mime_Part $mime_part  The object to sign.
+     *
+     * @return Horde_Mime_Part  See Horde_Crypt_pgp::signMIMEPart(). Returns
+     *                          PEAR_Error object on error.
+     */
+    public function IMPsignMIMEPart($mime_part)
+    {
+        return $this->signMIMEPart($mime_part, $this->_signParameters());
+    }
+
+    /**
+     * Encrypt a Horde_Mime_Part using PGP using IMP default parameters.
+     *
+     * @param Horde_Mime_Part $mime_part  The object to encrypt.
+     * @param array $addresses      The e-mail address of the keys to use for
+     *                              encryption.
+     * @param boolean $symmetric    Whether to encrypt symmetrically.
+     *
+     * @return Horde_Mime_Part  See Horde_Crypt_pgp::encryptMIMEPart(). Returns
+     *                    PEAR_Error object on error.
+     */
+    public function IMPencryptMIMEPart($mime_part, $addresses,
+                                       $symmetric = false)
+    {
+        $params = $this->_encryptParameters($addresses, $symmetric);
+        if (is_a($params, 'PEAR_Error')) {
+            return $params;
+        }
+        return $this->encryptMIMEPart($mime_part, $params);
+    }
+
+    /**
+     * Sign and Encrypt a Horde_Mime_Part using PGP using IMP default parameters.
+     *
+     * @param Horde_Mime_Part $mime_part  The object to sign and encrypt.
+     * @param array $addresses      The e-mail address of the keys to use for
+     *                              encryption.
+     * @param boolean $symmetric    Whether to encrypt symmetrically.
+     *
+     * @return Horde_Mime_Part  See Horde_Crypt_pgp::signAndencryptMIMEPart().
+     *                    Returns PEAR_Error object on error.
+     */
+    public function IMPsignAndEncryptMIMEPart($mime_part, $addresses,
+                                              $symmetric = false)
+    {
+        $encrypt_params = $this->_encryptParameters($addresses, $symmetric);
+        if (is_a($encrypt_params, 'PEAR_Error')) {
+            return $encrypt_params;
+        }
+        return $this->signAndEncryptMIMEPart($mime_part, $this->_signParameters(), $encrypt_params);
+    }
+
+    /**
+     * Generate a Horde_Mime_Part object, in accordance with RFC 2015/3156, that
+     * contains the user's public key.
+     *
+     * @return Horde_Mime_Part  See Horde_Crypt_pgp::publicKeyMIMEPart().
+     */
+    public function publicKeyMIMEPart()
+    {
+        return parent::publicKeyMIMEPart($this->getPersonalPublicKey());
+    }
+
+}
diff --git a/imp/lib/Crypt/smime.php b/imp/lib/Crypt/smime.php
new file mode 100644 (file)
index 0000000..7c2494b
--- /dev/null
@@ -0,0 +1,473 @@
+<?php
+
+require_once 'Horde/Crypt/smime.php';
+
+/**
+ * Name of the S/MIME public key field in addressbook.
+ */
+define('IMP_SMIME_PUBKEY_FIELD', 'smimePublicKey');
+
+/**
+ * The IMP_SMIME:: class contains all functions related to handling
+ * S/MIME messages within IMP.
+ *
+ * Copyright 2002-2008 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author  Mike Cochrane <mike@graftonhall.co.nz>
+ * @package IMP
+ */
+class IMP_SMIME extends Horde_Crypt_smime {
+
+    /**
+     * Constructor.
+     */
+    function IMP_SMIME()
+    {
+        parent::Horde_Crypt_smime(array('temp' => Horde::getTempDir()));
+    }
+
+    /**
+     * Add the personal public key to the prefs.
+     *
+     * @param mixed $key  The public key to add (either string or array).
+     */
+    function addPersonalPublicKey($key)
+    {
+        $GLOBALS['prefs']->setValue('smime_public_key', (is_array($key)) ? implode('', $key) : $key);
+    }
+
+    /**
+     * Add the personal private key to the prefs.
+     *
+     * @param mixed $key  The private key to add (either string or array).
+     */
+    function addPersonalPrivateKey($key)
+    {
+        $GLOBALS['prefs']->setValue('smime_private_key', (is_array($key)) ? implode('', $key) : $key);
+    }
+
+    /**
+     * Add the list of additional certs to the prefs.
+     *
+     * @param mixed $key  The private key to add (either string or array).
+     */
+    function addAdditionalCert($key)
+    {
+        $GLOBALS['prefs']->setValue('smime_additional_cert', (is_array($key)) ? implode('', $key) : $key);
+    }
+
+    /**
+     * Get the personal public key from the prefs.
+     *
+     * @return string  The personal S/MIME public key.
+     */
+    function getPersonalPublicKey()
+    {
+        return $GLOBALS['prefs']->getValue('smime_public_key');
+    }
+
+    /**
+     * Get the personal private key from the prefs.
+     *
+     * @return string  The personal S/MIME private key.
+     */
+    function getPersonalPrivateKey()
+    {
+        return $GLOBALS['prefs']->getValue('smime_private_key');
+    }
+
+    /**
+     * Get any additional certificates from the prefs.
+     *
+     * @return string  Additional signing certs for inclusion.
+     */
+    function getAdditionalCert()
+    {
+        return $GLOBALS['prefs']->getValue('smime_additional_cert');
+    }
+
+    /**
+     * Deletes the specified personal keys from the prefs.
+     */
+    function deletePersonalKeys()
+    {
+        $GLOBALS['prefs']->setValue('smime_public_key', '');
+        $GLOBALS['prefs']->setValue('smime_private_key', '');
+        $GLOBALS['prefs']->setValue('smime_additional_cert', '');
+        $this->unsetPassphrase();
+    }
+
+    /**
+     * Add a public key to an address book.
+     *
+     * @param string $cert  A public certificate to add.
+     *
+     * @return boolean  True on successful add.
+     *                  Returns PEAR_Error or error.
+     */
+    function addPublicKey($cert)
+    {
+        /* Make sure the certificate is valid. */
+        $key_info = openssl_x509_parse($cert);
+        if (!is_array($key_info) || !isset($key_info['subject'])) {
+            return PEAR::raiseError(_("Not a valid public key."), 'horde.error');
+        }
+
+        /* Add key to the user's address book. */
+        $email = $this->getEmailFromKey($cert);
+        if ($email === null) {
+            return PEAR::raiseError(_("No email information located in the public key."), 'horde.error');
+        }
+
+        /* Get the name corresponding to this key. */
+        if (isset($key_info['subject']['CN'])) {
+            $name = $key_info['subject']['CN'];
+        } elseif (isset($key_info['subject']['OU'])) {
+            $name = $key_info['subject']['OU'];
+        } else {
+            return PEAR::raiseError(_("Not a valid public key."), 'horde.error');
+        }
+
+        $res = $GLOBALS['registry']->call('contacts/addField', array($email, $name, IMP_SMIME_PUBKEY_FIELD, $cert, $GLOBALS['prefs']->getValue('add_source')));
+        if (is_a($res, 'PEAR_Error')) {
+            return $res;
+        }
+
+        return $key_info;
+    }
+
+    /**
+     * Returns the params needed to encrypt a message being sent to the
+     * specified email address.
+     *
+     * @param string $address  The e-mail address of the recipient.
+     *
+     * @return array  The list of parameters needed by encrypt().
+     *                Returns PEAR_Error object on error.
+     */
+    function _encryptParameters($address)
+    {
+        /* We can only encrypt if we are sending to a single person. */
+        $addrOb = Horde_Mime_Address::bareAddress($address, $_SESSION['imp']['maildomain'], true);
+        $key_addr = array_pop($addrOb);
+
+        $public_key = $this->getPublicKey($key_addr);
+        if (is_a($public_key, 'PEAR_Error')) {
+            return $public_key;
+        }
+
+        return array('type' => 'message', 'pubkey' => $public_key, 'email'  => $address);
+    }
+
+    /**
+     * Retrieves a public key by e-mail.
+     * The key will be retrieved from a user's address book(s).
+     *
+     * @param string $address  The e-mail address to search for.
+     *
+     * @return string  The S/MIME public key requested.
+     *                 Returns PEAR_Error object on error.
+     */
+    function getPublicKey($address)
+    {
+        $params = IMP_Compose::getAddressSearchParams();
+        $key = $GLOBALS['registry']->call('contacts/getField', array($address, IMP_SMIME_PUBKEY_FIELD, $params['sources'], false, true));
+
+        /* See if the address points to the user's public key. */
+        if (is_a($key, 'PEAR_Error')) {
+            require_once 'Horde/Identity.php';
+            $identity = &Identity::singleton(array('imp', 'imp'));
+            $personal_pubkey = $this->getPersonalPublicKey();
+            if (!empty($personal_pubkey) && $identity->hasAddress($address)) {
+                return $personal_pubkey;
+            }
+        }
+
+        /* If more than one public key is returned, just return the first in
+         * the array. There is no way of knowing which is the "preferred" key,
+         * if the keys are different. */
+        if (is_array($key)) {
+            return reset($key);
+        }
+
+        return $key;
+    }
+
+    /**
+     * Retrieves all public keys from a user's address book(s).
+     *
+     * @return array  All PGP public keys available.
+     *                Returns PEAR_Error object on error.
+     */
+    function listPublicKeys()
+    {
+        $params = IMP_Compose::getAddressSearchParams();
+        return (empty($params['sources'])) ? array() : $GLOBALS['registry']->call('contacts/getAllAttributeValues', array(IMP_SMIME_PUBKEY_FIELD, $params['sources']));
+    }
+
+    /**
+     * Deletes a public key from a user's address book(s) by e-mail.
+     *
+     * @param string $email  The e-mail address to delete.
+     *
+     * @return PEAR_Error  Returns PEAR_Error object on error.
+     */
+    function deletePublicKey($email)
+    {
+        $params = IMP_Compose::getAddressSearchParams();
+        return $GLOBALS['registry']->call('contacts/deleteField', array($email, IMP_SMIME_PUBKEY_FIELD, $params['sources']));
+    }
+
+    /**
+     * Returns the parameters needed for signing a message.
+     *
+     * @access private
+     *
+     * @return array  The list of parameters needed by encrypt().
+     */
+    function _signParameters()
+    {
+        return array(
+            'type' => 'signature',
+            'pubkey' => $this->getPersonalPublicKey(),
+            'privkey' => $this->getPersonalPrivateKey(),
+            'passphrase' => $this->getPassphrase(),
+            'sigtype' => 'detach',
+            'certs' => $this->getAdditionalCert()
+        );
+    }
+
+    /**
+     * Verifies a signed message with a given public key.
+     *
+     * @param string $text  The text to verify.
+     *
+     * @return stdClass  See Horde_Crypt_smime::verify().
+     */
+    function verifySignature($text)
+    {
+        return $this->verify($text, empty($GLOBALS['conf']['utils']['openssl_cafile']) ? array() : $GLOBALS['conf']['utils']['openssl_cafile']);
+    }
+
+
+    /**
+     * Decrypt a message with user's public/private keypair.
+     *
+     * @param string $text  The text to decrypt.
+     *
+     * @return string  See Horde_Crypt_smime::decrypt().
+     *                 Returns PEAR_Error object on error.
+     */
+    function decryptMessage($text)
+    {
+        /* decrypt() returns a PEAR_Error object on error. */
+        return $this->decrypt($text, array('type' => 'message', 'pubkey' => $this->getPersonalPublicKey(), 'privkey' => $this->getPersonalPrivateKey(), 'passphrase' => $this->getPassphrase()));
+    }
+
+    /**
+     * Gets the user's passphrase from the session cache.
+     *
+     * @return mixed  The passphrase, if set.  Returns false if the passphrase
+     *                has not been loaded yet.  Returns null if no passphrase
+     *                is needed.
+     */
+    function getPassphrase()
+    {
+        $private_key = $GLOBALS['prefs']->getValue('smime_private_key');
+        if (empty($private_key)) {
+            return false;
+        }
+
+        if (isset($_SESSION['imp']['smime']['passphrase'])) {
+            return Secret::read(IMP::getAuthKey(), $_SESSION['imp']['smime']['passphrase']);
+        } elseif (isset($_SESSION['imp']['smime']['null_passphrase'])) {
+            return ($_SESSION['imp']['smime']['null_passphrase']) ? null : false;
+        } else {
+            $res = $this->verifyPassphrase($private_key, null);
+            if (!isset($_SESSION['imp']['smime'])) {
+                $_SESSION['imp']['smime'] = array();
+            }
+            $_SESSION['imp']['smime']['null_passphrase'] = ($res) ? null : false;
+            return $_SESSION['imp']['smime']['null_passphrase'];
+        }
+    }
+
+    /**
+     * Store's the user's passphrase in the session cache.
+     *
+     * @param string $passphrase  The user's passphrase.
+     *
+     * @return boolean  Returns true if correct passphrase, false if incorrect.
+     */
+    function storePassphrase($passphrase)
+    {
+        if ($this->verifyPassphrase($this->getPersonalPrivateKey(), $passphrase) === false) {
+            return false;
+        }
+
+        if (!isset($_SESSION['imp']['smime'])) {
+            $_SESSION['imp']['smime'] = array();
+        }
+        $_SESSION['imp']['smime']['passphrase'] = Secret::write(IMP::getAuthKey(), $passphrase);
+
+        return true;
+    }
+
+    /**
+     * Clear the passphrase from the session cache.
+     */
+    function unsetPassphrase()
+    {
+        unset($_SESSION['imp']['smime']['null_passphrase']);
+        unset($_SESSION['imp']['smime']['passphrase']);
+    }
+
+    /**
+     * Generates the javascript code for saving public keys.
+     *
+     * @param MIME_Part $mime_part  The MIME_Part containing the public key.
+     *
+     * @return string  The URL for saving public keys.
+     */
+    function savePublicKeyURL($mime_part)
+    {
+        if (empty($cache)) {
+            require_once 'Horde/SessionObjects.php';
+            $cacheSess = &Horde_SessionObjects::singleton();
+            $oid = $cacheSess->storeOid($mime_part);
+        }
+
+        return $this->getJSOpenWinCode('save_attachment_public_key', false, array('cert' => $oid));
+    }
+
+    /**
+     * Print out the link for the javascript S/MIME popup.
+     *
+     * @param integer $actionid  The actionID to perform.
+     * @param mixed $reload      If true, reload base window on close. If text,
+     *                           run this JS on close. If false, don't do
+     *                           anything on close.
+     * @param array $params      Additional parameters needed for the reload
+     *                           page.
+     *
+     * @return string  The javascript link.
+     */
+    function getJSOpenWinCode($actionid, $reload = true, $params = array())
+    {
+        $params['actionID'] = $actionid;
+        if (!empty($reload)) {
+            if (is_bool($reload)) {
+                $params['reload'] = html_entity_decode(Util::removeParameter(Horde::selfUrl(true), array('actionID')));
+            } else {
+                require_once 'Horde/SessionObjects.php';
+                $cacheSess = &Horde_SessionObjects::singleton();
+                $params['passphrase_action'] = $cacheSess->storeOid($reload, false);
+            }
+        }
+
+        return IMP::popupIMPString('smime.php', $params, 450, 200);
+    }
+
+    /**
+     * Encrypt a MIME_Part using S/MIME using IMP defaults.
+     *
+     * @param MIME_Part $mime_part  The MIME_Part object to encrypt.
+     * @param mixed $to_address     The e-mail address of the key to use for
+     *                              encryption.
+     *
+     * @return MIME_Part  See Horde_Crypt_smime::encryptMIMEPart(). Returns
+     *                    PEAR_Error on error.
+     */
+    function IMPencryptMIMEPart($mime_part, $to_address)
+    {
+        $params = $this->_encryptParameters($to_address);
+        if (is_a($params, 'PEAR_Error')) {
+            return $params;
+        }
+        return $this->encryptMIMEPart($mime_part, $params);
+    }
+
+    /**
+     * Sign a MIME_Part using S/MIME using IMP defaults.
+     *
+     * @param MIME_Part $mime_part  The MIME_Part object to sign.
+     *
+     * @return MIME_Part  See Horde_Crypt_smime::signMIMEPart(). Returns
+     *                    PEAR_Error on error.
+     */
+    function IMPsignMIMEPart($mime_part)
+    {
+        return $this->signMIMEPart($mime_part, $this->_signParameters());
+    }
+
+    /**
+     * Sign and encrypt a MIME_Part using S/MIME using IMP defaults.
+     *
+     * @param MIME_Part $mime_part  The MIME_Part object to sign and encrypt.
+     * @param string $to_address    The e-mail address of the key to use for
+     *                              encryption.
+     *
+     * @return MIME_Part  See Horde_Crypt_smime::signAndencryptMIMEPart().
+     *                    Returns PEAR_Error on error.
+     */
+    function IMPsignAndEncryptMIMEPart($mime_part, $to_address)
+    {
+        $encrypt_params = $this->_encryptParameters($to_address);
+        if (is_a($encrypt_params, 'PEAR_Error')) {
+            return $encrypt_params;
+        }
+        return $this->signAndEncryptMIMEPart($mime_part, $this->_signParameters(), $encrypt_params);
+    }
+
+    /**
+     * Store the public/private/additional certificates in the preferences
+     * from a given PKCS 12 file.
+     *
+     * @param string $pkcs12    The PKCS 12 data.
+     * @param string $password  The password of the PKCS 12 file.
+     * @param string $pkpass    The password to use to encrypt the private key.
+     *
+     * @return boolean  True on success, PEAR_Error on error.
+     */
+    function addFromPKCS12($pkcs12, $password, $pkpass = null)
+    {
+        $openssl = IMP_SMIME::checkForOpenSSL();
+        if (is_a($openssl, 'PEAR_Error')) {
+            return $openssl;
+        }
+
+        $sslpath = (empty($GLOBALS['conf']['utils']['openssl_binary'])) ? null : $GLOBALS['conf']['utils']['openssl_binary'];
+        $params = array('sslpath' => $sslpath, 'password' => $password);
+        if (!empty($pkpass)) {
+            $params['newpassword'] = $pkpass;
+        }
+        $res = $this->parsePKCS12Data($pkcs12, $params);
+        if (is_a($res, 'PEAR_Error')) {
+            return $res;
+        }
+
+        $this->addPersonalPrivateKey($res->private);
+        $this->addPersonalPublicKey($res->public);
+        $this->addAdditionalCert($res->certs);
+
+        return true;
+    }
+
+    /**
+     * Extract the contents from signed S/MIME data.
+     *
+     * @param string $data  The signed S/MIME data.
+     *
+     * @return string  The contents embedded in the signed data.
+     *                 Returns PEAR_Error on error.
+     */
+    function extractSignedContents($data)
+    {
+        $sslpath = (empty($GLOBALS['conf']['utils']['openssl_binary'])) ? null : $GLOBALS['conf']['utils']['openssl_binary'];
+        return parent::extractSignedContents($data, $sslpath);
+    }
+
+}