Horde 4-ify Horde_Crypt.
authorMichael M Slusarz <slusarz@curecanti.org>
Thu, 26 Feb 2009 05:30:40 +0000 (22:30 -0700)
committerMichael M Slusarz <slusarz@curecanti.org>
Thu, 26 Feb 2009 07:01:16 +0000 (00:01 -0700)
Rework driver naming scheme
Use file_put_contents() in Pgp driver.
Convert to full Exception handling.

framework/Crypt/lib/Horde/Crypt.php
framework/Crypt/lib/Horde/Crypt/Pgp.php [new file with mode: 0644]
framework/Crypt/lib/Horde/Crypt/Smime.php [new file with mode: 0644]
framework/Crypt/lib/Horde/Crypt/pgp.php [deleted file]
framework/Crypt/lib/Horde/Crypt/smime.php [deleted file]
framework/Crypt/package.xml
framework/Crypt/test/Horde/Crypt/pgp.inc
framework/Crypt/test/Horde/Crypt/smime.inc

index 5d35bcc..0dfa3ef 100644 (file)
 class Horde_Crypt
 {
     /**
+     * Singleton instances.
+     *
+     * @var array
+     */
+    static protected $_instances = array();
+
+    /**
      * The temporary directory to use.
      *
      * @var string
@@ -30,8 +37,8 @@ class Horde_Crypt
      * @param array $params  A hash containing any additional configuration or
      *                       parameters a subclass might need.
      *
-     * @return Horde_Crypt  The newly created concrete Horde_Crypt instance, or
-     *                      false on an error.
+     * @return Horde_Crypt  The newly created concrete Horde_Crypt instance.
+     * @throws Horde_Exception
      */
     static public function factory($driver, $params = array())
     {
@@ -43,26 +50,17 @@ class Horde_Crypt
         }
 
         /* Return a base Horde_Crypt object if no driver is specified. */
-        if (empty($driver) || (strcmp($driver, 'none') == 0)) {
+        if (empty($driver) || (strcasecmp($driver, 'none') == 0)) {
             return new Horde_Crypt();
         }
 
-        $class = 'Horde_Crypt_' . $driver;
-        if (!empty($app)) {
-            $class = $app . '_' . $class;
-        }
+        $class = (empty($app) ? 'Horde' : $app) . '_Crypt_' . ucfirst($driver);
 
-        if (!class_exists($class)) {
-            if (empty($app)) {
-                include_once dirname(__FILE__) . '/Crypt/' . $driver . '.php';
-            } else {
-                include_once $GLOBALS['registry']->get('fileroot', $app) . '/lib/Crypt/' . $driver . '.php';
-            }
+        if (class_exists($class)) {
+            return new $class($params);
         }
 
-        return class_exists($class)
-            ? new $class($params)
-            : PEAR::raiseError('Class definition of ' . $class . ' not found.');
+        throw new Horde_Exception('Class definition of ' . $class . ' not found.');
     }
 
     /**
@@ -73,7 +71,7 @@ class Horde_Crypt
      * This should be used if multiple crypto backends (and, thus,
      * multiple Horde_Crypt instances) are required.
      *
-     * This method must be invoked as: $var = &Horde_Crypt::singleton()
+     * This method must be invoked as: $var = Horde_Crypt::singleton()
      *
      * @param mixed $driver  The type of concrete Horde_Crypt subclass to
      *                       return. If $driver is an array, then we will look
@@ -82,26 +80,25 @@ class Horde_Crypt
      * @param array $params  A hash containing any additional configuration or
      *                       connection parameters a subclass might need.
      *
-     * @return Horde_Crypt  The concrete Horde_Crypt reference, or false on an
-     *                      error.
+     * @return Horde_Crypt  The concrete Horde_Crypt reference.
+     * @throws Horde_Exception
      */
     static public function &singleton($driver, $params = array())
     {
-        static $instances = array();
+        ksort($params);
+        $signature = hash('md5', serialize(array($driver, $params)));
 
-        $signature = serialize(array($driver, $params));
-        if (!isset($instances[$signature])) {
-            $instances[$signature] = Horde_Crypt::factory($driver, $params);
+        if (!isset(self::$_instances[$signature])) {
+            self::$_instances[$signature] = Horde_Crypt::factory($driver, $params);
         }
 
-        return $instances[$signature];
+        return self::$_instances[$signature];
     }
 
     /**
-     * Outputs error message if we are not using a secure connection.
+     * Throws exception if not using a secure connection.
      *
-     * @return PEAR_Error  Returns a PEAR_Error object if there is no secure
-     *                     connection.
+     * @throws Horde_Exception
      */
     public function requireSecureConnection()
     {
@@ -131,7 +128,7 @@ class Horde_Crypt
             }
         }
 
-        return PEAR::raiseError(_("The encryption features require a secure web connection."));
+        throw new Horde_Exception (_("The encryption features require a secure web connection."));
     }
 
     /**
@@ -156,6 +153,7 @@ class Horde_Crypt
      * @param array $params  An array of arguments needed to decrypt the data.
      *
      * @return array  The decrypted data.
+     * @throws Horde_Exception
      */
     public function decrypt($data, $params = array())
     {
@@ -166,8 +164,6 @@ class Horde_Crypt
      * Create a temporary file that will be deleted at the end of this
      * process.
      *
-     * @access private
-     *
      * @param string  $descrip  Description string to use in filename.
      * @param boolean $delete   Delete the file automatically?
      *
diff --git a/framework/Crypt/lib/Horde/Crypt/Pgp.php b/framework/Crypt/lib/Horde/Crypt/Pgp.php
new file mode 100644 (file)
index 0000000..a8b07e8
--- /dev/null
@@ -0,0 +1,1609 @@
+<?php
+/**
+ * Horde_Crypt_Pgp:: provides a framework for Horde applications to interact
+ * with the GNU Privacy Guard program ("GnuPG").  GnuPG implements the OpenPGP
+ * standard (RFC 2440).
+ *
+ * GnuPG Website: http://www.gnupg.org/
+ *
+ * This class has been developed with, and is only guaranteed to work with,
+ * Version 1.21 or above of GnuPG.
+ *
+ * Copyright 2002-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ *
+ * @author  Michael Slusarz <slusarz@horde.org>
+ * @package Horde_Crypt
+ */
+class Horde_Crypt_Pgp extends Horde_Crypt
+{
+    /**
+     * Armor Header Lines - From RFC 2440:
+     *
+     * An Armor Header Line consists of the appropriate header line text
+     * surrounded by five (5) dashes ('-', 0x2D) on either side of the header
+     * line text. The header line text is chosen based upon the type of data
+     * that is being encoded in Armor, and how it is being encoded.
+     *
+     *  All Armor Header Lines are prefixed with 'PGP'.
+     *
+     *  The Armor Tail Line is composed in the same manner as the Armor Header
+     *  Line, except the string "BEGIN" is replaced by the string "END."
+     */
+
+    /* Used for signed, encrypted, or compressed files. */
+    const ARMOR_MESSAGE = 1;
+
+    /* Used for signed files. */
+    const ARMOR_SIGNED_MESSAGE = 2;
+
+    /* Used for armoring public keys. */
+    const ARMOR_PUBLIC_KEY = 3;
+
+    /* Used for armoring private keys. */
+    const ARMOR_PRIVATE_KEY = 4;
+
+    /* Used for detached signatures, PGP/MIME signatures, and natures
+     * following clearsigned messages. */
+    const ARMOR_SIGNATURE = 5;
+
+    /* Regular text contained in an PGP message. */
+    const ARMOR_TEXT = 6;
+
+    /**
+     * Strings in armor header lines used to distinguish between the different
+     * types of PGP decryption/encryption.
+     *
+     * @var array
+     */
+    protected $_armor = array(
+        'MESSAGE' => self::ARMOR_MESSAGE,
+        'SIGNED MESSAGE' => self::ARMOR_SIGNED_MESSAGE,
+        'PUBLIC KEY BLOCK' => self::ARMOR_PUBLIC_KEY,
+        'PRIVATE KEY BLOCK' => self::ARMOR_PRIVATE_KEY,
+        'SIGNATURE' => self::ARMOR_SIGNATURE
+    );
+
+    /* The default public PGP keyserver to use. */
+    const KEYSERVER_PUBLIC = 'pgp.mit.edu';
+
+    /* The number of times the keyserver refuses connection before an error is
+     * returned. */
+    const KEYSERVER_REFUSE = 3;
+
+    /* The number of seconds that PHP will attempt to connect to the keyserver
+     * before it will stop processing the request. */
+    const KEYSERVER_TIMEOUT = 10;
+
+    /**
+     * The list of PGP hash algorithms (from RFC 3156).
+     *
+     * @var array
+     */
+    protected $_hashAlg = array(
+        1 => 'pgp-md5',
+        2 => 'pgp-sha1',
+        3 => 'pgp-ripemd160',
+        5 => 'pgp-md2',
+        6 => 'pgp-tiger192',
+        7 => 'pgp-haval-5-160',
+        8 => 'pgp-sha256',
+        9 => 'pgp-sha384',
+        10 => 'pgp-sha512',
+        11 => 'pgp-sha224',
+    );
+
+    /**
+     * GnuPG program location/common options.
+     *
+     * @var array
+     */
+    protected $_gnupg;
+
+    /**
+     * Filename of the temporary public keyring.
+     *
+     * @var string
+     */
+    protected $_publicKeyring;
+
+    /**
+     * Filename of the temporary private keyring.
+     *
+     * @var string
+     */
+    protected $_privateKeyring;
+
+    /**
+     * Constructor.
+     *
+     * @param array $params  Parameter array containing the path to the GnuPG
+     *                       binary (key = 'program') and to a temporary
+     *                       directory.
+     *
+     * @throws Horde_Exception
+     */
+    protected function __construct($params = array())
+    {
+        $this->_tempdir = Util::createTempDir(true, $params['temp']);
+
+        if (empty($params['program'])) {
+            Horde::fatal(new Horde_Exception('The location of the GnuPG binary must be given to the Horde_Crypt_Pgp:: class.'), __FILE__, __LINE__);
+        }
+
+        /* Store the location of GnuPG and set common options. */
+        $this->_gnupg = array(
+            $params['program'],
+            '--no-tty',
+            '--no-secmem-warning',
+            '--no-options',
+            '--no-default-keyring',
+            '--yes',
+            '--homedir ' . $this->_tempdir
+        );
+
+        if (strncasecmp(PHP_OS, 'WIN', 3)) {
+            array_unshift($this->_gnupg, 'LANG= ;');
+        }
+    }
+
+    /**
+     * Generates a personal Public/Private keypair combination.
+     *
+     * @param string $realname    The name to use for the key.
+     * @param string $email       The email to use for the key.
+     * @param string $passphrase  The passphrase to use for the key.
+     * @param string $comment     The comment to use for the key.
+     * @param integer $keylength  The keylength to use for the key.
+     *
+     * @return array  An array consisting of:
+     * <pre>
+     * Key            Value
+     * --------------------------
+     * 'public'   =>  Public Key
+     * 'private'  =>  Private Key
+     * </pre>
+     * @throws Horde_Exception
+     */
+    public function generateKey($realname, $email, $passphrase, $comment = '',
+                                $keylength = 1024)
+    {
+        /* Create temp files to hold the generated keys. */
+        $pub_file = $this->_createTempFile('horde-pgp');
+        $secret_file = $this->_createTempFile('horde-pgp');
+
+        /* Create the config file necessary for GnuPG to run in batch mode. */
+        /* TODO: Sanitize input, More user customizable? */
+        $input = array(
+            '%pubring ' . $pub_file,
+            '%secring ' . $secret_file,
+            'Key-Type: DSA',
+            'Key-Length: 1024',
+            'Subkey-Type: ELG-E',
+            'Subkey-Length: ' . $keylength,
+            'Name-Real: ' . $realname,
+            'Name-Email: ' . $email,
+            'Expire-Date: 0',
+            'Passphrase: ' . $passphrase
+        );
+        if (!empty($comment)) {
+            $input[] = 'Name-Comment: ' . $comment;
+        }
+        $input[] = '%commit';
+
+        /* Run through gpg binary. */
+        $cmdline = array(
+            '--gen-key',
+            '--batch',
+            '--armor'
+        );
+        $result = $this->_callGpg($cmdline, 'w', $input, true, true);
+
+        /* Get the keys from the temp files. */
+        $public_key = file($pub_file);
+        $secret_key = file($secret_file);
+
+        /* If either key is empty, something went wrong. */
+        if (empty($public_key) || empty($secret_key)) {
+            $msg = _("Public/Private keypair not generated successfully.");
+            if (!empty($result->stderr)) {
+                $msg .= ' ' . _("Returned error message:") . ' ' . $result->stderr;
+            }
+            throw new Horde_Exception($msg, 'horde.error');
+        }
+
+        return array('public' => $public_key, 'private' => $secret_key);
+    }
+
+    /**
+     * Returns information on a PGP data block.
+     *
+     * @param string $pgpdata  The PGP data block.
+     *
+     * @return array  An array with information on the PGP data block. If an
+     *                element is not present in the data block, it will
+     *                likewise not be set in the array.
+     * <pre>
+     * Array Format:
+     * -------------
+     * [public_key]/[secret_key] => Array
+     *   (
+     *     [created] => Key creation - UNIX timestamp
+     *     [expires] => Key expiration - UNIX timestamp (0 = never expires)
+     *     [size]    => Size of the key in bits
+     *   )
+     *
+     * [keyid] => Key ID of the PGP data (if available)
+     *            16-bit hex value (as of Horde 3.2)
+     *
+     * [signature] => Array (
+     *     [id{n}/'_SIGNATURE'] => Array (
+     *         [name]        => Full Name
+     *         [comment]     => Comment
+     *         [email]       => E-mail Address
+     *         [keyid]       => 16-bit hex value (as of Horde 3.2)
+     *         [created]     => Signature creation - UNIX timestamp
+     *         [expires]     => Signature expiration - UNIX timestamp
+     *         [micalg]      => The hash used to create the signature
+     *         [sig_{hex}]   => Array [details of a sig verifying the ID] (
+     *             [created]     => Signature creation - UNIX timestamp
+     *             [expires]     => Signature expiration - UNIX timestamp
+     *             [keyid]       => 16-bit hex value (as of Horde 3.2)
+     *             [micalg]      => The hash used to create the signature
+     *         )
+     *     )
+     * )
+     * </pre>
+     *
+     * Each user ID will be stored in the array 'signature' and have data
+     * associated with it, including an array for information on each
+     * signature that has signed that UID. Signatures not associated with a
+     * UID (e.g. revocation signatures and sub keys) will be stored under the
+     * special keyword '_SIGNATURE'.
+     */
+    public function pgpPacketInformation($pgpdata)
+    {
+        $data_array = array();
+        $keyid = '';
+        $header = null;
+        $input = $this->_createTempFile('horde-pgp');
+        $sig_id = $uid_idx = 0;
+
+        /* Store message in temporary file. */
+        file_put_contents($input, $pgpdata);
+
+        $cmdline = array(
+            '--list-packets',
+            $input
+        );
+        $result = $this->_callGpg($cmdline, 'r');
+
+        foreach (explode("\n", $result->stdout) as $line) {
+            /* Headers are prefaced with a ':' as the first character on the
+               line. */
+            if (strpos($line, ':') === 0) {
+                $lowerLine = String::lower($line);
+
+                /* If we have a key (rather than a signature block), get the
+                   key's ID */
+                if (strpos($lowerLine, ':public key packet:') !== false ||
+                    strpos($lowerLine, ':secret key packet:') !== false) {
+                    $cmdline = array(
+                        '--with-colons',
+                        $input
+                    );
+                    $data = $this->_callGpg($cmdline, 'r');
+                    if (preg_match("/(sec|pub):.*:.*:.*:([A-F0-9]{16}):/", $data->stdout, $matches)) {
+                        $keyid = $matches[2];
+                    }
+                }
+
+                if (strpos($lowerLine, ':public key packet:') !== false) {
+                    $header = 'public_key';
+                } elseif (strpos($lowerLine, ':secret key packet:') !== false) {
+                    $header = 'secret_key';
+                } elseif (strpos($lowerLine, ':user id packet:') !== false) {
+                    $uid_idx++;
+                    $line = preg_replace_callback('/\\\\x([0-9a-f]{2})/', array($this, '_pgpPacketInformationHelper'), $line);
+                    if (preg_match("/\"([^\<]+)\<([^\>]+)\>\"/", $line, $matches)) {
+                        $header = 'id' . $uid_idx;
+                        if (preg_match('/([^\(]+)\((.+)\)$/', trim($matches[1]), $comment_matches)) {
+                            $data_array['signature'][$header]['name'] = trim($comment_matches[1]);
+                            $data_array['signature'][$header]['comment'] = $comment_matches[2];
+                        } else {
+                            $data_array['signature'][$header]['name'] = trim($matches[1]);
+                            $data_array['signature'][$header]['comment'] = '';
+                        }
+                        $data_array['signature'][$header]['email'] = $matches[2];
+                        $data_array['signature'][$header]['keyid'] = $keyid;
+                    }
+                } elseif (strpos($lowerLine, ':signature packet:') !== false) {
+                    if (empty($header) || empty($uid_idx)) {
+                        $header = '_SIGNATURE';
+                    }
+                    if (preg_match("/keyid\s+([0-9A-F]+)/i", $line, $matches)) {
+                        $sig_id = $matches[1];
+                        $data_array['signature'][$header]['sig_' . $sig_id]['keyid'] = $matches[1];
+                        $data_array['keyid'] = $matches[1];
+                    }
+                } elseif (strpos($lowerLine, ':literal data packet:') !== false) {
+                    $header = 'literal';
+                } elseif (strpos($lowerLine, ':encrypted data packet:') !== false) {
+                    $header = 'encrypted';
+                } else {
+                    $header = null;
+                }
+            } else {
+                if ($header == 'secret_key' || $header == 'public_key') {
+                    if (preg_match("/created\s+(\d+),\s+expires\s+(\d+)/i", $line, $matches)) {
+                        $data_array[$header]['created'] = $matches[1];
+                        $data_array[$header]['expires'] = $matches[2];
+                    } elseif (preg_match("/\s+[sp]key\[0\]:\s+\[(\d+)/i", $line, $matches)) {
+                        $data_array[$header]['size'] = $matches[1];
+                    }
+                } elseif ($header == 'literal' || $header == 'encrypted') {
+                    $data_array[$header] = true;
+                } elseif ($header) {
+                    if (preg_match("/version\s+\d+,\s+created\s+(\d+)/i", $line, $matches)) {
+                        $data_array['signature'][$header]['sig_' . $sig_id]['created'] = $matches[1];
+                    } elseif (isset($data_array['signature'][$header]['sig_' . $sig_id]['created']) &&
+                              preg_match('/expires after (\d+y\d+d\d+h\d+m)\)$/', $line, $matches)) {
+                        $expires = $matches[1];
+                        preg_match('/^(\d+)y(\d+)d(\d+)h(\d+)m$/', $expires, $matches);
+                        list(, $years, $days, $hours, $minutes) = $matches;
+                        $data_array['signature'][$header]['sig_' . $sig_id]['expires'] =
+                            strtotime('+ ' . $years . ' years + ' . $days . ' days + ' . $hours . ' hours + ' . $minutes . ' minutes', $data_array['signature'][$header]['sig_' . $sig_id]['created']);
+                    } elseif (preg_match("/digest algo\s+(\d{1})/", $line, $matches)) {
+                        $micalg = $this->_hashAlg[$matches[1]];
+                        $data_array['signature'][$header]['sig_' . $sig_id]['micalg'] = $micalg;
+                        if ($header == '_SIGNATURE') {
+                            /* Likely a signature block, not a key. */
+                            $data_array['signature']['_SIGNATURE']['micalg'] = $micalg;
+                        }
+                        if ($sig_id == $keyid) {
+                            /* Self signing signature - we can assume
+                             * the micalg value from this signature is
+                             * that for the key */
+                            $data_array['signature']['_SIGNATURE']['micalg'] = $micalg;
+                            $data_array['signature'][$header]['micalg'] = $micalg;
+                        }
+                    }
+                }
+            }
+        }
+
+        $keyid && $data_array['keyid'] = $keyid;
+
+        return $data_array;
+    }
+
+    /**
+     * TODO
+     */
+    protected function _pgpPacketInformationHelper($a)
+    {
+        return chr(hexdec($a[1]));
+    }
+
+    /**
+     * Returns human readable information on a PGP key.
+     *
+     * @param string $pgpdata  The PGP data block.
+     *
+     * @return string  Tabular information on the PGP key.
+     */
+    public function pgpPrettyKey($pgpdata)
+    {
+        $msg = '';
+        $packet_info = $this->pgpPacketInformation($pgpdata);
+        $fingerprints = $this->getFingerprintsFromKey($pgpdata);
+
+        if (!empty($packet_info['signature'])) {
+            /* Making the property names the same width for all
+             * localizations .*/
+            $leftrow = array(_("Name"), _("Key Type"), _("Key Creation"),
+                             _("Expiration Date"), _("Key Length"),
+                             _("Comment"), _("E-Mail"), _("Hash-Algorithm"),
+                             _("Key ID"), _("Key Fingerprint"));
+            $leftwidth = array_map('strlen', $leftrow);
+            $maxwidth  = max($leftwidth) + 2;
+            array_walk($leftrow, array($this, '_pgpPrettyKeyFormatter'), $maxwidth);
+
+            foreach (array_keys($packet_info['signature']) as $uid_idx) {
+                if ($uid_idx == '_SIGNATURE') {
+                    continue;
+                }
+                $key_info = $this->pgpPacketSignatureByUidIndex($pgpdata, $uid_idx);
+
+                if (!empty($key_info['keyid'])) {
+                    $key_info['keyid'] = $this->_getKeyIDString($key_info['keyid']);
+                } else {
+                    $key_info['keyid'] = null;
+                }
+
+                $fingerprint = isset($fingerprints[$key_info['keyid']]) ? $fingerprints[$key_info['keyid']] : null;
+
+                $msg .= $leftrow[0] . (isset($key_info['name']) ? stripcslashes($key_info['name']) : '') . "\n"
+                    . $leftrow[1] . (($key_info['key_type'] == 'public_key') ? _("Public Key") : _("Private Key")) . "\n"
+                    . $leftrow[2] . strftime("%D", $key_info['key_created']) . "\n"
+                    . $leftrow[3] . (empty($key_info['key_expires']) ? '[' . _("Never") . ']' : strftime("%D", $key_info['key_expires'])) . "\n"
+                    . $leftrow[4] . $key_info['key_size'] . " Bytes\n"
+                    . $leftrow[5] . (empty($key_info['comment']) ? '[' . _("None") . ']' : $key_info['comment']) . "\n"
+                    . $leftrow[6] . (empty($key_info['email']) ? '[' . _("None") . ']' : $key_info['email']) . "\n"
+                    . $leftrow[7] . (empty($key_info['micalg']) ? '[' . _("Unknown") . ']' : $key_info['micalg']) . "\n"
+                    . $leftrow[8] . (empty($key_info['keyid']) ? '[' . _("Unknown") . ']' : $key_info['keyid']) . "\n"
+                    . $leftrow[9] . (empty($fingerprint) ? '[' . _("Unknown") . ']' : $fingerprint) . "\n\n";
+            }
+        }
+
+        return $msg;
+    }
+
+    /**
+     * TODO
+     */
+    protected function _pgpPrettyKeyFormatter(&$s, $k, $m)
+    {
+        $s .= ':' . str_repeat(' ', $m - String::length($s));
+    }
+
+    /**
+     * TODO
+     */
+    protected function _getKeyIDString($keyid)
+    {
+        /* Get the 8 character key ID string. */
+        if (strpos($keyid, '0x') === 0) {
+            $keyid = substr($keyid, 2);
+        }
+        if (strlen($keyid) > 8) {
+            $keyid = substr($keyid, -8);
+        }
+        return '0x' . $keyid;
+    }
+
+    /**
+     * Returns only information on the first ID that matches the email address
+     * input.
+     *
+     * @param string $pgpdata  The PGP data block.
+     * @param string $email    An e-mail address.
+     *
+     * @return array  An array with information on the PGP data block. If an
+     *                element is not present in the data block, it will
+     *                likewise not be set in the array.
+     * <pre>
+     * Array Fields:
+     * -------------
+     * key_created  =>  Key creation - UNIX timestamp
+     * key_expires  =>  Key expiration - UNIX timestamp (0 = never expires)
+     * key_size     =>  Size of the key in bits
+     * key_type     =>  The key type (public_key or secret_key)
+     * name         =>  Full Name
+     * comment      =>  Comment
+     * email        =>  E-mail Address
+     * keyid        =>  16-bit hex value
+     * created      =>  Signature creation - UNIX timestamp
+     * micalg       =>  The hash used to create the signature
+     * </pre>
+     */
+    public function pgpPacketSignature($pgpdata, $email)
+    {
+        $data = $this->pgpPacketInformation($pgpdata);
+        $key_type = null;
+        $return_array = array();
+
+        /* Check that [signature] key exists. */
+        if (!isset($data['signature'])) {
+            return $return_array;
+        }
+
+        /* Store the signature information now. */
+        if (($email == '_SIGNATURE') &&
+            isset($data['signature']['_SIGNATURE'])) {
+            foreach ($data['signature'][$email] as $key => $value) {
+                $return_array[$key] = $value;
+            }
+        } else {
+            $uid_idx = 1;
+
+            while (isset($data['signature']['id' . $uid_idx])) {
+                if ($data['signature']['id' . $uid_idx]['email'] == $email) {
+                    foreach ($data['signature']['id' . $uid_idx] as $key => $val) {
+                        $return_array[$key] = $val;
+                    }
+                    break;
+                }
+                $uid_idx++;
+            }
+        }
+
+        return $this->_pgpPacketSignature($data, $return_array);
+    }
+
+    /**
+     * Returns information on a PGP signature embedded in PGP data.  Similar
+     * to pgpPacketSignature(), but returns information by unique User ID
+     * Index (format id{n} where n is an integer of 1 or greater).
+     *
+     * @param string $pgpdata  See pgpPacketSignature().
+     * @param string $uid_idx  The UID index.
+     *
+     * @return array  See pgpPacketSignature().
+     */
+    public function pgpPacketSignatureByUidIndex($pgpdata, $uid_idx)
+    {
+        $data = $this->pgpPacketInformation($pgpdata);
+        $key_type = null;
+        $return_array = array();
+
+        /* Search for the UID index. */
+        if (!isset($data['signature']) ||
+            !isset($data['signature'][$uid_idx])) {
+            return $return_array;
+        }
+
+        /* Store the signature information now. */
+        foreach ($data['signature'][$uid_idx] as $key => $value) {
+            $return_array[$key] = $value;
+        }
+
+        return $this->_pgpPacketSignature($data, $return_array);
+    }
+
+    /**
+     * Adds some data to the pgpPacketSignature*() function array.
+     *
+     * @param array $data      See pgpPacketSignature().
+     * @param array $retarray  The return array.
+     *
+     * @return array  The return array.
+     */
+    protected function _pgpPacketSignature($data, $retarray)
+    {
+        /* If empty, return now. */
+        if (empty($retarray)) {
+            return $retarray;
+        }
+
+        $key_type = null;
+
+        /* Store any public/private key information. */
+        if (isset($data['public_key'])) {
+            $key_type = 'public_key';
+        } elseif (isset($data['secret_key'])) {
+            $key_type = 'secret_key';
+        }
+
+        if ($key_type) {
+            $retarray['key_type'] = $key_type;
+            if (isset($data[$key_type]['created'])) {
+                $retarray['key_created'] = $data[$key_type]['created'];
+            }
+            if (isset($data[$key_type]['expires'])) {
+                $retarray['key_expires'] = $data[$key_type]['expires'];
+            }
+            if (isset($data[$key_type]['size'])) {
+                $retarray['key_size'] = $data[$key_type]['size'];
+            }
+        }
+
+        return $retarray;
+    }
+
+    /**
+     * Returns the key ID of the key used to sign a block of PGP data.
+     *
+     * @param string $text  The PGP signed text block.
+     *
+     * @return string  The key ID of the key used to sign $text.
+     */
+    public function getSignersKeyID($text)
+    {
+        $keyid = null;
+
+        $input = $this->_createTempFile('horde-pgp');
+        file_put_contents($input, $text);
+
+        $cmdline = array(
+            '--verify',
+            $input
+        );
+        $result = $this->_callGpg($cmdline, 'r', null, true, true);
+        if (preg_match('/gpg:\sSignature\smade.*ID\s+([A-F0-9]{8})\s+/', $result->stderr, $matches)) {
+            $keyid = $matches[1];
+        }
+
+        return $keyid;
+    }
+
+    /**
+     * Verify a passphrase for a given public/private keypair.
+     *
+     * @param string $public_key   The user's PGP public key.
+     * @param string $private_key  The user's PGP private key.
+     * @param string $passphrase   The user's passphrase.
+     *
+     * @return boolean  Returns true on valid passphrase, false on invalid
+     *                  passphrase.
+     */
+    public function verifyPassphrase($public_key, $private_key, $passphrase)
+    {
+        /* Encrypt a test message. */
+        try {
+            $this->encrypt('Test', array('type' => 'message', 'pubkey' => $public_key));
+        } catch (Horde_Exception $e) {
+            return false;
+        }
+
+        /* Try to decrypt the message. */
+        try {
+           $this->decrypt($result, array('type' => 'message', 'pubkey' => $public_key, 'privkey' => $private_key, 'passphrase' => $passphrase));
+        } catch (Horde_Exception $e) {
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Parses a message into text and PGP components.
+     *
+     * @param string $text  The text to parse.
+     *
+     * @return array  An array with the parsed text, returned in blocks of
+     *                text corresponding to their actual order. Keys:
+     * <pre>
+     * 'type' -  (integer) The type of data contained in block.
+     *           Valid types are defined at the top of this class
+     *           (the ARMOR_* constants).
+     * 'data' - (array) The data for each section. Each line has been stripped
+     *          of EOL characters.
+     * </pre>
+     */
+    public function parsePGPData($text)
+    {
+        $data = array();
+        $temp = array(
+            'type' => self::ARMOR_TEXT
+        );
+
+        $buffer = explode("\n", $text);
+        while (list(,$val) = each($buffer)) {
+            $val = rtrim($val, "\r");
+            if (preg_match('/^-----(BEGIN|END) PGP ([^-]+)-----\s*$/', $val, $matches)) {
+                if (isset($temp['data'])) {
+                    $data[] = $temp;
+                }
+                $temp= array();
+
+                if ($matches[1] == 'BEGIN') {
+                    $temp['type'] = $this->_armor[$matches[2]];
+                    $temp['data'][] = $val;
+                } elseif ($matches[1] == 'END') {
+                    $temp['type'] = self::ARMOR_TEXT;
+                    $data[count($data) - 1]['data'][] = $val;
+                }
+            } else {
+                $temp['data'][] = $val;
+            }
+        }
+
+        if (isset($temp['data']) &&
+            ((count($temp['data']) > 1) || !empty($temp['data'][0]))) {
+            $data[] = $temp;
+        }
+
+        return $data;
+    }
+
+    /**
+     * Returns a PGP public key from a public keyserver.
+     *
+     * @param string $keyid    The key ID of the PGP key.
+     * @param string $server   The keyserver to use.
+     * @param float $timeout   The keyserver timeout.
+     * @param string $address  The email address of the PGP key.
+     *
+     * @return string  The PGP public key.
+     */
+    public function getPublicKeyserver($keyid,
+                                       $server = self::KEYSERVER_PUBLIC,
+                                       $timeout = self::KEYSERVER_TIMEOUT,
+                                       $address = null)
+    {
+        if (empty($keyid) && !empty($address)) {
+            $keyid = $this->getKeyID($address, $server, $timeout);
+        }
+
+        /* Connect to the public keyserver. */
+        $uri = '/pks/lookup?op=get&search=' . $this->_getKeyIDString($keyid);
+        $output = $this->_connectKeyserver('GET', $server, $uri, '', $timeout);
+
+        /* Strip HTML Tags from output. */
+        if (($start = strstr($output, '-----BEGIN'))) {
+            $length = strpos($start, '-----END') + 34;
+            return substr($start, 0, $length);
+        }
+
+        throw new Horde_Exception(_("Could not obtain public key from the keyserver."), 'horde.error');
+    }
+
+    /**
+     * Sends a PGP public key to a public keyserver.
+     *
+     * @param string $pubkey  The PGP public key
+     * @param string $server  The keyserver to use.
+     * @param float $timeout  The keyserver timeout.
+     *
+     * @throws Horde_Exception
+     */
+    public function putPublicKeyserver($pubkey,
+                                       $server = self::KEYSERVER_PUBLIC,
+                                       $timeout = self::KEYSERVER_TIMEOUT)
+    {
+        /* Get the key ID of the public key. */
+        $info = $this->pgpPacketInformation($pubkey);
+
+        /* See if the public key already exists on the keyserver. */
+        try {
+            $this->getPublicKeyserver($info['keyid'], $server, $timeout);
+        } catch (Horde_Exception $e) {
+            throw new Horde_Exception(_("Key already exists on the public keyserver."), 'horde.warning');
+        }
+
+        /* Connect to the public keyserver. _connectKeyserver() */
+        $pubkey = 'keytext=' . urlencode(rtrim($pubkey));
+        $cmd = array(
+            'Host: ' . $server . ':11371',
+            'User-Agent: Horde Application Framework 3.2',
+            'Content-Type: application/x-www-form-urlencoded',
+            'Content-Length: ' . strlen($pubkey),
+            'Connection: close',
+            '',
+            $pubkey
+        );
+
+        return $this->_connectKeyserver('POST', $server, '/pks/add', implode("\r\n", $cmd), $timeout);
+    }
+
+    /**
+     * Returns the first matching key ID for an email address from a
+     * public keyserver.
+     *
+     * @param string $address  The email address of the PGP key.
+     * @param string $server   The keyserver to use.
+     * @param float $timeout   The keyserver timeout.
+     *
+     * @return string  The PGP key ID.
+     * @throws Horde_Exception
+     */
+    public function getKeyID($address, $server = self::KEYSERVER_PUBLIC,
+                             $timeout = self::KEYSERVER_TIMEOUT)
+    {
+        /* Connect to the public keyserver. */
+        $uri = '/pks/lookup?op=index&options=mr&search=' . urlencode($address);
+        $output = $this->_connectKeyserver('GET', $server, $uri, '', $timeout);
+
+        if (($start = strstr($output, '-----BEGIN PGP PUBLIC KEY BLOCK'))) {
+            /* The server returned the matching key immediately. */
+            $length = strpos($start, '-----END PGP PUBLIC KEY BLOCK') + 34;
+            $info = $this->pgpPacketInformation(substr($start, 0, $length));
+            if (!empty($info['keyid']) &&
+                (empty($info['public_key']['expires']) ||
+                 $info['public_key']['expires'] > time())) {
+                return $info['keyid'];
+            }
+        } elseif (strpos($output, 'pub:') !== false) {
+            $output = explode("\n", $output);
+            $keyids = array();
+            foreach ($output as $line) {
+                if (substr($line, 0, 4) == 'pub:') {
+                    $line = explode(':', $line);
+                    /* Ignore invalid lines and expired keys. */
+                    if (count($line) != 7 ||
+                        (!empty($line[5]) && $line[5] <= time())) {
+                        continue;
+                    }
+                    $keyids[$line[4]] = $line[1];
+                }
+            }
+            /* Sort by timestamp to use the newest key. */
+            if (count($keyids)) {
+                ksort($keyids);
+                return array_pop($keyids);
+            }
+        }
+
+        throw new Horde_Exception(_("Could not obtain public key from the keyserver."));
+    }
+
+    /**
+     * Get the fingerprints from a key block.
+     *
+     * @param string $pgpdata  The PGP data block.
+     *
+     * @return array The fingerprints in $pgpdata indexed by key id.
+     */
+    public function getFingerprintsFromKey($pgpdata)
+    {
+        $fingerprints = array();
+
+        /* Store the key in a temporary keyring. */
+        $keyring = $this->_putInKeyring($pgpdata);
+
+        /* Options for the GPG binary. */
+        $cmdline = array(
+            '--fingerprint',
+            $keyring,
+        );
+
+        $result = $this->_callGpg($cmdline, 'r');
+        if (!$result || !$result->stdout) {
+            return $fingerprints;
+        }
+
+        /* Parse fingerprints and key ids from output. */
+        $lines = explode("\n", $result->stdout);
+        $keyid = null;
+        foreach ($lines as $line) {
+            if (preg_match('/pub\s+\w+\/(\w{8})/', $line, $matches)) {
+                $keyid = '0x' . $matches[1];
+            } elseif ($keyid && preg_match('/^\s+[\s\w]+=\s*([\w\s]+)$/m', $line, $matches)) {
+                $fingerprints[$keyid] = trim($matches[1]);
+                $keyid = null;
+            }
+        }
+
+        return $fingerprints;
+    }
+
+    /**
+     * Connects to a public key server via HKP (Horrowitz Keyserver Protocol).
+     *
+     * @param string $method   POST, GET, etc.
+     * @param string $server   The keyserver to use.
+     * @param string $uri      The URI to access (relative to the server).
+     * @param string $command  The PGP command to run.
+     * @param float $timeout   The timeout value.
+     *
+     * @return string  The text from standard output on success.
+     * @throws Horde_Exception
+     */
+    protected function _connectKeyserver($method, $server, $resource,
+                                         $command, $timeout)
+    {
+        $connRefuse = 0;
+        $output = '';
+
+        $port = '11371';
+        if (!empty($GLOBALS['conf']['http']['proxy']['proxy_host'])) {
+            $resource = 'http://' . $server . ':' . $port . $resource;
+
+            $server = $GLOBALS['conf']['http']['proxy']['proxy_host'];
+            if (!empty($GLOBALS['conf']['http']['proxy']['proxy_port'])) {
+                $port = $GLOBALS['conf']['http']['proxy']['proxy_port'];
+            } else {
+                $port = 80;
+            }
+        }
+
+        $command = $method . ' ' . $resource . ' HTTP/1.0' . ($command ? "\r\n" . $command : '');
+
+        /* Attempt to get the key from the keyserver. */
+        do {
+            $errno = $errstr = null;
+
+            /* The HKP server is located on port 11371. */
+            $fp = @fsockopen($server, $port, $errno, $errstr, $timeout);
+            if ($fp) {
+                fputs($fp, $command . "\n\n");
+                while (!feof($fp)) {
+                    $output .= fgets($fp, 1024);
+                }
+                fclose($fp);
+                return $output;
+            }
+        } while (++$connRefuse < self::KEYSERVER_REFUSE);
+
+        if ($errno == 0) {
+            throw new Horde_Exception(_("Connection refused to the public keyserver."), 'horde.error');
+        } else {
+            throw new Horde_Exception(sprintf(_("Connection refused to the public keyserver. Reason: %s (%s)"), String::convertCharset($errstr, NLS::getExternalCharset()), $errno), 'horde.error');
+        }
+    }
+
+    /**
+     * Encrypts text using PGP.
+     *
+     * @param string $text   The text to be PGP encrypted.
+     * @param array $params  The parameters needed for encryption.
+     *                       See the individual _encrypt*() functions for the
+     *                       parameter requirements.
+     *
+     * @return string  The encrypted message.
+     * @throws Horde_Exception
+     */
+    public function encrypt($text, $params = array())
+    {
+        if (isset($params['type'])) {
+            if ($params['type'] === 'message') {
+                return $this->_encryptMessage($text, $params);
+            } elseif ($params['type'] === 'signature') {
+                return $this->_encryptSignature($text, $params);
+            }
+        }
+    }
+
+    /**
+     * Decrypts text using PGP.
+     *
+     * @param string $text   The text to be PGP decrypted.
+     * @param array $params  The parameters needed for decryption.
+     *                       See the individual _decrypt*() functions for the
+     *                       parameter requirements.
+     *
+     * @return stdClass  An object with the following properties:
+     * <pre>
+     * 'message' - (string) The signature result text.
+     * 'result' - (boolean) The result of the signature test.
+     * </pre>
+     * @throws Horde_Exception
+     */
+    public function decrypt($text, $params = array())
+    {
+        if (isset($params['type'])) {
+            if ($params['type'] === 'message') {
+                return $this->_decryptMessage($text, $params);
+            } elseif (($params['type'] === 'signature') ||
+                      ($params['type'] === 'detached-signature')) {
+                return $this->_decryptSignature($text, $params);
+            }
+        }
+    }
+
+    /**
+     * Returns whether a text has been encrypted symmetrically.
+     *
+     * @param string $text  The PGP encrypted text.
+     *
+     * @return boolean  True if the text is symmetricallly encrypted.
+     */
+    public function encryptedSymmetrically($text)
+    {
+        $cmdline = array(
+            '--decrypt',
+            '--batch'
+        );
+        $result = $this->_callGpg($cmdline, 'w', $text, true, true, true);
+        return strpos($result->stderr, 'gpg: encrypted with 1 passphrase') !== false;
+    }
+
+    /**
+     * Creates a temporary gpg keyring.
+     *
+     * @param string $type  The type of key to analyze. Either 'public'
+     *                      (Default) or 'private'
+     *
+     * @return string  Command line keystring option to use with gpg program.
+     */
+    protected function _createKeyring($type = 'public')
+    {
+        $type = String::lower($type);
+
+        if ($type === 'public') {
+            if (empty($this->_publicKeyring)) {
+                $this->_publicKeyring = $this->_createTempFile('horde-pgp');
+            }
+            return '--keyring ' . $this->_publicKeyring;
+        } elseif ($type === 'private') {
+            if (empty($this->_privateKeyring)) {
+                $this->_privateKeyring = $this->_createTempFile('horde-pgp');
+            }
+            return '--secret-keyring ' . $this->_privateKeyring;
+        }
+    }
+
+    /**
+     * Adds PGP keys to the keyring.
+     *
+     * @param mixed $keys   A single key or an array of key(s) to add to the
+     *                      keyring.
+     * @param string $type  The type of key(s) to add. Either 'public'
+     *                      (Default) or 'private'
+     *
+     * @return string  Command line keystring option to use with gpg program.
+     */
+    protected function _putInKeyring($keys = array(), $type = 'public')
+    {
+        $type = String::lower($type);
+
+        if (!is_array($keys)) {
+            $keys = array($keys);
+        }
+
+        /* Create the keyrings if they don't already exist. */
+        $keyring = $this->_createKeyring($type);
+
+        /* Store the key(s) in the keyring. */
+        $cmdline = array(
+            '--allow-secret-key-import',
+            '--fast-import',
+            $keyring
+        );
+        $this->_callGpg($cmdline, 'w', array_values($keys));
+
+        return $keyring;
+    }
+
+    /**
+     * Encrypts a message in PGP format using a public key.
+     *
+     * @param string $text   The text to be encrypted.
+     * @param array $params  The parameters needed for encryption.
+     * <pre>
+     * Parameters:
+     * ===========
+     * 'type'       => 'message' (REQUIRED)
+     * 'symmetric'  => Whether to use symmetric instead of asymmetric
+     *                 encryption (defaults to false)
+     * 'recips'     => An array with the e-mail address of the recipient as
+     *                 the key and that person's public key as the value.
+     *                 (REQUIRED if 'symmetric' is false)
+     * 'passphrase' => The passphrase for the symmetric encryption (REQUIRED if
+     *                 'symmetric' is true)
+     * </pre>
+     *
+     * @return string  The encrypted message.
+     * @throws Horde_Exception
+     */
+    protected function _encryptMessage($text, $params)
+    {
+        /* Create temp files for input. */
+        $input = $this->_createTempFile('horde-pgp');
+        file_put_contents($input, $text);
+
+        /* Build command line. */
+        $cmdline = array(
+            '--armor',
+            '--batch',
+            '--always-trust'
+        );
+        if (empty($params['symmetric'])) {
+            /* Store public key in temporary keyring. */
+            $keyring = $this->_putInKeyring(array_values($params['recips']));
+
+            $cmdline[] = $keyring;
+            $cmdline[] = '--encrypt';
+            foreach (array_keys($params['recips']) as $val) {
+                $cmdline[] = '--recipient ' . $val;
+            }
+        } else {
+            $cmdline[] = '--symmetric';
+            $cmdline[] = '--passphrase-fd 0';
+        }
+        $cmdline[] = $input;
+
+        /* Encrypt the document. */
+        $result = $this->_callGpg($cmdline, 'w', empty($params['symmetric']) ? null : $params['passphrase'], true, true);
+        if (empty($result->output)) {
+            $error = preg_replace('/\n.*/', '', $result->stderr);
+            throw new Horde_Exception(_("Could not PGP encrypt message: ") . $error, 'horde.error');
+        }
+
+        return $result->output;
+    }
+
+    /**
+     * Signs a message in PGP format using a private key.
+     *
+     * @param string $text   The text to be signed.
+     * @param array $params  The parameters needed for signing.
+     * <pre>
+     * Parameters:
+     * ===========
+     * 'type'        =>  'signature' (REQUIRED)
+     * 'pubkey'      =>  PGP public key. (REQUIRED)
+     * 'privkey'     =>  PGP private key. (REQUIRED)
+     * 'passphrase'  =>  Passphrase for PGP Key. (REQUIRED)
+     * 'sigtype'     =>  Determine the signature type to use. (Optional)
+     *                   'cleartext'  --  Make a clear text signature
+     *                   'detach'     --  Make a detached signature (DEFAULT)
+     * </pre>
+     *
+     * @return string  The signed message.
+     * @throws Horde_Exception
+     */
+    protected function _encryptSignature($text, $params)
+    {
+        /* Check for required parameters. */
+        if (!isset($params['pubkey']) ||
+            !isset($params['privkey']) ||
+            !isset($params['passphrase'])) {
+            throw new Horde_Exception(_("A public PGP key, private PGP key, and passphrase are required to sign a message."), 'horde.error');
+        }
+
+        /* Create temp files for input. */
+        $input = $this->_createTempFile('horde-pgp');
+
+        /* Encryption requires both keyrings. */
+        $pub_keyring = $this->_putInKeyring(array($params['pubkey']));
+        $sec_keyring = $this->_putInKeyring(array($params['privkey']), 'private');
+
+        /* Store message in temporary file. */
+        file_put_contents($input, $text);
+
+        /* Determine the signature type to use. */
+        $cmdline = array();
+        if (isset($params['sigtype']) &&
+            $params['sigtype'] == 'cleartext') {
+            $sign_type = '--clearsign';
+        } else {
+            $sign_type = '--detach-sign';
+        }
+
+        /* Additional GPG options. */
+        $cmdline += array(
+            '--armor',
+            '--batch',
+            '--passphrase-fd 0',
+            $sec_keyring,
+            $pub_keyring,
+            $sign_type,
+            $input
+        );
+
+        /* Sign the document. */
+        $result = $this->_callGpg($cmdline, 'w', $params['passphrase'], true, true);
+        if (empty($result->output)) {
+            $error = preg_replace('/\n.*/', '', $result->stderr);
+            throw new Horde_Exception(_("Could not PGP sign message: ") . $error, 'horde.error');
+        }
+
+        return $result->output;
+    }
+
+    /**
+     * Decrypts an PGP encrypted message using a private/public keypair and a
+     * passhprase.
+     *
+     * @param string $text   The text to be decrypted.
+     * @param array $params  The parameters needed for decryption.
+     * <pre>
+     * Parameters:
+     * ===========
+     * 'type'        =>  'message' (REQUIRED)
+     * 'pubkey'      =>  PGP public key. (REQUIRED for asymmetric encryption)
+     * 'privkey'     =>  PGP private key. (REQUIRED for asymmetric encryption)
+     * 'passphrase'  =>  Passphrase for PGP Key. (REQUIRED)
+     * </pre>
+     *
+     * @return stdClass  An object with the following properties:
+     * <pre>
+     * 'message'     -  The decrypted message.
+     * 'sig_result'  -  The result of the signature test.
+     * </pre>
+     * @return stdClass  An object with the following properties:
+     * <pre>
+     * 'message' - (string) The signature result text.
+     * 'result' - (boolean) The result of the signature test.
+     * </pre>
+     * @throws Horde_Exception
+     */
+    protected function _decryptMessage($text, $params)
+    {
+        $good_sig_flag = false;
+
+        /* Check for required parameters. */
+        if (!isset($params['passphrase']) && empty($params['no_passphrase'])) {
+            throw new Horde_Exception(_("A passphrase is required to decrypt a message."), 'horde.error');
+        }
+
+        /* Create temp files. */
+        $input = $this->_createTempFile('horde-pgp');
+
+        /* Store message in file. */
+        file_put_contents($input, $text);
+
+        /* Build command line. */
+        $cmdline = array(
+            '--always-trust',
+            '--armor',
+            '--batch'
+        );
+        if (empty($param['no_passphrase'])) {
+            $cmdline[] = '--passphrase-fd 0';
+        }
+        if (!empty($params['pubkey']) && !empty($params['privkey'])) {
+            /* Decryption requires both keyrings. */
+            $pub_keyring = $this->_putInKeyring(array($params['pubkey']));
+            $sec_keyring = $this->_putInKeyring(array($params['privkey']), 'private');
+            $cmdline[] = $sec_keyring;
+            $cmdline[] = $pub_keyring;
+        }
+        $cmdline[] = '--decrypt';
+        $cmdline[] = $input;
+
+        /* Decrypt the document now. */
+        if (empty($params['no_passphrase'])) {
+            $result = $this->_callGpg($cmdline, 'w', $params['passphrase'], true, true);
+        } else {
+            $result = $this->_callGpg($cmdline, 'r', null, true, true);
+        }
+        if (empty($result->output)) {
+            $error = preg_replace('/\n.*/', '', $result->stderr);
+            throw new Horde_Exception(_("Could not decrypt PGP data: ") . $error, 'horde.error');
+        }
+
+        /* Create the return object. */
+        return $this->_checkSignatureResult($result->stderr, $result->output);
+    }
+
+    /**
+     * Decrypts an PGP signed message using a public key.
+     *
+     * @param string $text   The text to be verified.
+     * @param array $params  The parameters needed for verification.
+     * <pre>
+     * Parameters:
+     * ===========
+     * 'type'       =>  'signature' or 'detached-signature' (REQUIRED)
+     * 'pubkey'     =>  PGP public key. (REQUIRED)
+     * 'signature'  =>  PGP signature block. (REQUIRED for detached signature)
+     * </pre>
+     *
+     * @return stdClass  An object with the following properties:
+     * <pre>
+     * 'message' - (string) The signature result text.
+     * 'result' - (boolean) The result of the signature test.
+     * </pre>
+     * @throws Horde_Exception
+     */
+    protected function _decryptSignature($text, $params)
+    {
+        /* Check for required parameters. */
+        if (!isset($params['pubkey'])) {
+            throw new Horde_Exception(_("A public PGP key is required to verify a signed message."), 'horde.error');
+        }
+        if (($params['type'] === 'detached-signature') &&
+            !isset($params['signature'])) {
+            throw new Horde_Exception(_("The detached PGP signature block is required to verify the signed message."), 'horde.error');
+        }
+
+        $good_sig_flag = 0;
+
+        /* Create temp files for input. */
+        $input = $this->_createTempFile('horde-pgp');
+
+        /* Store public key in temporary keyring. */
+        $keyring = $this->_putInKeyring($params['pubkey']);
+
+        /* Store the message in a temporary file. */
+        file_put_contents($input, $text);
+
+        /* Options for the GPG binary. */
+        $cmdline = array(
+            '--armor',
+            '--always-trust',
+            '--batch',
+            '--charset ' . NLS::getCharset(),
+            $keyring,
+            '--verify'
+        );
+
+        /* Extra stuff to do if we are using a detached signature. */
+        if ($params['type'] === 'detached-signature') {
+            $sigfile = $this->_createTempFile('horde-pgp');
+            $cmdline[] = $sigfile . ' ' . $input;
+            file_put_contents($sigfile, $params['signature']);
+        } else {
+            $cmdline[] = $input;
+        }
+
+        /* Verify the signature.  We need to catch standard error output,
+         * since this is where the signature information is sent. */
+        $result = $this->_callGpg($cmdline, 'r', null, true, true, true);
+        return $this->_checkSignatureResult($result->stderr, $result->stderr);
+    }
+
+    /**
+     * Checks signature result from the GnuPG binary.
+     *
+     * @param string $result   The signature result.
+     * @param string $message  The decrypted message data.
+     *
+     * @return stdClass  An object with the following properties:
+     * <pre>
+     * 'message' - (string) The signature result text.
+     * 'result' - (boolean) The result of the signature test.
+     * </pre>
+     * @throws Horde_Exception
+     */
+    protected function _checkSignatureResult($result, $message = null)
+    {
+        /* Good signature:
+         *   gpg: Good signature from "blah blah blah (Comment)"
+         * Bad signature:
+         *   gpg: BAD signature from "blah blah blah (Comment)" */
+        if (strpos($result, 'gpg: BAD signature') !== false) {
+            throw new Horde_Exception($result, 'horde.error');
+        }
+
+        $ob = new stdClass;
+        $ob->message = $message;
+        $ob->result = (strpos($result, 'gpg: Good signature') !== false);
+
+        return $ob;
+    }
+
+    /**
+     * Signs a MIME part using PGP.
+     *
+     * @param Horde_Mime_Part $mime_part  The object to sign.
+     * @param array $params               The parameters required for signing.
+     *                                    @see _encryptSignature().
+     *
+     * @return mixed  A Horde_Mime_Part object that is signed according to RFC
+     *                3156.
+     * @throws Horde_Exception
+     */
+    public function signMIMEPart($mime_part, $params = array())
+    {
+        $params = array_merge($params, array('type' => 'signature', 'sigtype' => 'detach'));
+
+        /* RFC 3156 Requirements for a PGP signed message:
+         * + Content-Type params 'micalg' & 'protocol' are REQUIRED.
+         * + The digitally signed message MUST be constrained to 7 bits.
+         * + The MIME headers MUST be a part of the signed data. */
+
+        $mime_part->strict7bit(true);
+        $msg_sign = $this->encrypt($mime_part->toCanonicalString(), $params);
+
+        /* Add the PGP signature. */
+        $charset = NLS::getEmailCharset();
+        $pgp_sign = new Horde_Mime_Part();
+        $pgp_sign->setType('application/pgp-signature');
+        $pgp_sign->setCharset($charset);
+        $pgp_sign->setDisposition('inline');
+        $pgp_sign->setDescription(String::convertCharset(_("PGP Digital Signature"), NLS::getCharset(), $charset));
+        $pgp_sign->setContents($msg_sign);
+
+        /* Get the algorithim information from the signature. Since we are
+         * analyzing a signature packet, we need to use the special keyword
+         * '_SIGNATURE' - see Horde_Crypt_Pgp. */
+        $sig_info = $this->pgpPacketSignature($msg_sign, '_SIGNATURE');
+
+        /* Setup the multipart MIME Part. */
+        $part = new Horde_Mime_Part();
+        $part->setType('multipart/signed');
+        $part->setContents('This message is in MIME format and has been PGP signed.' . "\n");
+        $part->addPart($mime_part);
+        $part->addPart($pgp_sign);
+        $part->setContentTypeParameter('protocol', 'application/pgp-signature');
+        $part->setContentTypeParameter('micalg', $sig_info['micalg']);
+
+        return $part;
+    }
+
+    /**
+     * Encrypts a MIME part using PGP.
+     *
+     * @param Horde_Mime_Part $mime_part  The object to encrypt.
+     * @param array $params               The parameters required for
+     *                                    encryption.
+     *                                    @see _encryptMessage().
+     *
+     * @return mixed  A Horde_Mime_Part object that is encrypted according to
+     *                RFC 3156.
+     * @throws Horde_Exception
+     */
+    public function encryptMIMEPart($mime_part, $params = array())
+    {
+        $params = array_merge($params, array('type' => 'message'));
+
+        $signenc_body = $mime_part->toCanonicalString();
+        $message_encrypt = $this->encrypt($signenc_body, $params);
+
+        /* Set up MIME Structure according to RFC 3156. */
+        $charset = NLS::getEmailCharset();
+        $part = new Horde_Mime_Part();
+        $part->setType('multipart/encrypted');
+        $part->setCharset($charset);
+        $part->setContentTypeParameter('protocol', 'application/pgp-encrypted');
+        $part->setDescription(String::convertCharset(_("PGP Encrypted Data"), NLS::getCharset(), $charset));
+        $part->setContents('This message is in MIME format and has been PGP encrypted.' . "\n");
+
+        $part1 = new Horde_Mime_Part();
+        $part1->setType('application/pgp-encrypted');
+        $part1->setCharset(null);
+        $part1->setContents("Version: 1\n");
+        $part->addPart($part1);
+
+        $part2 = new Horde_Mime_Part();
+        $part2->setType('application/octet-stream');
+        $part2->setCharset(null);
+        $part2->setContents($message_encrypt);
+        $part2->setDisposition('inline');
+        $part->addPart($part2);
+
+        return $part;
+    }
+
+    /**
+     * Signs and encrypts a MIME part using PGP.
+     *
+     * @param Horde_Mime_Part $mime_part   The object to sign and encrypt.
+     * @param array $sign_params           The parameters required for
+     *                                     signing. @see _encryptSignature().
+     * @param array $encrypt_params        The parameters required for
+     *                                     encryption. @see _encryptMessage().
+     *
+     * @return mixed  A Horde_Mime_Part object that is signed and encrypted
+     *                according to RFC 3156.
+     * @throws Horde_Exception
+     */
+    public function signAndEncryptMIMEPart($mime_part, $sign_params = array(),
+                                           $encrypt_params = array())
+    {
+        /* RFC 3156 requires that the entire signed message be encrypted.  We
+         * need to explicitly call using Horde_Crypt_Pgp:: because we don't
+         * know whether a subclass has extended these methods. */
+        $part = $this->signMIMEPart($mime_part, $sign_params);
+        $part = $this->encryptMIMEPart($part, $encrypt_params);
+        $part->setContents('This message is in MIME format and has been PGP signed and encrypted.' . "\n");
+
+        $charset = NLS::getEmailCharset();
+        $part->setCharset($charset);
+        $part->setDescription(String::convertCharset(_("PGP Signed/Encrypted Data"), NLS::getCharset(), $charset));
+
+        return $part;
+    }
+
+    /**
+     * Generates a Horde_Mime_Part object, in accordance with RFC 3156, that
+     * contains a public key.
+     *
+     * @param string $key  The public key.
+     *
+     * @return Horde_Mime_Part  An object that contains the public key.
+     */
+    public function publicKeyMIMEPart($key)
+    {
+        $charset = NLS::getEmailCharset();
+        $part = new Horde_Mime_Part();
+        $part->setType('application/pgp-keys');
+        $part->setCharset($charset);
+        $part->setDescription(String::convertCharset(_("PGP Public Key"), NLS::getCharset(), $charset));
+        $part->setContents($key);
+
+        return $part;
+    }
+
+    /**
+     * Function that handles interfacing with the GnuPG binary.
+     *
+     * @param array $options    Options and commands to pass to GnuPG.
+     * @param string $mode      'r' to read from stdout, 'w' to write to
+     *                          stdin.
+     * @param array $input      Input to write to stdin.
+     * @param boolean $output   Collect and store output in object returned?
+     * @param boolean $stderr   Collect and store stderr in object returned?
+     * @param boolean $verbose  Run GnuPG with verbose flag?
+     *
+     * @return stdClass  Class with members output, stderr, and stdout.
+     */
+    protected function _callGpg($options, $mode, $input = array(),
+                                $output = false, $stderr = false,
+                                $verbose = false)
+    {
+        $data = new stdClass;
+        $data->output = null;
+        $data->stderr = null;
+        $data->stdout = null;
+
+        /* Verbose output? */
+        if (!$verbose) {
+            array_unshift($options, '--quiet');
+        }
+
+        /* Create temp files for output. */
+        if ($output) {
+            $output_file = $this->_createTempFile('horde-pgp', false);
+            array_unshift($options, '--output ' . $output_file);
+
+            /* Do we need standard error output? */
+            if ($stderr) {
+                $stderr_file = $this->_createTempFile('horde-pgp', false);
+                $options[] = '2> ' . $stderr_file;
+            }
+        }
+
+        /* Silence errors if not requested. */
+        if (!$output || !$stderr) {
+            $options[] = '2> /dev/null';
+        }
+
+        /* Build the command line string now. */
+        $cmdline = implode(' ', array_merge($this->_gnupg, $options));
+
+        if ($mode == 'w') {
+            $fp = popen($cmdline, 'w');
+            $win32 = !strncasecmp(PHP_OS, 'WIN', 3);
+
+            if (!is_array($input)) {
+                $input = array($input);
+            }
+            foreach ($input as $line) {
+                if ($win32 && (strpos($line, "\x0d\x0a") !== false)) {
+                    $chunks = explode("\x0d\x0a", $line);
+                    foreach ($chunks as $chunk) {
+                        fputs($fp, $chunk . "\n");
+                    }
+                } else {
+                    fputs($fp, $line . "\n");
+                }
+            }
+        } elseif ($mode == 'r') {
+            $fp = popen($cmdline, 'r');
+            while (!feof($fp)) {
+                $data->stdout .= fgets($fp, 1024);
+            }
+        }
+        pclose($fp);
+
+        if ($output) {
+            $data->output = file_get_contents($output_file);
+            unlink($output_file);
+            if ($stderr) {
+                $data->stderr = file_get_contents($stderr_file);
+                unlink($stderr_file);
+            }
+        }
+
+        return $data;
+    }
+
+    /**
+     * Generates a revocation certificate.
+     *
+     * @param string $key         The private key.
+     * @param string $email       The email to use for the key.
+     * @param string $passphrase  The passphrase to use for the key.
+     *
+     * @return string  The revocation certificate.
+     * @throws Horde_Exception
+     */
+    public function generateRevocation($key, $email, $passphrase)
+    {
+        $keyring = $this->_putInKeyring($key, 'private');
+
+        /* Prepare the canned answers. */
+        $input = array(
+            'y', // Really generate a revocation certificate
+            '0', // Refuse to specify a reason
+            '',  // Empty comment
+            'y', // Confirm empty comment
+        );
+        if (!empty($passphrase)) {
+            $input[] = $passphrase;
+        }
+
+        /* Run through gpg binary. */
+        $cmdline = array(
+            $keyring,
+            '--command-fd 0',
+            '--gen-revoke' . ' ' . $email,
+        );
+        $results = $this->_callGpg($cmdline, 'w', $input, true);
+
+        /* If the key is empty, something went wrong. */
+        if (empty($results->output)) {
+            throw new Horde_Exception(_("Revocation key not generated successfully."), 'horde.error');
+        }
+
+        return $results->output;
+    }
+
+}
diff --git a/framework/Crypt/lib/Horde/Crypt/Smime.php b/framework/Crypt/lib/Horde/Crypt/Smime.php
new file mode 100644 (file)
index 0000000..ec1cd74
--- /dev/null
@@ -0,0 +1,1302 @@
+<?php
+/**
+ * Horde_Crypt_Smime:: provides a framework for Horde applications to
+ * interact with the OpenSSL library and implement S/MIME.
+ *
+ * Copyright 2002-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ *
+ * @author  Mike Cochrane <mike@graftonhall.co.nz>
+ * @package Horde_Crypt
+ */
+class Horde_Crypt_Smime extends Horde_Crypt
+{
+    /**
+     * Object Identifers to name array.
+     *
+     * @var array
+     */
+    protected $_oids = array(
+        '2.5.4.3' => 'CommonName',
+        '2.5.4.4' => 'Surname',
+        '2.5.4.6' => 'Country',
+        '2.5.4.7' => 'Location',
+        '2.5.4.8' => 'StateOrProvince',
+        '2.5.4.9' => 'StreetAddress',
+        '2.5.4.10' => 'Organisation',
+        '2.5.4.11' => 'OrganisationalUnit',
+        '2.5.4.12' => 'Title',
+        '2.5.4.20' => 'TelephoneNumber',
+        '2.5.4.42' => 'GivenName',
+
+        '2.5.29.14' => 'id-ce-subjectKeyIdentifier',
+
+        '2.5.29.14' => 'id-ce-subjectKeyIdentifier',
+        '2.5.29.15' => 'id-ce-keyUsage',
+        '2.5.29.17' => 'id-ce-subjectAltName',
+        '2.5.29.19' => 'id-ce-basicConstraints',
+        '2.5.29.31' => 'id-ce-CRLDistributionPoints',
+        '2.5.29.32' => 'id-ce-certificatePolicies',
+        '2.5.29.35' => 'id-ce-authorityKeyIdentifier',
+        '2.5.29.37' => 'id-ce-extKeyUsage',
+
+        '1.2.840.113549.1.9.1' => 'Email',
+        '1.2.840.113549.1.1.1' => 'RSAEncryption',
+        '1.2.840.113549.1.1.2' => 'md2WithRSAEncryption',
+        '1.2.840.113549.1.1.4' => 'md5withRSAEncryption',
+        '1.2.840.113549.1.1.5' => 'SHA-1WithRSAEncryption',
+        '1.2.840.10040.4.3' => 'id-dsa-with-sha-1',
+
+        '1.3.6.1.5.5.7.3.2' => 'id_kp_clientAuth',
+
+        '2.16.840.1.113730.1.1' => 'netscape-cert-type',
+        '2.16.840.1.113730.1.2' => 'netscape-base-url',
+        '2.16.840.1.113730.1.3' => 'netscape-revocation-url',
+        '2.16.840.1.113730.1.4' => 'netscape-ca-revocation-url',
+        '2.16.840.1.113730.1.7' => 'netscape-cert-renewal-url',
+        '2.16.840.1.113730.1.8' => 'netscape-ca-policy-url',
+        '2.16.840.1.113730.1.12' => 'netscape-ssl-server-name',
+        '2.16.840.1.113730.1.13' => 'netscape-comment',
+    );
+
+    /**
+     * Constructor.
+     *
+     * @param array $params  Parameter array.
+     *                       'temp' => Location of temporary directory.
+     */
+    protected function __construct($params)
+    {
+        $this->_tempdir = $params['temp'];
+    }
+
+    /**
+     * Verify a passphrase for a given private key.
+     *
+     * @param string $private_key  The user's private key.
+     * @param string $passphrase   The user's passphrase.
+     *
+     * @return boolean  Returns true on valid passphrase, false on invalid
+     *                  passphrase.
+     */
+    public function verifyPassphrase($private_key, $passphrase)
+    {
+        $res = is_null($passphrase)
+            ? openssl_pkey_get_private($private_key)
+            : openssl_pkey_get_private($private_key, $passphrase);
+
+        return is_resource($res);
+    }
+
+    /**
+     * Encrypt text using S/MIME.
+     *
+     * @param string $text   The text to be encrypted.
+     * @param array $params  The parameters needed for encryption.
+     *                       See the individual _encrypt*() functions for
+     *                       the parameter requirements.
+     *
+     * @return string  The encrypted message.
+     * @throws Horde_Exception
+     */
+    public function encrypt($text, $params = array())
+    {
+        /* Check for availability of OpenSSL PHP extension. */
+        $this->checkForOpenSSL();
+
+        if (isset($params['type'])) {
+            if ($params['type'] === 'message') {
+                return $this->_encryptMessage($text, $params);
+            } elseif ($params['type'] === 'signature') {
+                return $this->_encryptSignature($text, $params);
+            }
+        }
+    }
+
+    /**
+     * Decrypt text via S/MIME.
+     *
+     * @param string $text   The text to be smime decrypted.
+     * @param array $params  The parameters needed for decryption.
+     *                       See the individual _decrypt*() functions for
+     *                       the parameter requirements.
+     *
+     * @return string  The decrypted message.
+     * @throws Horde_Exception
+     */
+    public function decrypt($text, $params = array())
+    {
+        /* Check for availability of OpenSSL PHP extension. */
+        $this->checkForOpenSSL();
+
+        if (isset($params['type'])) {
+            if ($params['type'] === 'message') {
+                return $this->_decryptMessage($text, $params);
+            } elseif (($params['type'] === 'signature') ||
+                      ($params['type'] === 'detached-signature')) {
+                return $this->_decryptSignature($text, $params);
+            }
+        }
+    }
+
+    /**
+     * Verify a signature using via S/MIME.
+     *
+     * @param string $text  The multipart/signed data to be verified.
+     * @param mixed $certs  Either a single or array of root certificates.
+     *
+     * @return stdClass  Object with the following elements:
+     *                   'result' -> Returns true on success.
+     *                   'cert' -> The certificate of the signer stored
+     *                             in the message (in PEM format).
+     *                   'email' -> The email of the signing person.
+     * @throws Horde_Exception
+     */
+    public function verify($text, $certs)
+    {
+        /* Check for availability of OpenSSL PHP extension. */
+        $openssl = $this->checkForOpenSSL();
+
+        /* Create temp files for input/output. */
+        $input = $this->_createTempFile('horde-smime');
+        $output = $this->_createTempFile('horde-smime');
+
+        /* Write text to file */
+        file_put_contents($input, $text);
+        unset($text);
+
+        $root_certs = array();
+        if (!is_array($certs)) {
+            $certs = array($certs);
+        }
+        foreach ($certs as $file) {
+            if (file_exists($file)) {
+                $root_certs[] = $file;
+            }
+        }
+
+        $ob = new stdClass;
+
+        if (!empty($root_certs)) {
+            $result = openssl_pkcs7_verify($input, 0, $output, $root_certs);
+            /* Message verified */
+            if ($result === true) {
+                $ob->result = true;
+                $ob->cert = file_get_contents($output);
+                $ob->email = $this->getEmailFromKey($ob->cert);
+                return $ob;
+            }
+        }
+
+        /* Try again without verfying the signer's cert */
+        $result = openssl_pkcs7_verify($input, PKCS7_NOVERIFY, $output);
+
+        if ($result === true) {
+            throw new Horde_Exception(_("Message Verified Successfully but the signer's certificate could not be verified."), 'horde.warning');
+        } elseif ($result == -1) {
+            throw new Horde_Exception(_("Verification failed - an unknown error has occurred."), 'horde.error');
+        } else {
+            throw new Horde_Exception(_("Verification failed - this message may have been tampered with."), 'horde.error');
+        }
+
+        $ob->cert = file_get_contents($output);
+        $ob->email = $this->getEmailFromKey($ob->cert);
+
+        return $ob;
+    }
+
+    /**
+     * Extract the contents from signed S/MIME data.
+     *
+     * @param string $data     The signed S/MIME data.
+     * @param string $sslpath  The path to the OpenSSL binary.
+     *
+     * @return string  The contents embedded in the signed data.
+     * @throws Horde_Exception
+     */
+    public function extractSignedContents($data, $sslpath)
+    {
+        /* Check for availability of OpenSSL PHP extension. */
+        $this->checkForOpenSSL();
+
+        /* Create temp files for input/output. */
+        $input = $this->_createTempFile('horde-smime');
+        $output = $this->_createTempFile('horde-smime');
+
+        /* Write text to file. */
+        file_put_contents($input, $data);
+        unset($data);
+
+        exec($sslpath . ' smime -verify -noverify -nochain -in ' . $input . ' -out ' . $output);
+
+        $ret = file_get_contents($output);
+        if ($ret) {
+            return $ret;
+        }
+
+        throw new Horde_Exception(_("OpenSSL error: Could not extract data from signed S/MIME part."), 'horde.error');
+    }
+
+    /**
+     * Sign a MIME part using S/MIME.
+     *
+     * @param Horde_Mime_Part $mime_part  The object to sign.
+     * @param array $params               The parameters required for signing.
+     *
+     * @return mixed  A Horde_Mime_Part object that is signed.
+     * @throws Horde_Exception
+     */
+    public function signMIMEPart($mime_part, $params)
+    {
+        /* Sign the part as a message */
+        $message = $this->encrypt($mime_part->toCanonicalString(), $params);
+
+        /* Break the result into its components */
+        $mime_message = Horde_Mime_Part::parseMessage($message);
+
+        $smime_sign = $mime_message->getPart('2');
+        $smime_sign->setDescription(_("S/MIME Cryptographic Signature"));
+        $smime_sign->transferDecodeContents();
+        $smime_sign->setTransferEncoding('base64');
+
+        $smime_part = new Horde_Mime_Part();
+        $smime_part->setType('multipart/signed');
+        $smime_part->setContents('This is a cryptographically signed message in MIME format.' . "\n");
+        $smime_part->setContentTypeParameter('protocol', 'application/pkcs7-signature');
+        $smime_part->setContentTypeParameter('micalg', 'sha1');
+        $smime_part->addPart($mime_part);
+        $smime_part->addPart($smime_sign);
+
+        return $smime_part;
+    }
+
+    /**
+     * Encrypt a MIME part using S/MIME.
+     *
+     * @param Horde_Mime_Part $mime_part  The object to encrypt.
+     * @param array $params               The parameters required for
+     *                                    encryption.
+     *
+     * @return mixed  A Horde_Mime_Part object that is encrypted.
+     * @throws Horde_Exception
+     */
+    public function encryptMIMEPart($mime_part, $params = array())
+    {
+        /* Sign the part as a message */
+        $message = $this->encrypt($mime_part->toCanonicalString(), $params);
+
+        /* Get charset for mime part description. */
+        $charset = NLS::getEmailCharset();
+
+        $msg = new Horde_Mime_Part();
+        $msg->setCharset($charset);
+        $msg->setDescription(String::convertCharset(_("S/MIME Encrypted Message"), NLS::getCharset(), $charset));
+        $msg->setDisposition('inline');
+        $msg->setType('application/pkcs7-mime');
+        $msg->setContentTypeParameter('smime-type', 'enveloped-data');
+        $msg->setContents(substr($message, strpos($message, "\n\n") + 2));
+
+        return $msg;
+    }
+
+    /**
+     * Encrypt a message in S/MIME format using a public key.
+     *
+     * @param string $text   The text to be encrypted.
+     * @param array $params  The parameters needed for encryption.
+     * <pre>
+     * Parameters:
+     * ===========
+     * 'type'   => 'message' (REQUIRED)
+     * 'pubkey' => public key (REQUIRED)
+     * </pre>
+     *
+     * @return string  The encrypted message.
+     * @throws Horde_Exception
+     */
+    protected function _encryptMessage($text, $params)
+    {
+        /* Check for required parameters. */
+        if (!isset($params['pubkey'])) {
+            throw new Horde_Exception(_("A public S/MIME key is required to encrypt a message."), 'horde.error');
+        }
+
+        /* Create temp files for input/output. */
+        $input = $this->_createTempFile('horde-smime');
+        $output = $this->_createTempFile('horde-smime');
+
+        /* Store message in file. */
+        file_put_contents($input, $text);
+        unset($text);
+
+        /* Encrypt the document. */
+        if (openssl_pkcs7_encrypt($input, $output, $params['pubkey'], array())) {
+            $result = file_get_contents($output);
+            if (!empty($result)) {
+                return $this->_fixContentType($result, 'encrypt');
+            }
+        }
+
+        throw new Horde_Exception(_("Could not S/MIME encrypt message."), 'horde.error');
+    }
+
+    /**
+     * Sign a message in S/MIME format using a private key.
+     *
+     * @param string $text   The text to be signed.
+     * @param array $params  The parameters needed for signing.
+     * <pre>
+     * Parameters:
+     * ===========
+     * 'certs'       =>  Additional signing certs (Optional)
+     * 'passphrase'  =>  Passphrase for key (REQUIRED)
+     * 'privkey'     =>  Private key (REQUIRED)
+     * 'pubkey'      =>  Public key (REQUIRED)
+     * 'sigtype'     =>  Determine the signature type to use. (Optional)
+     *                   'cleartext'  --  Make a clear text signature
+     *                   'detach'     --  Make a detached signature (DEFAULT)
+     * 'type'        =>  'signature' (REQUIRED)
+     * </pre>
+     *
+     * @return string  The signed message.
+     * @throws Horde_Exception
+     */
+    protected function _encryptSignature($text, $params)
+    {
+        /* Check for required parameters. */
+        if (!isset($params['pubkey']) ||
+            !isset($params['privkey']) ||
+            !array_key_exists('passphrase', $params)) {
+            throw new Horde_Exception(_("A public S/MIME key, private S/MIME key, and passphrase are required to sign a message."), 'horde.error');
+        }
+
+        /* Create temp files for input/output/certificates. */
+        $input = $this->_createTempFile('horde-smime');
+        $output = $this->_createTempFile('horde-smime');
+        $certs = $this->_createTempFile('horde-smime');
+
+        /* Store message in temporary file. */
+        file_put_contents($input, $text);
+        unset($text);
+
+        /* Store additional certs in temporary file. */
+        if (!empty($params['certs'])) {
+            file_put_contents($certs, $params['certs']);
+        }
+
+        /* Determine the signature type to use. */
+        $flags = (isset($params['sigtype']) && ($params['sigtype'] == 'cleartext'))
+            ? PKCS7_TEXT
+            : PKCS7_DETACHED;
+
+        $privkey = (is_null($params['passphrase'])) ? $params['privkey'] : array($params['privkey'], $params['passphrase']);
+
+        if (empty($params['certs'])) {
+            $res = openssl_pkcs7_sign($input, $output, $params['pubkey'], $privkey, array(), $flags);
+        } else {
+            $res = openssl_pkcs7_sign($input, $output, $params['pubkey'], $privkey, array(), $flags, $certs);
+        }
+
+        if (!$res) {
+            throw new Horde_Exception(_("Could not S/MIME sign message."), 'horde.error');
+        }
+
+        $data = file_get_contents($output);
+        return $this->_fixContentType($data, 'signature');
+    }
+
+    /**
+     * Decrypt an S/MIME encrypted message using a private/public keypair
+     * and a passhprase.
+     *
+     * @param string $text   The text to be decrypted.
+     * @param array $params  The parameters needed for decryption.
+     * <pre>
+     * Parameters:
+     * ===========
+     * 'type'        =>  'message' (REQUIRED)
+     * 'pubkey'      =>  public key. (REQUIRED)
+     * 'privkey'     =>  private key. (REQUIRED)
+     * 'passphrase'  =>  Passphrase for Key. (REQUIRED)
+     * </pre>
+     *
+     * @return string  The decrypted message.
+     * @throws Horde_Exception
+     */
+    protected function _decryptMessage($text, $params)
+    {
+        /* Check for required parameters. */
+        if (!isset($params['pubkey']) ||
+            !isset($params['privkey']) ||
+            !array_key_exists('passphrase', $params)) {
+            throw new Horde_Exception(_("A public S/MIME key, private S/MIME key, and passphrase are required to decrypt a message."), 'horde.error');
+        }
+
+        /* Create temp files for input/output. */
+        $input = $this->_createTempFile('horde-smime');
+        $output = $this->_createTempFile('horde-smime');
+
+        /* Store message in file. */
+        file_put_contents($input, $text);
+        unset($text);
+
+        $privkey = (is_null($params['passphrase'])) ? $params['privkey'] : array($params['privkey'], $params['passphrase']);
+        if (openssl_pkcs7_decrypt($input, $output, $params['pubkey'], $privkey)) {
+            return file_get_contents($output);
+        }
+
+        throw new Horde_Exception(_("Could not decrypt S/MIME data."), 'horde.error');
+    }
+
+    /**
+     * Sign and Encrypt a MIME part using S/MIME.
+     *
+     * @param Horde_Mime_Part $mime_part   The object to sign and encrypt.
+     * @param array $sign_params           The parameters required for
+     *                                     signing. @see _encryptSignature().
+     * @param array $encrypt_params        The parameters required for
+     *                                     encryption.
+     *                                     @see _encryptMessage().
+     *
+     * @return mixed  A Horde_Mime_Part object that is signed and encrypted.
+     * @throws Horde_Exception
+     */
+    public function signAndEncryptMIMEPart($mime_part, $sign_params = array(),
+                                           $encrypt_params = array())
+    {
+        $part = $this->signMIMEPart($mime_part, $sign_params);
+        return $this->encryptMIMEPart($part, $encrypt_params);
+    }
+
+    /**
+     * Convert a PEM format certificate to readable HTML version
+     *
+     * @param string $cert   PEM format certificate
+     *
+     * @return string  HTML detailing the certificate.
+     */
+    public function certToHTML($cert)
+    {
+        /* Common Fields */
+        $fieldnames = array(
+            'Email' => _("Email Address"),
+            'CommonName' => _("Common Name"),
+            'Organisation' => _("Organisation"),
+            'OrganisationalUnit' => _("Organisational Unit"),
+            'Country' => _("Country"),
+            'StateOrProvince' => _("State or Province"),
+            'Location' => _("Location"),
+            'StreetAddress' => _("Street Address"),
+            'TelephoneNumber' => _("Telephone Number"),
+            'Surname' => _("Surname"),
+            'GivenName' => _("Given Name")
+        );
+
+        /* Netscape Extensions */
+        $fieldnames += array(
+            'netscape-cert-type' => _("Netscape certificate type"),
+            'netscape-base-url' => _("Netscape Base URL"),
+            'netscape-revocation-url' => _("Netscape Revocation URL"),
+            'netscape-ca-revocation-url' => _("Netscape CA Revocation URL"),
+            'netscape-cert-renewal-url' => _("Netscape Renewal URL"),
+            'netscape-ca-policy-url' => _("Netscape CA policy URL"),
+            'netscape-ssl-server-name' => _("Netscape SSL server name"),
+            'netscape-comment' => _("Netscape certificate comment")
+        );
+
+        /* X590v3 Extensions */
+        $fieldnames += array(
+            'id-ce-extKeyUsage' => _("X509v3 Extended Key Usage"),
+            'id-ce-basicConstraints' => _("X509v3 Basic Constraints"),
+            'id-ce-subjectAltName' => _("X509v3 Subject Alternative Name"),
+            'id-ce-subjectKeyIdentifier' => _("X509v3 Subject Key Identifier"),
+            'id-ce-certificatePolicies' => _("Certificate Policies"),
+            'id-ce-CRLDistributionPoints' => _("CRL Distribution Points"),
+            'id-ce-keyUsage' => _("Key Usage")
+        );
+
+        $cert_details = $this->parseCert($cert);
+        if (!is_array($cert_details)) {
+            return '<pre class="fixed">' . _("Unable to extract certificate details") . '</pre>';
+        }
+        $certificate = $cert_details['certificate'];
+
+        $text = '<pre class="fixed">';
+
+        /* Subject (a/k/a Certificate Owner) */
+        if (isset($certificate['subject'])) {
+            $text .= "<strong>" . _("Certificate Owner") . ":</strong>\n";
+
+            foreach ($certificate['subject'] as $key => $value) {
+                if (isset($fieldnames[$key])) {
+                    $text .= sprintf("&nbsp;&nbsp;%s: %s\n", $fieldnames[$key], $value);
+                } else {
+                    $text .= sprintf("&nbsp;&nbsp;*%s: %s\n", $key, $value);
+                }
+            }
+            $text .= "\n";
+        }
+
+        /* Issuer */
+        if (isset($certificate['issuer'])) {
+            $text .= "<strong>" . _("Issuer") . ":</strong>\n";
+
+            foreach ($certificate['issuer'] as $key => $value) {
+                if (isset($fieldnames[$key])) {
+                    $text .= sprintf("&nbsp;&nbsp;%s: %s\n", $fieldnames[$key], $value);
+                } else {
+                    $text .= sprintf("&nbsp;&nbsp;*%s: %s\n", $key, $value);
+                }
+            }
+            $text .= "\n";
+        }
+
+        /* Dates  */
+        $text .= "<strong>" . _("Validity") . ":</strong>\n";
+        $text .= sprintf("&nbsp;&nbsp;%s: %s\n", _("Not Before"), strftime("%x %X", $certificate['validity']['notbefore']));
+        $text .= sprintf("&nbsp;&nbsp;%s: %s\n", _("Not After"), strftime("%x %X", $certificate['validity']['notafter']));
+        $text .= "\n";
+
+        /* Certificate Owner - Public Key Info */
+        $text .= "<strong>" . _("Public Key Info") . ":</strong>\n";
+        $text .= sprintf("&nbsp;&nbsp;%s: %s\n", _("Public Key Algorithm"), $certificate['subjectPublicKeyInfo']['algorithm']);
+        if ($certificate['subjectPublicKeyInfo']['algorithm'] == 'rsaEncryption') {
+            if (Util::extensionExists('bcmath')) {
+                $modulus = $certificate['subjectPublicKeyInfo']['subjectPublicKey']['modulus'];
+                $modulus_hex = '';
+                while ($modulus != '0') {
+                    $modulus_hex = dechex(bcmod($modulus, '16')) . $modulus_hex;
+                    $modulus = bcdiv($modulus, '16', 0);
+                }
+
+                if ((strlen($modulus_hex) > 64) &&
+                    (strlen($modulus_hex) < 128)) {
+                    str_pad($modulus_hex, 128, '0', STR_PAD_RIGHT);
+                } elseif ((strlen($modulus_hex) > 128) &&
+                          (strlen($modulus_hex) < 256)) {
+                    str_pad($modulus_hex, 256, '0', STR_PAD_RIGHT);
+                }
+
+                $text .= "&nbsp;&nbsp;" . sprintf(_("RSA Public Key (%d bit)"), strlen($modulus_hex) * 4) . ":\n";
+
+                $modulus_str = '';
+                for ($i = 0; $i < strlen($modulus_hex); $i += 2) {
+                    if (($i % 32) == 0) {
+                        $modulus_str .= "\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";
+                    }
+                    $modulus_str .= substr($modulus_hex, $i, 2) . ':';
+                }
+
+                $text .= sprintf("&nbsp;&nbsp;&nbsp;&nbsp;%s: %s\n", _("Modulus"), $modulus_str);
+            }
+
+            $text .= sprintf("&nbsp;&nbsp;&nbsp;&nbsp;%s: %s\n", _("Exponent"), $certificate['subjectPublicKeyInfo']['subjectPublicKey']['publicExponent']);
+        }
+        $text .= "\n";
+
+        /* X509v3 extensions */
+        if (isset($certificate['extensions'])) {
+            $text .= "<strong>" . _("X509v3 extensions") . ":</strong>\n";
+
+            foreach ($certificate['extensions'] as $key => $value) {
+                if (is_array($value)) {
+                    $value = _("Unsupported Extension");
+                }
+                if (isset($fieldnames[$key])) {
+                    $text .= sprintf("&nbsp;&nbsp;%s:\n&nbsp;&nbsp;&nbsp;&nbsp;%s\n", $fieldnames[$key], wordwrap($value, 40, "\n&nbsp;&nbsp;&nbsp;&nbsp;"));
+                } else {
+                    $text .= sprintf("&nbsp;&nbsp;%s:\n&nbsp;&nbsp;&nbsp;&nbsp;%s\n", $key, wordwrap($value, 60, "\n&nbsp;&nbsp;&nbsp;&nbsp;"));
+                }
+            }
+
+            $text .= "\n";
+        }
+
+        /* Certificate Details */
+        $text .= "<strong>" . _("Certificate Details") . ":</strong>\n";
+        $text .= sprintf("&nbsp;&nbsp;%s: %d\n", _("Version"), $certificate['version']);
+        $text .= sprintf("&nbsp;&nbsp;%s: %d\n", _("Serial Number"), $certificate['serialNumber']);
+
+        foreach ($cert_details['fingerprints'] as $hash => $fingerprint) {
+            $label = sprintf(_("%s Fingerprint"), String::upper($hash));
+            $fingerprint_str = '';
+            for ($i = 0; $i < strlen($fingerprint); $i += 2) {
+                $fingerprint_str .= substr($fingerprint, $i, 2) . ':';
+            }
+            $text .= sprintf("&nbsp;&nbsp;%s:\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;%s\n", $label, $fingerprint_str);
+        }
+        $text .= sprintf("&nbsp;&nbsp;%s: %s\n", _("Signature Algorithm"), $cert_details['signatureAlgorithm']);
+        $text .= sprintf("&nbsp;&nbsp;%s:", _("Signature"));
+
+        $sig_str = '';
+        for ($i = 0; $i < strlen($cert_details['signature']); $i++) {
+            if (($i % 16) == 0) {
+                $sig_str .= "\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";
+            }
+            $sig_str .= sprintf("%02x:", ord($cert_details['signature'][$i]));
+        }
+
+        return $text . $sig_str . "\n</pre>";
+    }
+
+    /**
+     * Extract the contents of a PEM format certificate to an array.
+     *
+     * @param string $cert  PEM format certificate
+     *
+     * @return array  Array containing all extractable information about
+     *                the certificate.
+     */
+    public function parseCert($cert)
+    {
+        $cert_split = preg_split('/(-----((BEGIN)|(END)) CERTIFICATE-----)/', $cert);
+        if (!isset($cert_split[1])) {
+            $raw_cert = base64_decode($cert);
+        } else {
+            $raw_cert = base64_decode($cert_split[1]);
+        }
+
+        $cert_data = $this->_parseASN($raw_cert);
+        if (!is_array($cert_data) || ($cert_data[0] == 'UNKNOWN')) {
+            return false;
+        }
+
+        $cert_details = array();
+        $cert_details['fingerprints']['md5'] = hash('md5', $raw_cert);
+        $cert_details['fingerprints']['sha1'] = hash('sha1', $raw_cert);
+
+        $cert_details['certificate']['extensions'] = array();
+        $cert_details['certificate']['version'] = $cert_data[1][0][1][0][1] + 1;
+        $cert_details['certificate']['serialNumber'] = $cert_data[1][0][1][1][1];
+        $cert_details['certificate']['signature'] = $cert_data[1][0][1][2][1][0][1];
+        $cert_details['certificate']['issuer'] = $cert_data[1][0][1][3][1];
+        $cert_details['certificate']['validity'] = $cert_data[1][0][1][4][1];
+        $cert_details['certificate']['subject'] = @$cert_data[1][0][1][5][1];
+        $cert_details['certificate']['subjectPublicKeyInfo'] = $cert_data[1][0][1][6][1];
+
+        $cert_details['signatureAlgorithm'] = $cert_data[1][1][1][0][1];
+        $cert_details['signature'] = $cert_data[1][2][1];
+
+        // issuer
+        $issuer = array();
+        foreach ($cert_details['certificate']['issuer'] as $value) {
+            $issuer[$value[1][1][0][1]] = $value[1][1][1][1];
+        }
+        $cert_details['certificate']['issuer'] = $issuer;
+
+        // subject
+        $subject = array();
+        foreach ($cert_details['certificate']['subject'] as $value) {
+            $subject[$value[1][1][0][1]] = $value[1][1][1][1];
+        }
+        $cert_details['certificate']['subject'] = $subject;
+
+        // validity
+        $vals = $cert_details['certificate']['validity'];
+        $cert_details['certificate']['validity'] = array();
+        $cert_details['certificate']['validity']['notbefore'] = $vals[0][1];
+        $cert_details['certificate']['validity']['notafter'] = $vals[1][1];
+        foreach ($cert_details['certificate']['validity'] as $key => $val) {
+            $year = substr($val, 0, 2);
+            $month = substr($val, 2, 2);
+            $day = substr($val, 4, 2);
+            $hour = substr($val, 6, 2);
+            $minute = substr($val, 8, 2);
+            if (($val[11] == '-') || ($val[9] == '+')) {
+                // handle time zone offset here
+                $seconds = 0;
+            } elseif (String::upper($val[11]) == 'Z') {
+                $seconds = 0;
+            } else {
+                $seconds = substr($val, 10, 2);
+                if (($val[11] == '-') || ($val[9] == '+')) {
+                    // handle time zone offset here
+                }
+            }
+            $cert_details['certificate']['validity'][$key] = mktime ($hour, $minute, $seconds, $month, $day, $year);
+        }
+
+        // Split the Public Key into components.
+        $subjectPublicKeyInfo = array();
+        $subjectPublicKeyInfo['algorithm'] = $cert_details['certificate']['subjectPublicKeyInfo'][0][1][0][1];
+        if ($subjectPublicKeyInfo['algorithm'] == 'rsaEncryption') {
+            $subjectPublicKey = $this->_parseASN($cert_details['certificate']['subjectPublicKeyInfo'][1][1]);
+            $subjectPublicKeyInfo['subjectPublicKey']['modulus'] = $subjectPublicKey[1][0][1];
+            $subjectPublicKeyInfo['subjectPublicKey']['publicExponent'] = $subjectPublicKey[1][1][1];
+        }
+        $cert_details['certificate']['subjectPublicKeyInfo'] = $subjectPublicKeyInfo;
+
+        if (isset($cert_data[1][0][1][7]) &&
+            is_array($cert_data[1][0][1][7][1])) {
+            foreach ($cert_data[1][0][1][7][1] as $ext) {
+                $oid = $ext[1][0][1];
+                $cert_details['certificate']['extensions'][$oid] = $ext[1][1];
+            }
+        }
+
+        $i = 9;
+
+        while (isset($cert_data[1][0][1][$i]) &&
+               is_array($cert_data[1][0][1][$i][1])) {
+            $oid = $cert_data[1][0][1][$i][1][0][1];
+            $cert_details['certificate']['extensions'][$oid] = $cert_data[1][0][1][$i][1][1];
+            ++$i;
+        }
+
+        foreach ($cert_details['certificate']['extensions'] as $oid => $val) {
+            switch ($oid) {
+            case 'netscape-base-url':
+            case 'netscape-revocation-url':
+            case 'netscape-ca-revocation-url':
+            case 'netscape-cert-renewal-url':
+            case 'netscape-ca-policy-url':
+            case 'netscape-ssl-server-name':
+            case 'netscape-comment':
+                $val = $this->_parseASN($val[1]);
+                $cert_details['certificate']['extensions'][$oid] = $val[1];
+                break;
+
+            case 'id-ce-subjectAltName':
+                $val = $this->_parseASN($val[1]);
+                $cert_details['certificate']['extensions'][$oid] = '';
+                foreach ($val[1] as $name) {
+                    if (!empty($cert_details['certificate']['extensions'][$oid])) {
+                        $cert_details['certificate']['extensions'][$oid] .= ', ';
+                    }
+                    $cert_details['certificate']['extensions'][$oid] .= $name[1];
+                }
+                break;
+
+            case 'netscape-cert-type':
+                $val = $this->_parseASN($val[1]);
+                $val = ord($val[1]);
+                $newVal = '';
+
+                if ($val & 0x80) {
+                    $newVal .= empty($newVal) ? 'SSL client' : ', SSL client';
+                }
+                if ($val & 0x40) {
+                    $newVal .= empty($newVal) ? 'SSL server' : ', SSL server';
+                }
+                if ($val & 0x20) {
+                    $newVal .= empty($newVal) ? 'S/MIME' : ', S/MIME';
+                }
+                if ($val & 0x10) {
+                    $newVal .= empty($newVal) ? 'Object Signing' : ', Object Signing';
+                }
+                if ($val & 0x04) {
+                    $newVal .= empty($newVal) ? 'SSL CA' : ', SSL CA';
+                }
+                if ($val & 0x02) {
+                    $newVal .= empty($newVal) ? 'S/MIME CA' : ', S/MIME CA';
+                }
+                if ($val & 0x01) {
+                    $newVal .= empty($newVal) ? 'Object Signing CA' : ', Object Signing CA';
+                }
+
+                $cert_details['certificate']['extensions'][$oid] = $newVal;
+                break;
+
+            case 'id-ce-extKeyUsage':
+                $val = $this->_parseASN($val[1]);
+                $val = $val[1];
+
+                $newVal = '';
+                if ($val[0][1] != 'sequence') {
+                    $val = array($val);
+                } else {
+                    $val = $val[1][1];
+                }
+                foreach ($val as $usage) {
+                    if ($usage[1] == 'id_kp_clientAuth') {
+                        $newVal .= empty($newVal) ? 'TLS Web Client Authentication' : ', TLS Web Client Authentication';
+                    } else {
+                        $newVal .= empty($newVal) ? $usage[1] : ', ' . $usage[1];
+                    }
+                }
+                $cert_details['certificate']['extensions'][$oid] = $newVal;
+                break;
+
+            case 'id-ce-subjectKeyIdentifier':
+                $val = $this->_parseASN($val[1]);
+                $val = $val[1];
+
+                $newVal = '';
+
+                for ($i = 0; $i < strlen($val); $i++) {
+                    $newVal .= sprintf("%02x:", ord($val[$i]));
+                }
+                $cert_details['certificate']['extensions'][$oid] = $newVal;
+                break;
+
+            case 'id-ce-authorityKeyIdentifier':
+                $val = $this->_parseASN($val[1]);
+                if ($val[0] == 'string') {
+                    $val = $val[1];
+
+                    $newVal = '';
+                    for ($i = 0; $i < strlen($val); $i++) {
+                        $newVal .= sprintf("%02x:", ord($val[$i]));
+                    }
+                    $cert_details['certificate']['extensions'][$oid] = $newVal;
+                } else {
+                    $cert_details['certificate']['extensions'][$oid] = _("Unsupported Extension");
+                }
+                break;
+
+            case 'id-ce-basicConstraints':
+            case 'default':
+                $cert_details['certificate']['extensions'][$oid] = _("Unsupported Extension");
+                break;
+            }
+        }
+
+        return $cert_details;
+    }
+
+    /**
+     * Attempt to parse ASN.1 formated data.
+     *
+     * @param string $data  ASN.1 formated data
+     *
+     * @return array  Array contained the extracted values.
+     */
+    protected function _parseASN($data)
+    {
+        $result = array();
+
+        while (strlen($data) > 1) {
+            $class = ord($data[0]);
+            switch ($class) {
+            case 0x30:
+                // Sequence
+                $len = ord($data[1]);
+                $bytes = 0;
+                if ($len & 0x80) {
+                    $bytes = $len & 0x0f;
+                    $len = 0;
+                    for ($i = 0; $i < $bytes; $i++) {
+                        $len = ($len << 8) | ord($data[$i + 2]);
+                    }
+                }
+                $sequence_data = substr($data, 2 + $bytes, $len);
+                $data = substr($data, 2 + $bytes + $len);
+
+                $values = $this->_parseASN($sequence_data);
+                if (!is_array($values) || is_string($values[0])) {
+                    $values = array($values);
+                }
+                $sequence_values = array();
+                $i = 0;
+                foreach ($values as $val) {
+                    if ($val[0] == 'extension') {
+                        $sequence_values['extensions'][] = $val;
+                    } else {
+                        $sequence_values[$i++] = $val;
+                    }
+                }
+                $result[] = array('sequence', $sequence_values);
+                break;
+
+            case 0x31:
+                // Set of
+                $len = ord($data[1]);
+                $bytes = 0;
+                if ($len & 0x80) {
+                    $bytes = $len & 0x0f;
+                    $len = 0;
+                    for ($i = 0; $i < $bytes; $i++) {
+                        $len = ($len << 8) | ord($data[$i + 2]);
+                    }
+                }
+                $sequence_data = substr($data, 2 + $bytes, $len);
+                $data = substr($data, 2 + $bytes + $len);
+                $result[] = array('set', $this->_parseASN($sequence_data));
+                break;
+
+            case 0x01:
+                // Boolean type
+                $boolean_value = (ord($data[2]) == 0xff);
+                $data = substr($data, 3);
+                $result[] = array('boolean', $boolean_value);
+                break;
+
+            case 0x02:
+                // Integer type
+                $len = ord($data[1]);
+                $bytes = 0;
+                if ($len & 0x80) {
+                    $bytes = $len & 0x0f;
+                    $len = 0;
+                    for ($i = 0; $i < $bytes; $i++) {
+                        $len = ($len << 8) | ord($data[$i + 2]);
+                    }
+                }
+
+                $integer_data = substr($data, 2 + $bytes, $len);
+                $data = substr($data, 2 + $bytes + $len);
+
+                $value = 0;
+                if ($len <= 4) {
+                    /* Method works fine for small integers */
+                    for ($i = 0; $i < strlen($integer_data); $i++) {
+                        $value = ($value << 8) | ord($integer_data[$i]);
+                    }
+                } else {
+                    /* Method works for arbitrary length integers */
+                    if (Util::extensionExists('bcmath')) {
+                        for ($i = 0; $i < strlen($integer_data); $i++) {
+                            $value = bcadd(bcmul($value, 256), ord($integer_data[$i]));
+                        }
+                    } else {
+                        $value = -1;
+                    }
+                }
+                $result[] = array('integer(' . $len . ')', $value);
+                break;
+
+            case 0x03:
+                // Bitstring type
+                $len = ord($data[1]);
+                $bytes = 0;
+                if ($len & 0x80) {
+                    $bytes = $len & 0x0f;
+                    $len = 0;
+                    for ($i = 0; $i < $bytes; $i++) {
+                        $len = ($len << 8) | ord($data[$i + 2]);
+                    }
+                }
+                $bitstring_data = substr($data, 3 + $bytes, $len);
+                $data = substr($data, 2 + $bytes + $len);
+                $result[] = array('bit string', $bitstring_data);
+                break;
+
+            case 0x04:
+                // Octetstring type
+                $len = ord($data[1]);
+                $bytes = 0;
+                if ($len & 0x80) {
+                    $bytes = $len & 0x0f;
+                    $len = 0;
+                    for ($i = 0; $i < $bytes; $i++) {
+                        $len = ($len << 8) | ord($data[$i + 2]);
+                    }
+                }
+                $octectstring_data = substr($data, 2 + $bytes, $len);
+                $data = substr($data, 2 + $bytes + $len);
+                $result[] = array('octet string', $octectstring_data);
+                break;
+
+            case 0x05:
+                // Null type
+                $data = substr($data, 2);
+                $result[] = array('null', null);
+                break;
+
+            case 0x06:
+                // Object identifier type
+                $len = ord($data[1]);
+                $bytes = 0;
+                if ($len & 0x80) {
+                    $bytes = $len & 0x0f;
+                    $len = 0;
+                    for ($i = 0; $i < $bytes; $i++) {
+                        $len = ($len << 8) | ord($data[$i + 2]);
+                    }
+                }
+                $oid_data = substr($data, 2 + $bytes, $len);
+                $data = substr($data, 2 + $bytes + $len);
+
+                // Unpack the OID
+                $plain  = floor(ord($oid_data[0]) / 40);
+                $plain .= '.' . ord($oid_data[0]) % 40;
+
+                $value = 0;
+                $i = 1;
+                while ($i < strlen($oid_data)) {
+                    $value = $value << 7;
+                    $value = $value | (ord($oid_data[$i]) & 0x7f);
+
+                    if (!(ord($oid_data[$i]) & 0x80)) {
+                        $plain .= '.' . $value;
+                        $value = 0;
+                    }
+                    $i++;
+                }
+
+                if (isset($this->_oids[$plain])) {
+                    $result[] = array('oid', $this->_oids[$plain]);
+                } else {
+                    $result[] = array('oid', $plain);
+                }
+
+                break;
+
+            case 0x12:
+            case 0x13:
+            case 0x14:
+            case 0x15:
+            case 0x16:
+            case 0x81:
+            case 0x80:
+                // Character string type
+                $len = ord($data[1]);
+                $bytes = 0;
+                if ($len & 0x80) {
+                    $bytes = $len & 0x0f;
+                    $len = 0;
+                    for ($i = 0; $i < $bytes; $i++) {
+                        $len = ($len << 8) | ord($data[$i + 2]);
+                    }
+                }
+                $string_data = substr($data, 2 + $bytes, $len);
+                $data = substr($data, 2 + $bytes + $len);
+                $result[] = array('string', $string_data);
+                break;
+
+            case 0x17:
+                // Time types
+                $len = ord($data[1]);
+                $bytes = 0;
+                if ($len & 0x80) {
+                    $bytes = $len & 0x0f;
+                    $len = 0;
+                    for ($i = 0; $i < $bytes; $i++) {
+                        $len = ($len << 8) | ord($data[$i + 2]);
+                    }
+                }
+                $time_data = substr($data, 2 + $bytes, $len);
+                $data = substr($data, 2 + $bytes + $len);
+                $result[] = array('utctime', $time_data);
+                break;
+
+            case 0x82:
+                // X509v3 extensions?
+                $len = ord($data[1]);
+                $bytes = 0;
+                if ($len & 0x80) {
+                    $bytes = $len & 0x0f;
+                    $len = 0;
+                    for ($i = 0; $i < $bytes; $i++) {
+                        $len = ($len << 8) | ord($data[$i + 2]);
+                    }
+                }
+                $sequence_data = substr($data, 2 + $bytes, $len);
+                $data = substr($data, 2 + $bytes + $len);
+                $result[] = array('extension', 'X509v3 extensions');
+                $result[] = $this->_parseASN($sequence_data);
+                break;
+
+            case 0xa0:
+            case 0xa3:
+                // Extensions
+                $extension_data = substr($data, 0, 2);
+                $data = substr($data, 2);
+                $result[] = array('extension', dechex($extension_data));
+                break;
+
+            case 0xe6:
+                $extension_data = substr($data, 0, 1);
+                $data = substr($data, 1);
+                $result[] = array('extension', dechex($extension_data));
+                break;
+
+            case 0xa1:
+                $extension_data = substr($data, 0, 1);
+                $data = substr($data, 6);
+                $result[] = array('extension', dechex($extension_data));
+                break;
+
+            default:
+                // Unknown
+                $result[] = array('UNKNOWN', dechex($data));
+                $data = '';
+                break;
+            }
+        }
+
+        return (count($result) > 1) ? $result : array_pop($result);
+    }
+
+    /**
+     * Decrypt an S/MIME signed message using a public key.
+     *
+     * @param string $text   The text to be verified.
+     * @param array $params  The parameters needed for verification.
+     *
+     * @return string  The verification message.
+     * @throws Horde_Exception
+     */
+    protected function _decryptSignature($text, $params)
+    {
+        throw new Horde_Exception('_decryptSignature() ' . _("not yet implemented"));
+    }
+
+    /**
+     * Check for the presence of the OpenSSL extension to PHP.
+     *
+     * @throws Horde_Exception
+     */
+    public function checkForOpenSSL()
+    {
+        if (!Util::extensionExists('openssl')) {
+            throw new Horde_Exception(_("The openssl module is required for the Horde_Crypt_Smime:: class."));
+        }
+    }
+
+    /**
+     * Extract the email address from a public key.
+     *
+     * @param string $key  The public key.
+     *
+     * @return mixed  Returns the first email address found, or null if
+     *                there are none.
+     */
+    public function getEmailFromKey($key)
+    {
+        $key_info = openssl_x509_parse($key);
+        if (!is_array($key_info)) {
+            return null;
+        }
+
+        if (isset($key_info['subject'])) {
+            if (isset($key_info['subject']['Email'])) {
+                return $key_info['subject']['Email'];
+            } elseif (isset($key_info['subject']['emailAddress'])) {
+                return $key_info['subject']['emailAddress'];
+            }
+        }
+
+        // Check subjectAltName per http://www.ietf.org/rfc/rfc3850.txt
+        if (isset($key_info['extensions']['subjectAltName'])) {
+            $names = preg_split('/\s*,\s*/', $key_info['extensions']['subjectAltName'], -1, PREG_SPLIT_NO_EMPTY);
+            foreach ($names as $name) {
+                if (strpos($name, ':') === false) {
+                    continue;
+                }
+                list($kind, $value) = explode(':', $name, 2);
+                if (String::lower($kind) == 'email') {
+                    return $value;
+                }
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Convert a PKCS 12 encrypted certificate package into a private key,
+     * public key, and any additional keys.
+     *
+     * @param string $text   The PKCS 12 data.
+     * @param array $params  The parameters needed for parsing.
+     * <pre>
+     * Parameters:
+     * ===========
+     * 'sslpath' => The path to the OpenSSL binary. (REQUIRED)
+     * 'password' => The password to use to decrypt the data. (Optional)
+     * 'newpassword' => The password to use to encrypt the private key.
+     *                  (Optional)
+     * </pre>
+     *
+     * @return stdClass  An object.
+     *                   'private' -  The private key in PEM format.
+     *                   'public'  -  The public key in PEM format.
+     *                   'certs'   -  An array of additional certs.
+     * @throws Horde_Exception
+     */
+    public function parsePKCS12Data($pkcs12, $params)
+    {
+        /* Check for availability of OpenSSL PHP extension. */
+        $this->checkForOpenSSL();
+
+        if (!isset($params['sslpath'])) {
+            throw new Horde_Exception(_("No path to the OpenSSL binary provided. The OpenSSL binary is necessary to work with PKCS 12 data."), 'horde.error');
+        }
+        $sslpath = escapeshellcmd($params['sslpath']);
+
+        /* Create temp files for input/output. */
+        $input = $this->_createTempFile('horde-smime');
+        $output = $this->_createTempFile('horde-smime');
+
+        $ob = new stdClass;
+
+        /* Write text to file */
+        file_put_contents($input, $pkcs12);
+        unset($pkcs12);
+
+        /* Extract the private key from the file first. */
+        $cmdline = $sslpath . ' pkcs12 -in ' . $input . ' -out ' . $output . ' -nocerts';
+        if (isset($params['password'])) {
+            $cmdline .= ' -passin stdin';
+            if (!empty($params['newpassword'])) {
+                $cmdline .= ' -passout stdin';
+            } else {
+                $cmdline .= ' -nodes';
+            }
+            $fd = popen($cmdline, 'w');
+            fwrite($fd, $params['password'] . "\n");
+            if (!empty($params['newpassword'])) {
+                fwrite($fd, $params['newpassword'] . "\n");
+            }
+            pclose($fd);
+        } else {
+            $cmdline .= ' -nodes';
+            exec($cmdline);
+        }
+        $ob->private = trim(file_get_contents($output));
+        if (empty($ob->private)) {
+            throw new Horde_Exception(_("Password incorrect"), 'horde.error');
+        }
+
+        /* Extract the client public key next. */
+        $cmdline = $sslpath . ' pkcs12 -in ' . $input . ' -out ' . $output . ' -nokeys -clcerts';
+        if (isset($params['password'])) {
+            $cmdline .= ' -passin stdin';
+            $fd = popen($cmdline, 'w');
+            fwrite($fd, $params['password'] . "\n");
+            pclose($fd);
+        } else {
+            exec($cmdline);
+        }
+        $ob->public = trim(file_get_contents($output));
+
+        /* Extract the CA public key next. */
+        $cmdline = $sslpath . ' pkcs12 -in ' . $input . ' -out ' . $output . ' -nokeys -cacerts';
+        if (isset($params['password'])) {
+            $cmdline .= ' -passin stdin';
+            $fd = popen($cmdline, 'w');
+            fwrite($fd, $params['password'] . "\n");
+            pclose($fd);
+        } else {
+            exec($cmdline);
+        }
+        $ob->certs = trim(file_get_contents($output));
+
+        return $ob;
+    }
+
+    /**
+     * The Content-Type parameters PHP's openssl_pkcs7_* functions return are
+     * deprecated.  Fix these headers to the correct ones (see RFC 2311).
+     *
+     * @param string $text  The PKCS7 data.
+     * @param string $type  Is this 'message' or 'signature' data?
+     *
+     * @return string  The PKCS7 data with the correct Content-Type parameter.
+     */
+    protected function _fixContentType($text, $type)
+    {
+        if ($type == 'message') {
+            $from = 'application/x-pkcs7-mime';
+            $to = 'application/pkcs7-mime';
+        } else {
+            $from = 'application/x-pkcs7-signature';
+            $to = 'application/pkcs7-signature';
+        }
+        return str_replace('Content-Type: ' . $from, 'Content-Type: ' . $to, $text);
+    }
+
+}
diff --git a/framework/Crypt/lib/Horde/Crypt/pgp.php b/framework/Crypt/lib/Horde/Crypt/pgp.php
deleted file mode 100644 (file)
index db144b1..0000000
+++ /dev/null
@@ -1,1631 +0,0 @@
-<?php
-/**
- * Horde_Crypt_pgp:: provides a framework for Horde applications to interact
- * with the GNU Privacy Guard program ("GnuPG").  GnuPG implements the OpenPGP
- * standard (RFC 2440).
- *
- * GnuPG Website: http://www.gnupg.org/
- *
- * This class has been developed with, and is only guaranteed to work with,
- * Version 1.21 or above of GnuPG.
- *
- * Copyright 2002-2009 The Horde Project (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
- *
- * @author  Michael Slusarz <slusarz@horde.org>
- * @package Horde_Crypt
- */
-class Horde_Crypt_pgp extends Horde_Crypt
-{
-    /**
-     * Armor Header Lines - From RFC 2440:
-     *
-     * An Armor Header Line consists of the appropriate header line text
-     * surrounded by five (5) dashes ('-', 0x2D) on either side of the header
-     * line text. The header line text is chosen based upon the type of data
-     * that is being encoded in Armor, and how it is being encoded.
-     *
-     *  All Armor Header Lines are prefixed with 'PGP'.
-     *
-     *  The Armor Tail Line is composed in the same manner as the Armor Header
-     *  Line, except the string "BEGIN" is replaced by the string "END."
-     */
-
-    /* Used for signed, encrypted, or compressed files. */
-    const ARMOR_MESSAGE = 1;
-
-    /* Used for signed files. */
-    const ARMOR_SIGNED_MESSAGE = 2;
-
-    /* Used for armoring public keys. */
-    const ARMOR_PUBLIC_KEY = 3;
-
-    /* Used for armoring private keys. */
-    const ARMOR_PRIVATE_KEY = 4;
-
-    /* Used for detached signatures, PGP/MIME signatures, and natures
-     * following clearsigned messages. */
-    const ARMOR_SIGNATURE = 5;
-
-    /* Regular text contained in an PGP message. */
-    const ARMOR_TEXT = 6;
-
-    /**
-     * Strings in armor header lines used to distinguish between the different
-     * types of PGP decryption/encryption.
-     *
-     * @var array
-     */
-    protected $_armor = array(
-        'MESSAGE' => self::ARMOR_MESSAGE,
-        'SIGNED MESSAGE' => self::ARMOR_SIGNED_MESSAGE,
-        'PUBLIC KEY BLOCK' => self::ARMOR_PUBLIC_KEY,
-        'PRIVATE KEY BLOCK' => self::ARMOR_PRIVATE_KEY,
-        'SIGNATURE' => self::ARMOR_SIGNATURE
-    );
-
-    /* The default public PGP keyserver to use. */
-    const KEYSERVER_PUBLIC = 'pgp.mit.edu';
-
-    /* The number of times the keyserver refuses connection before an error is
-     * returned. */
-    const KEYSERVER_REFUSE = 3;
-
-    /* The number of seconds that PHP will attempt to connect to the keyserver
-     * before it will stop processing the request. */
-    const KEYSERVER_TIMEOUT = 10;
-
-    /**
-     * The list of PGP hash algorithms (from RFC 3156).
-     *
-     * @var array
-     */
-    protected $_hashAlg = array(
-        1 => 'pgp-md5',
-        2 => 'pgp-sha1',
-        3 => 'pgp-ripemd160',
-        5 => 'pgp-md2',
-        6 => 'pgp-tiger192',
-        7 => 'pgp-haval-5-160',
-        8 => 'pgp-sha256',
-        9 => 'pgp-sha384',
-        10 => 'pgp-sha512',
-        11 => 'pgp-sha224',
-    );
-
-    /**
-     * GnuPG program location/common options.
-     *
-     * @var array
-     */
-    protected $_gnupg;
-
-    /**
-     * Filename of the temporary public keyring.
-     *
-     * @var string
-     */
-    protected $_publicKeyring;
-
-    /**
-     * Filename of the temporary private keyring.
-     *
-     * @var string
-     */
-    protected $_privateKeyring;
-
-    /**
-     * Constructor.
-     *
-     * @param array $params  Parameter array containing the path to the GnuPG
-     *                       binary (key = 'program') and to a temporary
-     *                       directory.
-     */
-    public function __construct($params = array())
-    {
-        $this->_tempdir = Util::createTempDir(true, $params['temp']);
-
-        if (empty($params['program'])) {
-            Horde::fatal(PEAR::raiseError('The location of the GnuPG binary must be given to the Horde_Crypt_pgp:: class.'), __FILE__, __LINE__);
-        }
-
-        /* Store the location of GnuPG and set common options. */
-        $this->_gnupg = array(
-            $params['program'],
-            '--no-tty',
-            '--no-secmem-warning',
-            '--no-options',
-            '--no-default-keyring',
-            '--yes',
-            '--homedir ' . $this->_tempdir
-        );
-
-        if (strncasecmp(PHP_OS, 'WIN', 3)) {
-            array_unshift($this->_gnupg, 'LANG= ;');
-        }
-    }
-
-    /**
-     * Generates a personal Public/Private keypair combination.
-     *
-     * @param string $realname    The name to use for the key.
-     * @param string $email       The email to use for the key.
-     * @param string $passphrase  The passphrase to use for the key.
-     * @param string $comment     The comment to use for the key.
-     * @param integer $keylength  The keylength to use for the key.
-     *
-     * @return array  An array consisting of the public key and the private
-     *                key, or PEAR_Error on error.
-     * <pre>
-     * Return array:
-     * Key            Value
-     * --------------------------
-     * 'public'   =>  Public Key
-     * 'private'  =>  Private Key
-     * </pre>
-     */
-    public function generateKey($realname, $email, $passphrase, $comment = '',
-                                $keylength = 1024)
-    {
-        /* Create temp files to hold the generated keys. */
-        $pub_file = $this->_createTempFile('horde-pgp');
-        $secret_file = $this->_createTempFile('horde-pgp');
-
-        /* Create the config file necessary for GnuPG to run in batch mode. */
-        /* TODO: Sanitize input, More user customizable? */
-        $input = array();
-        $input[] = '%pubring ' . $pub_file;
-        $input[] = '%secring ' . $secret_file;
-        $input[] = 'Key-Type: DSA';
-        $input[] = 'Key-Length: 1024';
-        $input[] = 'Subkey-Type: ELG-E';
-        $input[] = 'Subkey-Length: ' . $keylength;
-        $input[] = 'Name-Real: ' . $realname;
-        if (!empty($comment)) {
-            $input[] = 'Name-Comment: ' . $comment;
-        }
-        $input[] = 'Name-Email: ' . $email;
-        $input[] = 'Expire-Date: 0';
-        $input[] = 'Passphrase: ' . $passphrase;
-        $input[] = '%commit';
-
-        /* Run through gpg binary. */
-        $cmdline = array(
-            '--gen-key',
-            '--batch',
-            '--armor'
-        );
-        $result = $this->_callGpg($cmdline, 'w', $input, true, true);
-
-        /* Get the keys from the temp files. */
-        $public_key = file($pub_file);
-        $secret_key = file($secret_file);
-
-        /* If either key is empty, something went wrong. */
-        if (empty($public_key) || empty($secret_key)) {
-            $msg = _("Public/Private keypair not generated successfully.");
-            if (!empty($result->stderr)) {
-                $msg .= ' ' . _("Returned error message:") . ' ' . $result->stderr;
-            }
-            return PEAR::raiseError($msg, 'horde.error');
-        }
-
-        return array('public' => $public_key, 'private' => $secret_key);
-    }
-
-    /**
-     * Returns information on a PGP data block.
-     *
-     * @param string $pgpdata  The PGP data block.
-     *
-     * @return array  An array with information on the PGP data block. If an
-     *                element is not present in the data block, it will
-     *                likewise not be set in the array.
-     * <pre>
-     * Array Format:
-     * -------------
-     * [public_key]/[secret_key] => Array
-     *   (
-     *     [created] => Key creation - UNIX timestamp
-     *     [expires] => Key expiration - UNIX timestamp (0 = never expires)
-     *     [size]    => Size of the key in bits
-     *   )
-     *
-     * [keyid] => Key ID of the PGP data (if available)
-     *            16-bit hex value (as of Horde 3.2)
-     *
-     * [signature] => Array (
-     *     [id{n}/'_SIGNATURE'] => Array (
-     *         [name]        => Full Name
-     *         [comment]     => Comment
-     *         [email]       => E-mail Address
-     *         [keyid]       => 16-bit hex value (as of Horde 3.2)
-     *         [created]     => Signature creation - UNIX timestamp
-     *         [expires]     => Signature expiration - UNIX timestamp
-     *         [micalg]      => The hash used to create the signature
-     *         [sig_{hex}]   => Array [details of a sig verifying the ID] (
-     *             [created]     => Signature creation - UNIX timestamp
-     *             [expires]     => Signature expiration - UNIX timestamp
-     *             [keyid]       => 16-bit hex value (as of Horde 3.2)
-     *             [micalg]      => The hash used to create the signature
-     *         )
-     *     )
-     * )
-     * </pre>
-     *
-     * Each user ID will be stored in the array 'signature' and have data
-     * associated with it, including an array for information on each
-     * signature that has signed that UID. Signatures not associated with a
-     * UID (e.g. revocation signatures and sub keys) will be stored under the
-     * special keyword '_SIGNATURE'.
-     */
-    public function pgpPacketInformation($pgpdata)
-    {
-        $data_array = array();
-        $keyid = '';
-        $header = null;
-        $input = $this->_createTempFile('horde-pgp');
-        $sig_id = $uid_idx = 0;
-
-        /* Store message in temporary file. */
-        $fp = fopen($input, 'w+');
-        fputs($fp, $pgpdata);
-        fclose($fp);
-
-        $cmdline = array(
-            '--list-packets',
-            $input
-        );
-        $result = $this->_callGpg($cmdline, 'r');
-
-        foreach (explode("\n", $result->stdout) as $line) {
-            /* Headers are prefaced with a ':' as the first character on the
-               line. */
-            if (strpos($line, ':') === 0) {
-                $lowerLine = String::lower($line);
-
-                /* If we have a key (rather than a signature block), get the
-                   key's ID */
-                if (strpos($lowerLine, ':public key packet:') !== false ||
-                    strpos($lowerLine, ':secret key packet:') !== false) {
-                    $cmdline = array(
-                        '--with-colons',
-                        $input
-                    );
-                    $data = $this->_callGpg($cmdline, 'r');
-                    if (preg_match("/(sec|pub):.*:.*:.*:([A-F0-9]{16}):/", $data->stdout, $matches)) {
-                        $keyid = $matches[2];
-                    }
-                }
-
-                if (strpos($lowerLine, ':public key packet:') !== false) {
-                    $header = 'public_key';
-                } elseif (strpos($lowerLine, ':secret key packet:') !== false) {
-                    $header = 'secret_key';
-                } elseif (strpos($lowerLine, ':user id packet:') !== false) {
-                    $uid_idx++;
-                    $line = preg_replace_callback('/\\\\x([0-9a-f]{2})/', array($this, '_pgpPacketInformationHelper'), $line);
-                    if (preg_match("/\"([^\<]+)\<([^\>]+)\>\"/", $line, $matches)) {
-                        $header = 'id' . $uid_idx;
-                        if (preg_match('/([^\(]+)\((.+)\)$/', trim($matches[1]), $comment_matches)) {
-                            $data_array['signature'][$header]['name'] = trim($comment_matches[1]);
-                            $data_array['signature'][$header]['comment'] = $comment_matches[2];
-                        } else {
-                            $data_array['signature'][$header]['name'] = trim($matches[1]);
-                            $data_array['signature'][$header]['comment'] = '';
-                        }
-                        $data_array['signature'][$header]['email'] = $matches[2];
-                        $data_array['signature'][$header]['keyid'] = $keyid;
-                    }
-                } elseif (strpos($lowerLine, ':signature packet:') !== false) {
-                    if (empty($header) || empty($uid_idx)) {
-                        $header = '_SIGNATURE';
-                    }
-                    if (preg_match("/keyid\s+([0-9A-F]+)/i", $line, $matches)) {
-                        $sig_id = $matches[1];
-                        $data_array['signature'][$header]['sig_' . $sig_id]['keyid'] = $matches[1];
-                        $data_array['keyid'] = $matches[1];
-                    }
-                } elseif (strpos($lowerLine, ':literal data packet:') !== false) {
-                    $header = 'literal';
-                } elseif (strpos($lowerLine, ':encrypted data packet:') !== false) {
-                    $header = 'encrypted';
-                } else {
-                    $header = null;
-                }
-            } else {
-                if ($header == 'secret_key' || $header == 'public_key') {
-                    if (preg_match("/created\s+(\d+),\s+expires\s+(\d+)/i", $line, $matches)) {
-                        $data_array[$header]['created'] = $matches[1];
-                        $data_array[$header]['expires'] = $matches[2];
-                    } elseif (preg_match("/\s+[sp]key\[0\]:\s+\[(\d+)/i", $line, $matches)) {
-                        $data_array[$header]['size'] = $matches[1];
-                    }
-                } elseif ($header == 'literal' || $header == 'encrypted') {
-                    $data_array[$header] = true;
-                } elseif ($header) {
-                    if (preg_match("/version\s+\d+,\s+created\s+(\d+)/i", $line, $matches)) {
-                        $data_array['signature'][$header]['sig_' . $sig_id]['created'] = $matches[1];
-                    } elseif (isset($data_array['signature'][$header]['sig_' . $sig_id]['created']) &&
-                              preg_match('/expires after (\d+y\d+d\d+h\d+m)\)$/', $line, $matches)) {
-                        $expires = $matches[1];
-                        preg_match('/^(\d+)y(\d+)d(\d+)h(\d+)m$/', $expires, $matches);
-                        list(, $years, $days, $hours, $minutes) = $matches;
-                        $data_array['signature'][$header]['sig_' . $sig_id]['expires'] =
-                            strtotime('+ ' . $years . ' years + ' . $days . ' days + ' . $hours . ' hours + ' . $minutes . ' minutes', $data_array['signature'][$header]['sig_' . $sig_id]['created']);
-                    } elseif (preg_match("/digest algo\s+(\d{1})/", $line, $matches)) {
-                        $micalg = $this->_hashAlg[$matches[1]];
-                        $data_array['signature'][$header]['sig_' . $sig_id]['micalg'] = $micalg;
-                        if ($header == '_SIGNATURE') {
-                            /* Likely a signature block, not a key. */
-                            $data_array['signature']['_SIGNATURE']['micalg'] = $micalg;
-                        }
-                        if ($sig_id == $keyid) {
-                            /* Self signing signature - we can assume
-                             * the micalg value from this signature is
-                             * that for the key */
-                            $data_array['signature']['_SIGNATURE']['micalg'] = $micalg;
-                            $data_array['signature'][$header]['micalg'] = $micalg;
-                        }
-                    }
-                }
-            }
-        }
-
-        $keyid && $data_array['keyid'] = $keyid;
-
-        return $data_array;
-    }
-
-    protected function _pgpPacketInformationHelper($a)
-    {
-        return chr(hexdec($a[1]));
-    }
-
-    /**
-     * Returns human readable information on a PGP key.
-     *
-     * @param string $pgpdata  The PGP data block.
-     *
-     * @return string  Tabular information on the PGP key.
-     */
-    public function pgpPrettyKey($pgpdata)
-    {
-        $msg = '';
-        $packet_info = $this->pgpPacketInformation($pgpdata);
-        $fingerprints = $this->getFingerprintsFromKey($pgpdata);
-
-        if (!empty($packet_info['signature'])) {
-            /* Making the property names the same width for all
-             * localizations .*/
-            $leftrow = array(_("Name"), _("Key Type"), _("Key Creation"),
-                             _("Expiration Date"), _("Key Length"),
-                             _("Comment"), _("E-Mail"), _("Hash-Algorithm"),
-                             _("Key ID"), _("Key Fingerprint"));
-            $leftwidth = array_map('strlen', $leftrow);
-            $maxwidth  = max($leftwidth) + 2;
-            array_walk($leftrow, array($this, '_pgpPrettyKeyFormatter'), $maxwidth);
-
-            foreach (array_keys($packet_info['signature']) as $uid_idx) {
-                if ($uid_idx == '_SIGNATURE') {
-                    continue;
-                }
-                $key_info = $this->pgpPacketSignatureByUidIndex($pgpdata, $uid_idx);
-
-                if (!empty($key_info['keyid'])) {
-                    $key_info['keyid'] = $this->_getKeyIDString($key_info['keyid']);
-                } else {
-                    $key_info['keyid'] = null;
-                }
-
-                $fingerprint = isset($fingerprints[$key_info['keyid']]) ? $fingerprints[$key_info['keyid']] : null;
-
-                $msg .= $leftrow[0] . (isset($key_info['name']) ? stripcslashes($key_info['name']) : '') . "\n"
-                    . $leftrow[1] . (($key_info['key_type'] == 'public_key') ? _("Public Key") : _("Private Key")) . "\n"
-                    . $leftrow[2] . strftime("%D", $key_info['key_created']) . "\n"
-                    . $leftrow[3] . (empty($key_info['key_expires']) ? '[' . _("Never") . ']' : strftime("%D", $key_info['key_expires'])) . "\n"
-                    . $leftrow[4] . $key_info['key_size'] . " Bytes\n"
-                    . $leftrow[5] . (empty($key_info['comment']) ? '[' . _("None") . ']' : $key_info['comment']) . "\n"
-                    . $leftrow[6] . (empty($key_info['email']) ? '[' . _("None") . ']' : $key_info['email']) . "\n"
-                    . $leftrow[7] . (empty($key_info['micalg']) ? '[' . _("Unknown") . ']' : $key_info['micalg']) . "\n"
-                    . $leftrow[8] . (empty($key_info['keyid']) ? '[' . _("Unknown") . ']' : $key_info['keyid']) . "\n"
-                    . $leftrow[9] . (empty($fingerprint) ? '[' . _("Unknown") . ']' : $fingerprint) . "\n\n";
-            }
-        }
-
-        return $msg;
-    }
-
-    protected function _pgpPrettyKeyFormatter(&$s, $k, $m)
-    {
-        $s .= ':' . str_repeat(' ', $m - String::length($s));
-    }
-
-    protected function _getKeyIDString($keyid)
-    {
-        /* Get the 8 character key ID string. */
-        if (strpos($keyid, '0x') === 0) {
-            $keyid = substr($keyid, 2);
-        }
-        if (strlen($keyid) > 8) {
-            $keyid = substr($keyid, -8);
-        }
-        return '0x' . $keyid;
-    }
-
-    /**
-     * Returns only information on the first ID that matches the email address
-     * input.
-     *
-     * @param string $pgpdata  The PGP data block.
-     * @param string $email    An e-mail address.
-     *
-     * @return array  An array with information on the PGP data block. If an
-     *                element is not present in the data block, it will
-     *                likewise not be set in the array.
-     * <pre>
-     * Array Fields:
-     * -------------
-     * key_created  =>  Key creation - UNIX timestamp
-     * key_expires  =>  Key expiration - UNIX timestamp (0 = never expires)
-     * key_size     =>  Size of the key in bits
-     * key_type     =>  The key type (public_key or secret_key)
-     * name         =>  Full Name
-     * comment      =>  Comment
-     * email        =>  E-mail Address
-     * keyid        =>  16-bit hex value
-     * created      =>  Signature creation - UNIX timestamp
-     * micalg       =>  The hash used to create the signature
-     * </pre>
-     */
-    public function pgpPacketSignature($pgpdata, $email)
-    {
-        $data = $this->pgpPacketInformation($pgpdata);
-        $key_type = null;
-        $return_array = array();
-
-        /* Check that [signature] key exists. */
-        if (!isset($data['signature'])) {
-            return $return_array;
-        }
-
-        /* Store the signature information now. */
-        if (($email == '_SIGNATURE') &&
-            isset($data['signature']['_SIGNATURE'])) {
-            foreach ($data['signature'][$email] as $key => $value) {
-                $return_array[$key] = $value;
-            }
-        } else {
-            $uid_idx = 1;
-
-            while (isset($data['signature']['id' . $uid_idx])) {
-                if ($data['signature']['id' . $uid_idx]['email'] == $email) {
-                    foreach ($data['signature']['id' . $uid_idx] as $key => $val) {
-                        $return_array[$key] = $val;
-                    }
-                    break;
-                }
-                $uid_idx++;
-            }
-        }
-
-        return $this->_pgpPacketSignature($data, $return_array);
-    }
-
-    /**
-     * Returns information on a PGP signature embedded in PGP data.  Similar
-     * to pgpPacketSignature(), but returns information by unique User ID
-     * Index (format id{n} where n is an integer of 1 or greater).
-     *
-     * @param string $pgpdata  See pgpPacketSignature().
-     * @param string $uid_idx  The UID index.
-     *
-     * @return array  See pgpPacketSignature().
-     */
-    public function pgpPacketSignatureByUidIndex($pgpdata, $uid_idx)
-    {
-        $data = $this->pgpPacketInformation($pgpdata);
-        $key_type = null;
-        $return_array = array();
-
-        /* Search for the UID index. */
-        if (!isset($data['signature']) ||
-            !isset($data['signature'][$uid_idx])) {
-            return $return_array;
-        }
-
-        /* Store the signature information now. */
-        foreach ($data['signature'][$uid_idx] as $key => $value) {
-            $return_array[$key] = $value;
-        }
-
-        return $this->_pgpPacketSignature($data, $return_array);
-    }
-
-    /**
-     * Adds some data to the pgpPacketSignature*() function array.
-     *
-     * @param array $data      See pgpPacketSignature().
-     * @param array $retarray  The return array.
-     *
-     * @return array  The return array.
-     */
-    protected function _pgpPacketSignature($data, $retarray)
-    {
-        /* If empty, return now. */
-        if (empty($retarray)) {
-            return $retarray;
-        }
-
-        $key_type = null;
-
-        /* Store any public/private key information. */
-        if (isset($data['public_key'])) {
-            $key_type = 'public_key';
-        } elseif (isset($data['secret_key'])) {
-            $key_type = 'secret_key';
-        }
-
-        if ($key_type) {
-            $retarray['key_type'] = $key_type;
-            if (isset($data[$key_type]['created'])) {
-                $retarray['key_created'] = $data[$key_type]['created'];
-            }
-            if (isset($data[$key_type]['expires'])) {
-                $retarray['key_expires'] = $data[$key_type]['expires'];
-            }
-            if (isset($data[$key_type]['size'])) {
-                $retarray['key_size'] = $data[$key_type]['size'];
-            }
-        }
-
-        return $retarray;
-    }
-
-    /**
-     * Returns the key ID of the key used to sign a block of PGP data.
-     *
-     * @param string $text  The PGP signed text block.
-     *
-     * @return string  The key ID of the key used to sign $text.
-     */
-    public function getSignersKeyID($text)
-    {
-        $keyid = null;
-
-        $input = $this->_createTempFile('horde-pgp');
-
-        $fp = fopen($input, 'w+');
-        fputs($fp, $text);
-        fclose($fp);
-
-        $cmdline = array(
-            '--verify',
-            $input
-        );
-        $result = $this->_callGpg($cmdline, 'r', null, true, true);
-        if (preg_match('/gpg:\sSignature\smade.*ID\s+([A-F0-9]{8})\s+/', $result->stderr, $matches)) {
-            $keyid = $matches[1];
-        }
-
-        return $keyid;
-    }
-
-    /**
-     * Verify a passphrase for a given public/private keypair.
-     *
-     * @param string $public_key   The user's PGP public key.
-     * @param string $private_key  The user's PGP private key.
-     * @param string $passphrase   The user's passphrase.
-     *
-     * @return boolean  Returns true on valid passphrase, false on invalid
-     *                  passphrase, and PEAR_Error on error.
-     */
-    public function verifyPassphrase($public_key, $private_key, $passphrase)
-    {
-        /* Encrypt a test message. */
-        $result = $this->encrypt('Test', array('type' => 'message', 'pubkey' => $public_key));
-        if (is_a($result, 'PEAR_Error')) {
-            return false;
-        }
-
-        /* Try to decrypt the message. */
-        $result = $this->decrypt($result, array('type' => 'message', 'pubkey' => $public_key, 'privkey' => $private_key, 'passphrase' => $passphrase));
-        if (is_a($result, 'PEAR_Error')) {
-            return false;
-        }
-
-        return true;
-    }
-
-    /**
-     * Parses a message into text and PGP components.
-     *
-     * @param string $text  The text to parse.
-     *
-     * @return array  An array with the parsed text, returned in blocks of
-     *                text corresponding to their actual order. Keys:
-     * <pre>
-     * 'type' -  (integer) The type of data contained in block.
-     *           Valid types are defined at the top of this class
-     *           (the ARMOR_* constants).
-     * 'data' - (array) The data for each section. Each line has been stripped
-     *          of EOL characters.
-     * </pre>
-     */
-    public function parsePGPData($text)
-    {
-        $data = array();
-        $temp = array(
-            'type' => self::ARMOR_TEXT
-        );
-
-        $buffer = explode("\n", $text);
-        while (list(,$val) = each($buffer)) {
-            $val = rtrim($val, "\r");
-            if (preg_match('/^-----(BEGIN|END) PGP ([^-]+)-----\s*$/', $val, $matches)) {
-                if (isset($temp['data'])) {
-                    $data[] = $temp;
-                }
-                $temp= array();
-
-                if ($matches[1] == 'BEGIN') {
-                    $temp['type'] = $this->_armor[$matches[2]];
-                    $temp['data'][] = $val;
-                } elseif ($matches[1] == 'END') {
-                    $temp['type'] = self::ARMOR_TEXT;
-                    $data[count($data) - 1]['data'][] = $val;
-                }
-            } else {
-                $temp['data'][] = $val;
-            }
-        }
-
-        if (isset($temp['data']) &&
-            ((count($temp['data']) > 1) || !empty($temp['data'][0]))) {
-            $data[] = $temp;
-        }
-
-        return $data;
-    }
-
-    /**
-     * Returns a PGP public key from a public keyserver.
-     *
-     * @param string $keyid    The key ID of the PGP key.
-     * @param string $server   The keyserver to use.
-     * @param float $timeout   The keyserver timeout.
-     * @param string $address  The email address of the PGP key.
-     *
-     * @return string  The PGP public key, or PEAR_Error on error.
-     */
-    public function getPublicKeyserver($keyid,
-                                       $server = self::KEYSERVER_PUBLIC,
-                                       $timeout = self::KEYSERVER_TIMEOUT,
-                                       $address = null)
-    {
-        if (empty($keyid) && !empty($address)) {
-            $keyid = $this->getKeyID($address, $server, $timeout);
-            if (is_a($keyid, 'PEAR_Error')) {
-                return $keyid;
-            }
-        }
-
-        /* Connect to the public keyserver. */
-        $uri = '/pks/lookup?op=get&search=' . $this->_getKeyIDString($keyid);
-        $output = $this->_connectKeyserver('GET', $server, $uri, '', $timeout);
-        if (is_a($output, 'PEAR_Error')) {
-            return $output;
-        }
-
-        /* Strip HTML Tags from output. */
-        if (($start = strstr($output, '-----BEGIN'))) {
-            $length = strpos($start, '-----END') + 34;
-            return substr($start, 0, $length);
-        } else {
-            return PEAR::raiseError(_("Could not obtain public key from the keyserver."), 'horde.error');
-        }
-    }
-
-    /**
-     * Sends a PGP public key to a public keyserver.
-     *
-     * @param string $pubkey  The PGP public key
-     * @param string $server  The keyserver to use.
-     * @param float $timeout  The keyserver timeout.
-     *
-     * @return PEAR_Error  PEAR_Error on error/failure.
-     */
-    public function putPublicKeyserver($pubkey,
-                                       $server = self::KEYSERVER_PUBLIC,
-                                       $timeout = self::KEYSERVER_TIMEOUT)
-    {
-        /* Get the key ID of the public key. */
-        $info = $this->pgpPacketInformation($pubkey);
-
-        /* See if the public key already exists on the keyserver. */
-        if (!is_a($this->getPublicKeyserver($info['keyid'], $server, $timeout), 'PEAR_Error')) {
-            return PEAR::raiseError(_("Key already exists on the public keyserver."), 'horde.warning');
-        }
-
-        /* Connect to the public keyserver. _connectKeyserver()
-         * returns a PEAR_Error object on error and the output text on
-         * success. */
-        $pubkey = 'keytext=' . urlencode(rtrim($pubkey));
-        $cmd = array(
-            'Host: ' . $server . ':11371',
-            'User-Agent: Horde Application Framework 3.2',
-            'Content-Type: application/x-www-form-urlencoded',
-            'Content-Length: ' . strlen($pubkey),
-            'Connection: close',
-            '',
-            $pubkey
-        );
-
-        $result = $this->_connectKeyserver('POST', $server, '/pks/add', implode("\r\n", $cmd), $timeout);
-        if (is_a($result, 'PEAR_Error')) {
-            return $result;
-        }
-    }
-
-    /**
-     * Returns the first matching key ID for an email address from a
-     * public keyserver.
-     *
-     * @param string $address  The email address of the PGP key.
-     * @param string $server   The keyserver to use.
-     * @param float $timeout   The keyserver timeout.
-     *
-     * @return string  The PGP key ID, or PEAR_Error on error.
-     */
-    public function getKeyID($address, $server = self::KEYSERVER_PUBLIC,
-                      $timeout = self::KEYSERVER_TIMEOUT)
-    {
-        /* Connect to the public keyserver. */
-        $uri = '/pks/lookup?op=index&options=mr&search=' . urlencode($address);
-        $output = $this->_connectKeyserver('GET', $server, $uri, '', $timeout);
-        if (is_a($output, 'PEAR_Error')) {
-            return $output;
-        }
-
-        if (($start = strstr($output, '-----BEGIN PGP PUBLIC KEY BLOCK'))) {
-            /* The server returned the matching key immediately. */
-            $length = strpos($start, '-----END PGP PUBLIC KEY BLOCK') + 34;
-            $info = $this->pgpPacketInformation(substr($start, 0, $length));
-            if (!empty($info['keyid']) &&
-                (empty($info['public_key']['expires']) ||
-                 $info['public_key']['expires'] > time())) {
-                return $info['keyid'];
-            }
-        } elseif (strpos($output, 'pub:') !== false) {
-            $output = explode("\n", $output);
-            $keyids = array();
-            foreach ($output as $line) {
-                if (substr($line, 0, 4) == 'pub:') {
-                    $line = explode(':', $line);
-                    /* Ignore invalid lines and expired keys. */
-                    if (count($line) != 7 ||
-                        (!empty($line[5]) && $line[5] <= time())) {
-                        continue;
-                    }
-                    $keyids[$line[4]] = $line[1];
-                }
-            }
-            /* Sort by timestamp to use the newest key. */
-            if (count($keyids)) {
-                ksort($keyids);
-                return array_pop($keyids);
-            }
-        }
-
-        return PEAR::raiseError(_("Could not obtain public key from the keyserver."));
-    }
-
-    /**
-     * Get the fingerprints from a key block.
-     *
-     * @param string $pgpdata  The PGP data block.
-     *
-     * @return array The fingerprints in $pgpdata indexed by key id.
-     */
-    public function getFingerprintsFromKey($pgpdata)
-    {
-        $fingerprints = array();
-
-        /* Store the key in a temporary keyring. */
-        $keyring = $this->_putInKeyring($pgpdata);
-
-        /* Options for the GPG binary. */
-        $cmdline = array(
-            '--fingerprint',
-            $keyring,
-        );
-
-        $result = $this->_callGpg($cmdline, 'r');
-        if (!$result || !$result->stdout) {
-            return $fingerprints;
-        }
-
-        /* Parse fingerprints and key ids from output. */
-        $lines = explode("\n", $result->stdout);
-        $keyid = null;
-        foreach ($lines as $line) {
-            if (preg_match('/pub\s+\w+\/(\w{8})/', $line, $matches)) {
-                $keyid = '0x' . $matches[1];
-            } elseif ($keyid && preg_match('/^\s+[\s\w]+=\s*([\w\s]+)$/m', $line, $matches)) {
-                $fingerprints[$keyid] = trim($matches[1]);
-                $keyid = null;
-            }
-        }
-
-        return $fingerprints;
-    }
-
-    /**
-     * Connects to a public key server via HKP (Horrowitz Keyserver Protocol).
-     *
-     * @param string $method   POST, GET, etc.
-     * @param string $server   The keyserver to use.
-     * @param string $uri      The URI to access (relative to the server).
-     * @param string $command  The PGP command to run.
-     * @param float $timeout   The timeout value.
-     *
-     * @return string  The text from standard output on success, or PEAR_Error
-     *                 on error/failure.
-     */
-    protected function _connectKeyserver($method, $server, $resource,
-                                         $command, $timeout)
-    {
-        $connRefuse = 0;
-        $output = '';
-
-        $port = '11371';
-        if (!empty($GLOBALS['conf']['http']['proxy']['proxy_host'])) {
-            $resource = 'http://' . $server . ':' . $port . $resource;
-
-            $server = $GLOBALS['conf']['http']['proxy']['proxy_host'];
-            if (!empty($GLOBALS['conf']['http']['proxy']['proxy_port'])) {
-                $port = $GLOBALS['conf']['http']['proxy']['proxy_port'];
-            } else {
-                $port = 80;
-            }
-        }
-
-        $command = $method . ' ' . $resource . ' HTTP/1.0' . ($command ? "\r\n" . $command : '');
-
-        /* Attempt to get the key from the keyserver. */
-        do {
-            $connError = false;
-            $errno = $errstr = null;
-
-            /* The HKP server is located on port 11371. */
-            $fp = @fsockopen($server, $port, $errno, $errstr, $timeout);
-            if (!$fp) {
-                $connError = true;
-            } else {
-                fputs($fp, $command . "\n\n");
-                while (!feof($fp)) {
-                    $output .= fgets($fp, 1024);
-                }
-                fclose($fp);
-            }
-
-            if ($connError) {
-                if (++$connRefuse === self::KEYSERVER_REFUSE) {
-                    if ($errno == 0) {
-                        $output = PEAR::raiseError(_("Connection refused to the public keyserver."), 'horde.error');
-                    } else {
-                        $output = PEAR::raiseError(sprintf(_("Connection refused to the public keyserver. Reason: %s (%s)"), String::convertCharset($errstr, NLS::getExternalCharset()), $errno), 'horde.error');
-                    }
-                    break;
-                }
-            }
-        } while ($connError);
-
-        return $output;
-    }
-
-    /**
-     * Encrypts text using PGP.
-     *
-     * @param string $text   The text to be PGP encrypted.
-     * @param array $params  The parameters needed for encryption.
-     *                       See the individual _encrypt*() functions for the
-     *                       parameter requirements.
-     *
-     * @return string  The encrypted message, or PEAR_Error on error.
-     */
-    public function encrypt($text, $params = array())
-    {
-        if (isset($params['type'])) {
-            if ($params['type'] === 'message') {
-                return $this->_encryptMessage($text, $params);
-            } elseif ($params['type'] === 'signature') {
-                return $this->_encryptSignature($text, $params);
-            }
-        }
-    }
-
-    /**
-     * Decrypts text using PGP.
-     *
-     * @param string $text   The text to be PGP decrypted.
-     * @param array $params  The parameters needed for decryption.
-     *                       See the individual _decrypt*() functions for the
-     *                       parameter requirements.
-     *
-     * @return string  The decrypted message, or PEAR_Error on error.
-     */
-    public function decrypt($text, $params = array())
-    {
-        if (isset($params['type'])) {
-            if ($params['type'] === 'message') {
-                return $this->_decryptMessage($text, $params);
-            } elseif (($params['type'] === 'signature') ||
-                      ($params['type'] === 'detached-signature')) {
-                return $this->_decryptSignature($text, $params);
-            }
-        }
-    }
-
-    /**
-     * Returns whether a text has been encrypted symmetrically.
-     *
-     * @param string $text  The PGP encrypted text.
-     *
-     * @return boolean  True if the text is symmetricallly encrypted.
-     */
-    public function encryptedSymmetrically($text)
-    {
-        $cmdline = array(
-            '--decrypt',
-            '--batch'
-        );
-        $result = $this->_callGpg($cmdline, 'w', $text, true, true, true);
-        return strpos($result->stderr, 'gpg: encrypted with 1 passphrase') !== false;
-    }
-
-    /**
-     * Creates a temporary gpg keyring.
-     *
-     * @param string $type  The type of key to analyze. Either 'public'
-     *                      (Default) or 'private'
-     *
-     * @return string  Command line keystring option to use with gpg program.
-     */
-    protected function _createKeyring($type = 'public')
-    {
-        $type = String::lower($type);
-
-        if ($type === 'public') {
-            if (empty($this->_publicKeyring)) {
-                $this->_publicKeyring = $this->_createTempFile('horde-pgp');
-            }
-            return '--keyring ' . $this->_publicKeyring;
-        } elseif ($type === 'private') {
-            if (empty($this->_privateKeyring)) {
-                $this->_privateKeyring = $this->_createTempFile('horde-pgp');
-            }
-            return '--secret-keyring ' . $this->_privateKeyring;
-        }
-    }
-
-    /**
-     * Adds PGP keys to the keyring.
-     *
-     * @param mixed $keys   A single key or an array of key(s) to add to the
-     *                      keyring.
-     * @param string $type  The type of key(s) to add. Either 'public'
-     *                      (Default) or 'private'
-     *
-     * @return string  Command line keystring option to use with gpg program.
-     */
-    protected function _putInKeyring($keys = array(), $type = 'public')
-    {
-        $type = String::lower($type);
-
-        if (!is_array($keys)) {
-            $keys = array($keys);
-        }
-
-        /* Create the keyrings if they don't already exist. */
-        $keyring = $this->_createKeyring($type);
-
-        /* Store the key(s) in the keyring. */
-        $cmdline = array(
-            '--allow-secret-key-import',
-            '--fast-import',
-            $keyring
-        );
-        $this->_callGpg($cmdline, 'w', array_values($keys));
-
-        return $keyring;
-    }
-
-    /**
-     * Encrypts a message in PGP format using a public key.
-     *
-     * @param string $text   The text to be encrypted.
-     * @param array $params  The parameters needed for encryption.
-     * <pre>
-     * Parameters:
-     * ===========
-     * 'type'       => 'message' (REQUIRED)
-     * 'symmetric'  => Whether to use symmetric instead of asymmetric
-     *                 encryption (defaults to false)
-     * 'recips'     => An array with the e-mail address of the recipient as
-     *                 the key and that person's public key as the value.
-     *                 (REQUIRED if 'symmetric' is false)
-     * 'passphrase' => The passphrase for the symmetric encryption (REQUIRED if
-     *                 'symmetric' is true)
-     * </pre>
-     *
-     * @return string  The encrypted message, or PEAR_Error on error.
-     */
-    protected function _encryptMessage($text, $params)
-    {
-        /* Create temp files for input. */
-        $input = $this->_createTempFile('horde-pgp');
-        $fp = fopen($input, 'w+');
-        fputs($fp, $text);
-        fclose($fp);
-
-        /* Build command line. */
-        $cmdline = array(
-            '--armor',
-            '--batch',
-            '--always-trust'
-        );
-        if (empty($params['symmetric'])) {
-            /* Store public key in temporary keyring. */
-            $keyring = $this->_putInKeyring(array_values($params['recips']));
-
-            $cmdline[] = $keyring;
-            $cmdline[] = '--encrypt';
-            foreach (array_keys($params['recips']) as $val) {
-                $cmdline[] = '--recipient ' . $val;
-            }
-        } else {
-            $cmdline[] = '--symmetric';
-            $cmdline[] = '--passphrase-fd 0';
-        }
-        $cmdline[] = $input;
-
-        /* Encrypt the document. */
-        $result = $this->_callGpg($cmdline, 'w', empty($params['symmetric']) ? null : $params['passphrase'], true, true);
-        if (empty($result->output)) {
-            $error = preg_replace('/\n.*/', '', $result->stderr);
-            return PEAR::raiseError(_("Could not PGP encrypt message: ") . $error, 'horde.error');
-        }
-
-        return $result->output;
-    }
-
-    /**
-     * Signs a message in PGP format using a private key.
-     *
-     * @param string $text   The text to be signed.
-     * @param array $params  The parameters needed for signing.
-     * <pre>
-     * Parameters:
-     * ===========
-     * 'type'        =>  'signature' (REQUIRED)
-     * 'pubkey'      =>  PGP public key. (REQUIRED)
-     * 'privkey'     =>  PGP private key. (REQUIRED)
-     * 'passphrase'  =>  Passphrase for PGP Key. (REQUIRED)
-     * 'sigtype'     =>  Determine the signature type to use. (Optional)
-     *                   'cleartext'  --  Make a clear text signature
-     *                   'detach'     --  Make a detached signature (DEFAULT)
-     * </pre>
-     *
-     * @return string  The signed message, or PEAR_Error on error.
-     */
-    protected function _encryptSignature($text, $params)
-    {
-        /* Check for required parameters. */
-        if (!isset($params['pubkey']) ||
-            !isset($params['privkey']) ||
-            !isset($params['passphrase'])) {
-            return PEAR::raiseError(_("A public PGP key, private PGP key, and passphrase are required to sign a message."), 'horde.error');
-        }
-
-        /* Create temp files for input. */
-        $input = $this->_createTempFile('horde-pgp');
-
-        /* Encryption requires both keyrings. */
-        $pub_keyring = $this->_putInKeyring(array($params['pubkey']));
-        $sec_keyring = $this->_putInKeyring(array($params['privkey']), 'private');
-
-        /* Store message in temporary file. */
-        $fp = fopen($input, 'w+');
-        fputs($fp, $text);
-        fclose($fp);
-
-        /* Determine the signature type to use. */
-        $cmdline = array();
-        if (isset($params['sigtype']) &&
-            $params['sigtype'] == 'cleartext') {
-            $sign_type = '--clearsign';
-        } else {
-            $sign_type = '--detach-sign';
-        }
-
-        /* Additional GPG options. */
-        $cmdline += array(
-            '--armor',
-            '--batch',
-            '--passphrase-fd 0',
-            $sec_keyring,
-            $pub_keyring,
-            $sign_type,
-            $input
-        );
-
-        /* Sign the document. */
-        $result = $this->_callGpg($cmdline, 'w', $params['passphrase'], true, true);
-        if (empty($result->output)) {
-            $error = preg_replace('/\n.*/', '', $result->stderr);
-            return PEAR::raiseError(_("Could not PGP sign message: ") . $error, 'horde.error');
-        } else {
-            return $result->output;
-        }
-    }
-
-    /**
-     * Decrypts an PGP encrypted message using a private/public keypair and a
-     * passhprase.
-     *
-     * @param string $text   The text to be decrypted.
-     * @param array $params  The parameters needed for decryption.
-     * <pre>
-     * Parameters:
-     * ===========
-     * 'type'        =>  'message' (REQUIRED)
-     * 'pubkey'      =>  PGP public key. (REQUIRED for asymmetric encryption)
-     * 'privkey'     =>  PGP private key. (REQUIRED for asymmetric encryption)
-     * 'passphrase'  =>  Passphrase for PGP Key. (REQUIRED)
-     * </pre>
-     *
-     * @return stdClass  An object with the following properties, or PEAR_Error
-     *                   on error:
-     * <pre>
-     * 'message'     -  The decrypted message.
-     * 'sig_result'  -  The result of the signature test.
-     * </pre>
-     */
-    protected function _decryptMessage($text, $params)
-    {
-        $good_sig_flag = false;
-
-        /* Check for required parameters. */
-        if (!isset($params['passphrase']) && empty($params['no_passphrase'])) {
-            return PEAR::raiseError(_("A passphrase is required to decrypt a message."), 'horde.error');
-        }
-
-        /* Create temp files. */
-        $input = $this->_createTempFile('horde-pgp');
-
-        /* Store message in file. */
-        $fp = fopen($input, 'w+');
-        fputs($fp, $text);
-        fclose($fp);
-
-        /* Build command line. */
-        $cmdline = array(
-            '--always-trust',
-            '--armor',
-            '--batch'
-        );
-        if (empty($param['no_passphrase'])) {
-            $cmdline[] = '--passphrase-fd 0';
-        }
-        if (!empty($params['pubkey']) && !empty($params['privkey'])) {
-            /* Decryption requires both keyrings. */
-            $pub_keyring = $this->_putInKeyring(array($params['pubkey']));
-            $sec_keyring = $this->_putInKeyring(array($params['privkey']), 'private');
-            $cmdline[] = $sec_keyring;
-            $cmdline[] = $pub_keyring;
-        }
-        $cmdline[] = '--decrypt';
-        $cmdline[] = $input;
-
-        /* Decrypt the document now. */
-        if (empty($params['no_passphrase'])) {
-            $result = $this->_callGpg($cmdline, 'w', $params['passphrase'], true, true);
-        } else {
-            $result = $this->_callGpg($cmdline, 'r', null, true, true);
-        }
-        if (empty($result->output)) {
-            $error = preg_replace('/\n.*/', '', $result->stderr);
-            return PEAR::raiseError(_("Could not decrypt PGP data: ") . $error, 'horde.error');
-        }
-
-        /* Create the return object. */
-        $ob = new stdClass;
-        $ob->message = $result->output;
-
-        /* Check the PGP signature. */
-        $sig_check = $this->_checkSignatureResult($result->stderr);
-        if (is_a($sig_check, 'PEAR_Error')) {
-            $ob->sig_result = $sig_check;
-        } else {
-            $ob->sig_result = ($sig_check) ? $result->stderr : '';
-        }
-
-        return $ob;
-    }
-
-    /**
-     * Decrypts an PGP signed message using a public key.
-     *
-     * @param string $text   The text to be verified.
-     * @param array $params  The parameters needed for verification.
-     * <pre>
-     * Parameters:
-     * ===========
-     * 'type'       =>  'signature' or 'detached-signature' (REQUIRED)
-     * 'pubkey'     =>  PGP public key. (REQUIRED)
-     * 'signature'  =>  PGP signature block. (REQUIRED for detached signature)
-     * </pre>
-     *
-     * @return string  The verification message from gpg. If no signature,
-     *                 returns empty string, and PEAR_Error on error.
-     */
-    protected function _decryptSignature($text, $params)
-    {
-        /* Check for required parameters. */
-        if (!isset($params['pubkey'])) {
-            return PEAR::raiseError(_("A public PGP key is required to verify a signed message."), 'horde.error');
-        }
-        if (($params['type'] === 'detached-signature') &&
-            !isset($params['signature'])) {
-            return PEAR::raiseError(_("The detached PGP signature block is required to verify the signed message."), 'horde.error');
-        }
-
-        $good_sig_flag = 0;
-
-        /* Create temp files for input. */
-        $input = $this->_createTempFile('horde-pgp');
-
-        /* Store public key in temporary keyring. */
-        $keyring = $this->_putInKeyring($params['pubkey']);
-
-        /* Store the message in a temporary file. */
-        $fp = fopen($input, 'w+');
-        fputs($fp, $text);
-        fclose($fp);
-
-        /* Options for the GPG binary. */
-        $cmdline = array(
-            '--armor',
-            '--always-trust',
-            '--batch',
-            '--charset ' . NLS::getCharset(),
-            $keyring,
-            '--verify'
-        );
-
-        /* Extra stuff to do if we are using a detached signature. */
-        if ($params['type'] === 'detached-signature') {
-            $sigfile = $this->_createTempFile('horde-pgp');
-            $cmdline[] = $sigfile . ' ' . $input;
-
-            $fp = fopen($sigfile, 'w+');
-            fputs($fp, $params['signature']);
-            fclose($fp);
-        } else {
-            $cmdline[] = $input;
-        }
-
-        /* Verify the signature.  We need to catch standard error output,
-         * since this is where the signature information is sent. */
-        $result = $this->_callGpg($cmdline, 'r', null, true, true);
-        $sig_result = $this->_checkSignatureResult($result->stderr);
-        if (is_a($sig_result, 'PEAR_Error')) {
-            return $sig_result;
-        } else {
-            return ($sig_result) ? $result->stderr : '';
-        }
-    }
-
-    /**
-     * Checks signature result from the GnuPG binary.
-     *
-     * @param string $result  The signature result.
-     *
-     * @return boolean  True if signature is good.
-     */
-    protected function _checkSignatureResult($result)
-    {
-        /* Good signature:
-         *   gpg: Good signature from "blah blah blah (Comment)"
-         * Bad signature:
-         *   gpg: BAD signature from "blah blah blah (Comment)" */
-        if (strpos($result, 'gpg: BAD signature') !== false) {
-            return PEAR::raiseError($result, 'horde.error');
-        } elseif (strpos($result, 'gpg: Good signature') !== false) {
-            return true;
-        } else {
-            return false;
-        }
-    }
-
-    /**
-     * Signs a MIME part using PGP.
-     *
-     * @param Horde_Mime_Part $mime_part  The object to sign.
-     * @param array $params               The parameters required for signing.
-     *                                    @see _encryptSignature().
-     *
-     * @return mixed  A Horde_Mime_Part object that is signed according to RFC
-     *                3156, or PEAR_Error on error.
-     */
-    public function signMIMEPart($mime_part, $params = array())
-    {
-        $params = array_merge($params, array('type' => 'signature', 'sigtype' => 'detach'));
-
-        /* RFC 3156 Requirements for a PGP signed message:
-         * + Content-Type params 'micalg' & 'protocol' are REQUIRED.
-         * + The digitally signed message MUST be constrained to 7 bits.
-         * + The MIME headers MUST be a part of the signed data. */
-
-        $mime_part->strict7bit(true);
-        $msg_sign = $this->encrypt($mime_part->toCanonicalString(), $params);
-        if (is_a($msg_sign, 'PEAR_Error')) {
-            return $msg_sign;
-        }
-
-        /* Add the PGP signature. */
-        $charset = NLS::getEmailCharset();
-        $pgp_sign = new Horde_Mime_Part();
-        $pgp_sign->setType('application/pgp-signature');
-        $pgp_sign->setCharset($charset);
-        $pgp_sign->setDisposition('inline');
-        $pgp_sign->setDescription(String::convertCharset(_("PGP Digital Signature"), NLS::getCharset(), $charset));
-        $pgp_sign->setContents($msg_sign);
-
-        /* Get the algorithim information from the signature. Since we are
-         * analyzing a signature packet, we need to use the special keyword
-         * '_SIGNATURE' - see Horde_Crypt_pgp. */
-        $sig_info = $this->pgpPacketSignature($msg_sign, '_SIGNATURE');
-
-        /* Setup the multipart MIME Part. */
-        $part = new Horde_Mime_Part();
-        $part->setType('multipart/signed');
-        $part->setContents('This message is in MIME format and has been PGP signed.' . "\n");
-        $part->addPart($mime_part);
-        $part->addPart($pgp_sign);
-        $part->setContentTypeParameter('protocol', 'application/pgp-signature');
-        $part->setContentTypeParameter('micalg', $sig_info['micalg']);
-
-        return $part;
-    }
-
-    /**
-     * Encrypts a MIME part using PGP.
-     *
-     * @param Horde_Mime_Part $mime_part  The object to encrypt.
-     * @param array $params               The parameters required for
-     *                                    encryption.
-     *                                    @see _encryptMessage().
-     *
-     * @return mixed  A Horde_Mime_Part object that is encrypted according to
-     *                RFC 3156, or PEAR_Error on error.
-     */
-    public function encryptMIMEPart($mime_part, $params = array())
-    {
-        $params = array_merge($params, array('type' => 'message'));
-
-        $signenc_body = $mime_part->toCanonicalString();
-        $message_encrypt = $this->encrypt($signenc_body, $params);
-        if (is_a($message_encrypt, 'PEAR_Error')) {
-            return $message_encrypt;
-        }
-
-        /* Set up MIME Structure according to RFC 3156. */
-        $charset = NLS::getEmailCharset();
-        $part = new Horde_Mime_Part();
-        $part->setType('multipart/encrypted');
-        $part->setCharset($charset);
-        $part->setContentTypeParameter('protocol', 'application/pgp-encrypted');
-        $part->setDescription(String::convertCharset(_("PGP Encrypted Data"), NLS::getCharset(), $charset));
-        $part->setContents('This message is in MIME format and has been PGP encrypted.' . "\n");
-
-        $part1 = new Horde_Mime_Part();
-        $part1->setType('application/pgp-encrypted');
-        $part1->setCharset(null);
-        $part1->setContents("Version: 1\n");
-        $part->addPart($part1);
-
-        $part2 = new Horde_Mime_Part();
-        $part2->setType('application/octet-stream');
-        $part2->setCharset(null);
-        $part2->setContents($message_encrypt);
-        $part2->setDisposition('inline');
-        $part->addPart($part2);
-
-        return $part;
-    }
-
-    /**
-     * Signs and encrypts a MIME part using PGP.
-     *
-     * @param Horde_Mime_Part $mime_part   The object to sign and encrypt.
-     * @param array $sign_params           The parameters required for
-     *                                     signing. @see _encryptSignature().
-     * @param array $encrypt_params        The parameters required for
-     *                                     encryption. @see _encryptMessage().
-     *
-     * @return mixed  A Horde_Mime_Part object that is signed and encrypted
-     *                according to RFC 3156, or PEAR_Error on error.
-     */
-    public function signAndEncryptMIMEPart($mime_part, $sign_params = array(),
-                                           $encrypt_params = array())
-    {
-        /* RFC 3156 requires that the entire signed message be encrypted.  We
-         * need to explicitly call using Horde_Crypt_pgp:: because we don't
-         * know whether a subclass has extended these methods. */
-        $part = $this->signMIMEPart($mime_part, $sign_params);
-        if (is_a($part, 'PEAR_Error')) {
-            return $part;
-        }
-        $part = $this->encryptMIMEPart($part, $encrypt_params);
-        if (is_a($part, 'PEAR_Error')) {
-            return $part;
-        }
-        $part->setContents('This message is in MIME format and has been PGP signed and encrypted.' . "\n");
-
-        $charset = NLS::getEmailCharset();
-        $part->setCharset($charset);
-        $part->setDescription(String::convertCharset(_("PGP Signed/Encrypted Data"), NLS::getCharset(), $charset));
-
-        return $part;
-    }
-
-    /**
-     * Generates a Horde_Mime_Part object, in accordance with RFC 3156, that
-     * contains a public key.
-     *
-     * @param string $key  The public key.
-     *
-     * @return Horde_Mime_Part  An object that contains the public key.
-     */
-    public function publicKeyMIMEPart($key)
-    {
-        include_once 'Horde/Mime/Part.php';
-
-        $charset = NLS::getEmailCharset();
-        $part = new Horde_Mime_Part();
-        $part->setType('application/pgp-keys');
-        $part->setCharset($charset);
-        $part->setDescription(String::convertCharset(_("PGP Public Key"), NLS::getCharset(), $charset));
-        $part->setContents($key);
-
-        return $part;
-    }
-
-    /**
-     * Function that handles interfacing with the GnuPG binary.
-     *
-     * @param array $options   Options and commands to pass to GnuPG.
-     * @param string $mode     'r' to read from stdout, 'w' to write to stdin.
-     * @param array $input     Input to write to stdin.
-     * @param boolean $output  If true, collect and store output in object returned.
-     * @param boolean $stderr  If true, collect and store stderr in object returned.
-     * @param boolean $verbose If true, run GnuPG with quiet flag.
-     *
-     * @return stdClass  Class with members output, stderr, and stdout.
-     */
-    protected function _callGpg($options, $mode, $input = array(),
-                                $output = false, $stderr = false,
-                                $verbose = false)
-    {
-        $data = new stdClass;
-        $data->output = null;
-        $data->stderr = null;
-        $data->stdout = null;
-
-        /* Verbose output? */
-        if (!$verbose) {
-            array_unshift($options, '--quiet');
-        }
-
-        /* Create temp files for output. */
-        if ($output) {
-            $output_file = $this->_createTempFile('horde-pgp', false);
-            array_unshift($options, '--output ' . $output_file);
-
-            /* Do we need standard error output? */
-            if ($stderr) {
-                $stderr_file = $this->_createTempFile('horde-pgp', false);
-                $options[] = '2> ' . $stderr_file;
-            }
-        }
-
-        /* Silence errors if not requested. */
-        if (!$output || !$stderr) {
-            $options[] = '2> /dev/null';
-        }
-
-        /* Build the command line string now. */
-        $cmdline = implode(' ', array_merge($this->_gnupg, $options));
-
-        if ($mode == 'w') {
-            $fp = popen($cmdline, 'w');
-            $win32 = !strncasecmp(PHP_OS, 'WIN', 3);
-
-            if (!is_array($input)) {
-                $input = array($input);
-            }
-            foreach ($input as $line) {
-                if ($win32 && (strpos($line, "\x0d\x0a") !== false)) {
-                    $chunks = explode("\x0d\x0a", $line);
-                    foreach ($chunks as $chunk) {
-                        fputs($fp, $chunk . "\n");
-                    }
-                } else {
-                    fputs($fp, $line . "\n");
-                }
-            }
-        } elseif ($mode == 'r') {
-            $fp = popen($cmdline, 'r');
-            while (!feof($fp)) {
-                $data->stdout .= fgets($fp, 1024);
-            }
-        }
-        pclose($fp);
-
-        if ($output) {
-            $data->output = file_get_contents($output_file);
-            unlink($output_file);
-            if ($stderr) {
-                $data->stderr = file_get_contents($stderr_file);
-                unlink($stderr_file);
-            }
-        }
-
-        return $data;
-    }
-
-    /**
-     * Generates a revocation certificate.
-     *
-     * @param string $key         The private key.
-     * @param string $email       The email to use for the key.
-     * @param string $passphrase  The passphrase to use for the key.
-     *
-     * @return string  The revocation certificate, or PEAR_Error on error.
-     */
-    public function generateRevocation($key, $email, $passphrase)
-    {
-        $keyring = $this->_putInKeyring($key, 'private');
-
-        /* Prepare the canned answers. */
-        $input = array();
-        $input[] = 'y'; // Really generate a revocation certificate
-        $input[] = '0'; // Refuse to specify a reason
-        $input[] = '';  // Empty comment
-        $input[] = 'y'; // Confirm empty comment
-        if (!empty($passphrase)) {
-            $input[] = $passphrase;
-        }
-
-        /* Run through gpg binary. */
-        $cmdline = array(
-            $keyring,
-            '--command-fd 0',
-            '--gen-revoke' . ' ' . $email,
-        );
-        $results = $this->_callGpg($cmdline, 'w', $input, true);
-
-        /* If the key is empty, something went wrong. */
-        if (empty($results->output)) {
-            return PEAR::raiseError(_("Revocation key not generated successfully."), 'horde.error');
-        }
-
-        return $results->output;
-    }
-
-}
diff --git a/framework/Crypt/lib/Horde/Crypt/smime.php b/framework/Crypt/lib/Horde/Crypt/smime.php
deleted file mode 100644 (file)
index 44d4eca..0000000
+++ /dev/null
@@ -1,1331 +0,0 @@
-<?php
-/**
- * Horde_Crypt_smime:: provides a framework for Horde applications to
- * interact with the OpenSSL library and implement S/MIME.
- *
- * Copyright 2002-2009 The Horde Project (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
- *
- * @author  Mike Cochrane <mike@graftonhall.co.nz>
- * @package Horde_Crypt
- */
-class Horde_Crypt_smime extends Horde_Crypt
-{
-    /**
-     * Object Identifers to name array.
-     *
-     * @var array
-     */
-    protected $_oids = array(
-        '2.5.4.3' => 'CommonName',
-        '2.5.4.4' => 'Surname',
-        '2.5.4.6' => 'Country',
-        '2.5.4.7' => 'Location',
-        '2.5.4.8' => 'StateOrProvince',
-        '2.5.4.9' => 'StreetAddress',
-        '2.5.4.10' => 'Organisation',
-        '2.5.4.11' => 'OrganisationalUnit',
-        '2.5.4.12' => 'Title',
-        '2.5.4.20' => 'TelephoneNumber',
-        '2.5.4.42' => 'GivenName',
-
-        '2.5.29.14' => 'id-ce-subjectKeyIdentifier',
-
-        '2.5.29.14' => 'id-ce-subjectKeyIdentifier',
-        '2.5.29.15' => 'id-ce-keyUsage',
-        '2.5.29.17' => 'id-ce-subjectAltName',
-        '2.5.29.19' => 'id-ce-basicConstraints',
-        '2.5.29.31' => 'id-ce-CRLDistributionPoints',
-        '2.5.29.32' => 'id-ce-certificatePolicies',
-        '2.5.29.35' => 'id-ce-authorityKeyIdentifier',
-        '2.5.29.37' => 'id-ce-extKeyUsage',
-
-        '1.2.840.113549.1.9.1' => 'Email',
-        '1.2.840.113549.1.1.1' => 'RSAEncryption',
-        '1.2.840.113549.1.1.2' => 'md2WithRSAEncryption',
-        '1.2.840.113549.1.1.4' => 'md5withRSAEncryption',
-        '1.2.840.113549.1.1.5' => 'SHA-1WithRSAEncryption',
-        '1.2.840.10040.4.3' => 'id-dsa-with-sha-1',
-
-        '1.3.6.1.5.5.7.3.2' => 'id_kp_clientAuth',
-
-        '2.16.840.1.113730.1.1' => 'netscape-cert-type',
-        '2.16.840.1.113730.1.2' => 'netscape-base-url',
-        '2.16.840.1.113730.1.3' => 'netscape-revocation-url',
-        '2.16.840.1.113730.1.4' => 'netscape-ca-revocation-url',
-        '2.16.840.1.113730.1.7' => 'netscape-cert-renewal-url',
-        '2.16.840.1.113730.1.8' => 'netscape-ca-policy-url',
-        '2.16.840.1.113730.1.12' => 'netscape-ssl-server-name',
-        '2.16.840.1.113730.1.13' => 'netscape-comment',
-    );
-
-    /**
-     * Constructor.
-     *
-     * @param array $params  Parameter array.
-     *                       'temp' => Location of temporary directory.
-     */
-    function __construct($params)
-    {
-        $this->_tempdir = $params['temp'];
-    }
-
-    /**
-     * Verify a passphrase for a given private key.
-     *
-     * @param string $private_key  The user's private key.
-     * @param string $passphrase   The user's passphrase.
-     *
-     * @return boolean  Returns true on valid passphrase, false on invalid
-     *                  passphrase.
-     *                  Returns PEAR_Error on error.
-     */
-    public function verifyPassphrase($private_key, $passphrase)
-    {
-        if (is_null($passphrase)) {
-            $res = openssl_pkey_get_private($private_key);
-        } else {
-            $res = openssl_pkey_get_private($private_key, $passphrase);
-        }
-
-        return is_resource($res);
-    }
-
-    /**
-     * Encrypt text using S/MIME.
-     *
-     * @param string $text   The text to be encrypted.
-     * @param array $params  The parameters needed for encryption.
-     *                       See the individual _encrypt*() functions for
-     *                       the parameter requirements.
-     *
-     * @return string  The encrypted message.
-     *                 Returns PEAR_Error object on error.
-     */
-    public function encrypt($text, $params = array())
-    {
-        /* Check for availability of OpenSSL PHP extension. */
-        $openssl = $this->checkForOpenSSL();
-        if (is_a($openssl, 'PEAR_Error')) {
-            return $openssl;
-        }
-
-        if (isset($params['type'])) {
-            if ($params['type'] === 'message') {
-                return $this->_encryptMessage($text, $params);
-            } elseif ($params['type'] === 'signature') {
-                return $this->_encryptSignature($text, $params);
-            }
-        }
-    }
-
-    /**
-     * Decrypt text via S/MIME.
-     *
-     * @param string $text   The text to be smime decrypted.
-     * @param array $params  The parameters needed for decryption.
-     *                       See the individual _decrypt*() functions for
-     *                       the parameter requirements.
-     *
-     * @return string  The decrypted message.
-     *                 Returns PEAR_Error object on error.
-     */
-    public function decrypt($text, $params = array())
-    {
-        /* Check for availability of OpenSSL PHP extension. */
-        $openssl = $this->checkForOpenSSL();
-        if (is_a($openssl, 'PEAR_Error')) {
-            return $openssl;
-        }
-
-        if (isset($params['type'])) {
-            if ($params['type'] === 'message') {
-                return $this->_decryptMessage($text, $params);
-            } elseif (($params['type'] === 'signature') ||
-                      ($params['type'] === 'detached-signature')) {
-                return $this->_decryptSignature($text, $params);
-            }
-        }
-    }
-
-    /**
-     * Verify a signature using via S/MIME.
-     *
-     * @param string $text  The multipart/signed data to be verified.
-     * @param mixed $certs  Either a single or array of root certificates.
-     *
-     * @return stdClass  Object with the following elements:
-     *                   'result' -> Returns true on success;
-     *                               PEAR_Error object on error.
-     *                   'cert' -> The certificate of the signer stored
-     *                             in the message (in PEM format).
-     *                   'email' -> The email of the signing person.
-     */
-    public function verify($text, $certs)
-    {
-        /* Check for availability of OpenSSL PHP extension. */
-        $openssl = $this->checkForOpenSSL();
-        if (is_a($openssl, 'PEAR_Error')) {
-            return $openssl;
-        }
-
-        /* Create temp files for input/output. */
-        $input = $this->_createTempFile('horde-smime');
-        $output = $this->_createTempFile('horde-smime');
-
-        /* Write text to file */
-        file_put_contents($input, $text);
-        unset($text);
-
-        $root_certs = array();
-        if (!is_array($certs)) {
-            $certs = array($certs);
-        }
-        foreach ($certs as $file) {
-            if (file_exists($file)) {
-                $root_certs[] = $file;
-            }
-        }
-
-        $ob = new stdClass;
-
-        if (!empty($root_certs)) {
-            $result = openssl_pkcs7_verify($input, 0, $output, $root_certs);
-            /* Message verified */
-            if ($result === true) {
-                $ob->result = true;
-                $ob->cert = file_get_contents($output);
-                $ob->email = $this->getEmailFromKey($ob->cert);
-                return $ob;
-            }
-        }
-
-        /* Try again without verfying the signer's cert */
-        $result = openssl_pkcs7_verify($input, PKCS7_NOVERIFY, $output);
-
-        if ($result === true) {
-            $ob->result = PEAR::raiseError(_("Message Verified Successfully but the signer's certificate could not be verified."), 'horde.warning');
-        } elseif ($result == -1) {
-            $ob->result = PEAR::raiseError(_("Verification failed - an unknown error has occurred."), 'horde.error');
-        } else {
-            $ob->result = PEAR::raiseError(_("Verification failed - this message may have been tampered with."), 'horde.error');
-        }
-
-        $ob->cert = file_get_contents($output);
-        $ob->email = $this->getEmailFromKey($ob->cert);
-
-        return $ob;
-    }
-
-    /**
-     * Extract the contents from signed S/MIME data.
-     *
-     * @param string $data     The signed S/MIME data.
-     * @param string $sslpath  The path to the OpenSSL binary.
-     *
-     * @return string  The contents embedded in the signed data.
-     *                 Returns PEAR_Error on error.
-     */
-    public function extractSignedContents($data, $sslpath)
-    {
-        /* Check for availability of OpenSSL PHP extension. */
-        $openssl = $this->checkForOpenSSL();
-        if (is_a($openssl, 'PEAR_Error')) {
-            return $openssl;
-        }
-
-        /* Create temp files for input/output. */
-        $input = $this->_createTempFile('horde-smime');
-        $output = $this->_createTempFile('horde-smime');
-
-        /* Write text to file. */
-        file_put_contents($input, $data);
-        unset($data);
-
-        exec($sslpath . ' smime -verify -noverify -nochain -in ' . $input . ' -out ' . $output);
-
-        $ret = file_get_contents($output);
-        return $ret
-            ? $ret
-            : PEAR::raiseError(_("OpenSSL error: Could not extract data from signed S/MIME part."), 'horde.error');
-    }
-
-    /**
-     * Sign a MIME part using S/MIME.
-     *
-     * @param Horde_Mime_Part $mime_part  The object to sign.
-     * @param array $params               The parameters required for signing.
-     *
-     * @return mixed  A Horde_Mime_Part object that is signed, or a
-     *                PEAR_Error object on error.
-     */
-    public function signMIMEPart($mime_part, $params)
-    {
-        /* Sign the part as a message */
-        $message = $this->encrypt($mime_part->toCanonicalString(), $params);
-        if (is_a($message, 'PEAR_Error')) {
-            return $message;
-        }
-
-        /* Break the result into its components */
-        $mime_message = Horde_Mime_Part::parseMessage($message);
-
-        $smime_sign = $mime_message->getPart('2');
-        $smime_sign->setDescription(_("S/MIME Cryptographic Signature"));
-        $smime_sign->transferDecodeContents();
-        $smime_sign->setTransferEncoding('base64');
-
-        $smime_part = new Horde_Mime_Part();
-        $smime_part->setType('multipart/signed');
-        $smime_part->setContents('This is a cryptographically signed message in MIME format.' . "\n");
-        $smime_part->setContentTypeParameter('protocol', 'application/pkcs7-signature');
-        $smime_part->setContentTypeParameter('micalg', 'sha1');
-        $smime_part->addPart($mime_part);
-        $smime_part->addPart($smime_sign);
-
-        return $smime_part;
-    }
-
-    /**
-     * Encrypt a MIME part using S/MIME.
-     *
-     * @param Horde_Mime_Part $mime_part  The object to encrypt.
-     * @param array $params               The parameters required for
-     *                                    encryption.
-     *
-     * @return mixed  A Horde_Mime_Part object that is encrypted or a
-     *                PEAR_Error on error.
-     */
-    public function encryptMIMEPart($mime_part, $params = array())
-    {
-        /* Sign the part as a message */
-        $message = $this->encrypt($mime_part->toCanonicalString(), $params);
-        if (is_a($message, 'PEAR_Error')) {
-            return $message;
-        }
-
-        /* Get charset for mime part description. */
-        $charset = NLS::getEmailCharset();
-
-        $msg = new Horde_Mime_Part();
-        $msg->setCharset($charset);
-        $msg->setDescription(String::convertCharset(_("S/MIME Encrypted Message"), NLS::getCharset(), $charset));
-        $msg->setDisposition('inline');
-        $msg->setType('application/pkcs7-mime');
-        $msg->setContentTypeParameter('smime-type', 'enveloped-data');
-        $msg->setContents(substr($message, strpos($message, "\n\n") + 2));
-
-        return $msg;
-    }
-
-    /**
-     * Encrypt a message in S/MIME format using a public key.
-     *
-     * @param string $text   The text to be encrypted.
-     * @param array $params  The parameters needed for encryption.
-     * <pre>
-     * Parameters:
-     * ===========
-     * 'type'   => 'message' (REQUIRED)
-     * 'pubkey' => public key (REQUIRED)
-     * </pre>
-     *
-     * @return string  The encrypted message.
-     *                 Return PEAR_Error object on error.
-     */
-    protected function _encryptMessage($text, $params)
-    {
-        /* Check for required parameters. */
-        if (!isset($params['pubkey'])) {
-            return PEAR::raiseError(_("A public S/MIME key is required to encrypt a message."), 'horde.error');
-        }
-
-        /* Create temp files for input/output. */
-        $input = $this->_createTempFile('horde-smime');
-        $output = $this->_createTempFile('horde-smime');
-
-        /* Store message in file. */
-        file_put_contents($input, $text);
-        unset($text);
-
-        /* Encrypt the document. */
-        if (openssl_pkcs7_encrypt($input, $output, $params['pubkey'], array())) {
-            $result = file_get_contents($output);
-            if (!empty($result)) {
-                return $this->_fixContentType($result, 'encrypt');
-            }
-        }
-
-        return PEAR::raiseError(_("Could not S/MIME encrypt message."), 'horde.error');
-    }
-
-    /**
-     * Sign a message in S/MIME format using a private key.
-     *
-     * @param string $text   The text to be signed.
-     * @param array $params  The parameters needed for signing.
-     * <pre>
-     * Parameters:
-     * ===========
-     * 'certs'       =>  Additional signing certs (Optional)
-     * 'passphrase'  =>  Passphrase for key (REQUIRED)
-     * 'privkey'     =>  Private key (REQUIRED)
-     * 'pubkey'      =>  Public key (REQUIRED)
-     * 'sigtype'     =>  Determine the signature type to use. (Optional)
-     *                   'cleartext'  --  Make a clear text signature
-     *                   'detach'     --  Make a detached signature (DEFAULT)
-     * 'type'        =>  'signature' (REQUIRED)
-     * </pre>
-     *
-     * @return string  The signed message.
-     *                 Return PEAR_Error object on error.
-     */
-    protected function _encryptSignature($text, $params)
-    {
-        /* Check for required parameters. */
-        if (!isset($params['pubkey']) ||
-            !isset($params['privkey']) ||
-            !array_key_exists('passphrase', $params)) {
-            return PEAR::raiseError(_("A public S/MIME key, private S/MIME key, and passphrase are required to sign a message."), 'horde.error');
-        }
-
-        /* Create temp files for input/output/certificates. */
-        $input = $this->_createTempFile('horde-smime');
-        $output = $this->_createTempFile('horde-smime');
-        $certs = $this->_createTempFile('horde-smime');
-
-        /* Store message in temporary file. */
-        file_put_contents($input, $text);
-        unset($text);
-
-        /* Store additional certs in temporary file. */
-        if (!empty($params['certs'])) {
-            file_put_contents($certs, $params['certs']);
-        }
-
-        /* Determine the signature type to use. */
-        if (isset($params['sigtype']) && ($params['sigtype'] == 'cleartext')) {
-            $flags = PKCS7_TEXT;
-        } else {
-            $flags = PKCS7_DETACHED;
-        }
-
-        $privkey = (is_null($params['passphrase'])) ? $params['privkey'] : array($params['privkey'], $params['passphrase']);
-
-        if (empty($params['certs'])) {
-            $res = openssl_pkcs7_sign($input, $output, $params['pubkey'], $privkey, array(), $flags);
-        } else {
-            $res = openssl_pkcs7_sign($input, $output, $params['pubkey'], $privkey, array(), $flags, $certs);
-        }
-
-        if (!$res) {
-            return PEAR::raiseError(_("Could not S/MIME sign message."), 'horde.error');
-        }
-
-        $data = file_get_contents($output);
-        return $this->_fixContentType($data, 'signature');
-    }
-
-    /**
-     * Decrypt an S/MIME encrypted message using a private/public keypair
-     * and a passhprase.
-     *
-     * @param string $text   The text to be decrypted.
-     * @param array $params  The parameters needed for decryption.
-     * <pre>
-     * Parameters:
-     * ===========
-     * 'type'        =>  'message' (REQUIRED)
-     * 'pubkey'      =>  public key. (REQUIRED)
-     * 'privkey'     =>  private key. (REQUIRED)
-     * 'passphrase'  =>  Passphrase for Key. (REQUIRED)
-     * </pre>
-     *
-     * @return string  The decrypted message.
-     *                 Returns PEAR_Error object on error.
-     */
-    protected function _decryptMessage($text, $params)
-    {
-        /* Check for required parameters. */
-        if (!isset($params['pubkey']) ||
-            !isset($params['privkey']) ||
-            !array_key_exists('passphrase', $params)) {
-            return PEAR::raiseError(_("A public S/MIME key, private S/MIME key, and passphrase are required to decrypt a message."), 'horde.error');
-        }
-
-        /* Create temp files for input/output. */
-        $input = $this->_createTempFile('horde-smime');
-        $output = $this->_createTempFile('horde-smime');
-
-        /* Store message in file. */
-        file_put_contents($input, $text);
-        unset($text);
-
-        $privkey = (is_null($params['passphrase'])) ? $params['privkey'] : array($params['privkey'], $params['passphrase']);
-        if (openssl_pkcs7_decrypt($input, $output, $params['pubkey'], $privkey)) {
-            return file_get_contents($output);
-        }
-
-        return PEAR::raiseError(_("Could not decrypt S/MIME data."), 'horde.error');
-    }
-
-    /**
-     * Sign and Encrypt a MIME part using S/MIME.
-     *
-     * @param Horde_Mime_Part $mime_part   The object to sign and encrypt.
-     * @param array $sign_params           The parameters required for
-     *                                     signing. @see _encryptSignature().
-     * @param array $encrypt_params        The parameters required for
-     *                                     encryption.
-     *                                     @see _encryptMessage().
-     *
-     * @return mixed  A Horde_Mime_Part object that is signed and encrypted.
-     *                Returns PEAR_Error on error.
-     */
-    public function signAndEncryptMIMEPart($mime_part, $sign_params = array(),
-                                           $encrypt_params = array())
-    {
-        $part = $this->signMIMEPart($mime_part, $sign_params);
-        if (is_a($part, 'PEAR_Error')) {
-            return $part;
-        }
-
-        return $this->encryptMIMEPart($part, $encrypt_params);
-    }
-
-    /**
-     * Convert a PEM format certificate to readable HTML version
-     *
-     * @param string $cert   PEM format certificate
-     *
-     * @return string  HTML detailing the certificate.
-     */
-    public function certToHTML($cert)
-    {
-        /* Common Fields */
-        $fieldnames = array(
-            'Email' => _("Email Address"),
-            'CommonName' => _("Common Name"),
-            'Organisation' => _("Organisation"),
-            'OrganisationalUnit' => _("Organisational Unit"),
-            'Country' => _("Country"),
-            'StateOrProvince' => _("State or Province"),
-            'Location' => _("Location"),
-            'StreetAddress' => _("Street Address"),
-            'TelephoneNumber' => _("Telephone Number"),
-            'Surname' => _("Surname"),
-            'GivenName' => _("Given Name")
-        );
-
-        /* Netscape Extensions */
-        $fieldnames += array(
-            'netscape-cert-type' => _("Netscape certificate type"),
-            'netscape-base-url' => _("Netscape Base URL"),
-            'netscape-revocation-url' => _("Netscape Revocation URL"),
-            'netscape-ca-revocation-url' => _("Netscape CA Revocation URL"),
-            'netscape-cert-renewal-url' => _("Netscape Renewal URL"),
-            'netscape-ca-policy-url' => _("Netscape CA policy URL"),
-            'netscape-ssl-server-name' => _("Netscape SSL server name"),
-            'netscape-comment' => _("Netscape certificate comment")
-        );
-
-        /* X590v3 Extensions */
-        $fieldnames += array(
-            'id-ce-extKeyUsage' => _("X509v3 Extended Key Usage"),
-            'id-ce-basicConstraints' => _("X509v3 Basic Constraints"),
-            'id-ce-subjectAltName' => _("X509v3 Subject Alternative Name"),
-            'id-ce-subjectKeyIdentifier' => _("X509v3 Subject Key Identifier"),
-            'id-ce-certificatePolicies' => _("Certificate Policies"),
-            'id-ce-CRLDistributionPoints' => _("CRL Distribution Points"),
-            'id-ce-keyUsage' => _("Key Usage")
-        );
-
-        $cert_details = $this->parseCert($cert);
-        if (!is_array($cert_details)) {
-            return '<pre class="fixed">' . _("Unable to extract certificate details") . '</pre>';
-        }
-        $certificate = $cert_details['certificate'];
-
-        $text = '<pre class="fixed">';
-
-        /* Subject (a/k/a Certificate Owner) */
-        if (isset($certificate['subject'])) {
-            $text .= "<strong>" . _("Certificate Owner") . ":</strong>\n";
-
-            foreach ($certificate['subject'] as $key => $value) {
-                if (isset($fieldnames[$key])) {
-                    $text .= sprintf("&nbsp;&nbsp;%s: %s\n", $fieldnames[$key], $value);
-                } else {
-                    $text .= sprintf("&nbsp;&nbsp;*%s: %s\n", $key, $value);
-                }
-            }
-            $text .= "\n";
-        }
-
-        /* Issuer */
-        if (isset($certificate['issuer'])) {
-            $text .= "<strong>" . _("Issuer") . ":</strong>\n";
-
-            foreach ($certificate['issuer'] as $key => $value) {
-                if (isset($fieldnames[$key])) {
-                    $text .= sprintf("&nbsp;&nbsp;%s: %s\n", $fieldnames[$key], $value);
-                } else {
-                    $text .= sprintf("&nbsp;&nbsp;*%s: %s\n", $key, $value);
-                }
-            }
-            $text .= "\n";
-        }
-
-        /* Dates  */
-        $text .= "<strong>" . _("Validity") . ":</strong>\n";
-        $text .= sprintf("&nbsp;&nbsp;%s: %s\n", _("Not Before"), strftime("%x %X", $certificate['validity']['notbefore']));
-        $text .= sprintf("&nbsp;&nbsp;%s: %s\n", _("Not After"), strftime("%x %X", $certificate['validity']['notafter']));
-        $text .= "\n";
-
-        /* Certificate Owner - Public Key Info */
-        $text .= "<strong>" . _("Public Key Info") . ":</strong>\n";
-        $text .= sprintf("&nbsp;&nbsp;%s: %s\n", _("Public Key Algorithm"), $certificate['subjectPublicKeyInfo']['algorithm']);
-        if ($certificate['subjectPublicKeyInfo']['algorithm'] == 'rsaEncryption') {
-            if (Util::extensionExists('bcmath')) {
-                $modulus = $certificate['subjectPublicKeyInfo']['subjectPublicKey']['modulus'];
-                $modulus_hex = '';
-                while ($modulus != '0') {
-                    $modulus_hex = dechex(bcmod($modulus, '16')) . $modulus_hex;
-                    $modulus = bcdiv($modulus, '16', 0);
-                }
-
-                if ((strlen($modulus_hex) > 64) &&
-                    (strlen($modulus_hex) < 128)) {
-                    str_pad($modulus_hex, 128, '0', STR_PAD_RIGHT);
-                } elseif ((strlen($modulus_hex) > 128) &&
-                          (strlen($modulus_hex) < 256)) {
-                    str_pad($modulus_hex, 256, '0', STR_PAD_RIGHT);
-                }
-
-                $text .= "&nbsp;&nbsp;" . sprintf(_("RSA Public Key (%d bit)"), strlen($modulus_hex) * 4) . ":\n";
-
-                $modulus_str = '';
-                for ($i = 0; $i < strlen($modulus_hex); $i += 2) {
-                    if (($i % 32) == 0) {
-                        $modulus_str .= "\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";
-                    }
-                    $modulus_str .= substr($modulus_hex, $i, 2) . ':';
-                }
-
-                $text .= sprintf("&nbsp;&nbsp;&nbsp;&nbsp;%s: %s\n", _("Modulus"), $modulus_str);
-            }
-
-            $text .= sprintf("&nbsp;&nbsp;&nbsp;&nbsp;%s: %s\n", _("Exponent"), $certificate['subjectPublicKeyInfo']['subjectPublicKey']['publicExponent']);
-        }
-        $text .= "\n";
-
-        /* X509v3 extensions */
-        if (isset($certificate['extensions'])) {
-            $text .= "<strong>" . _("X509v3 extensions") . ":</strong>\n";
-
-            foreach ($certificate['extensions'] as $key => $value) {
-                if (is_array($value)) {
-                    $value = _("Unsupported Extension");
-                }
-                if (isset($fieldnames[$key])) {
-                    $text .= sprintf("&nbsp;&nbsp;%s:\n&nbsp;&nbsp;&nbsp;&nbsp;%s\n", $fieldnames[$key], wordwrap($value, 40, "\n&nbsp;&nbsp;&nbsp;&nbsp;"));
-                } else {
-                    $text .= sprintf("&nbsp;&nbsp;%s:\n&nbsp;&nbsp;&nbsp;&nbsp;%s\n", $key, wordwrap($value, 60, "\n&nbsp;&nbsp;&nbsp;&nbsp;"));
-                }
-            }
-
-            $text .= "\n";
-        }
-
-        /* Certificate Details */
-        $text .= "<strong>" . _("Certificate Details") . ":</strong>\n";
-        $text .= sprintf("&nbsp;&nbsp;%s: %d\n", _("Version"), $certificate['version']);
-        $text .= sprintf("&nbsp;&nbsp;%s: %d\n", _("Serial Number"), $certificate['serialNumber']);
-
-        foreach ($cert_details['fingerprints'] as $hash => $fingerprint) {
-            $label = sprintf(_("%s Fingerprint"), String::upper($hash));
-            $fingerprint_str = '';
-            for ($i = 0; $i < strlen($fingerprint); $i += 2) {
-                $fingerprint_str .= substr($fingerprint, $i, 2) . ':';
-            }
-            $text .= sprintf("&nbsp;&nbsp;%s:\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;%s\n", $label, $fingerprint_str);
-        }
-        $text .= sprintf("&nbsp;&nbsp;%s: %s\n", _("Signature Algorithm"), $cert_details['signatureAlgorithm']);
-        $text .= sprintf("&nbsp;&nbsp;%s:", _("Signature"));
-
-        $sig_str = '';
-        for ($i = 0; $i < strlen($cert_details['signature']); $i++) {
-            if (($i % 16) == 0) {
-                $sig_str .= "\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";
-            }
-            $sig_str .= sprintf("%02x:", ord($cert_details['signature'][$i]));
-        }
-
-        return $text . $sig_str . "\n</pre>";
-    }
-
-    /**
-     * Extract the contents of a PEM format certificate to an array.
-     *
-     * @param string $cert  PEM format certificate
-     *
-     * @return array  Array containing all extractable information about
-     *                the certificate.
-     */
-    public function parseCert($cert)
-    {
-        $cert_split = preg_split('/(-----((BEGIN)|(END)) CERTIFICATE-----)/', $cert);
-        if (!isset($cert_split[1])) {
-            $raw_cert = base64_decode($cert);
-        } else {
-            $raw_cert = base64_decode($cert_split[1]);
-        }
-
-        $cert_data = $this->_parseASN($raw_cert);
-        if (!is_array($cert_data) || ($cert_data[0] == 'UNKNOWN')) {
-            return false;
-        }
-
-        $cert_details = array();
-        $cert_details['fingerprints']['md5'] = hash('md5', $raw_cert);
-        $cert_details['fingerprints']['sha1'] = hash('sha1', $raw_cert);
-
-        $cert_details['certificate']['extensions'] = array();
-        $cert_details['certificate']['version'] = $cert_data[1][0][1][0][1] + 1;
-        $cert_details['certificate']['serialNumber'] = $cert_data[1][0][1][1][1];
-        $cert_details['certificate']['signature'] = $cert_data[1][0][1][2][1][0][1];
-        $cert_details['certificate']['issuer'] = $cert_data[1][0][1][3][1];
-        $cert_details['certificate']['validity'] = $cert_data[1][0][1][4][1];
-        $cert_details['certificate']['subject'] = @$cert_data[1][0][1][5][1];
-        $cert_details['certificate']['subjectPublicKeyInfo'] = $cert_data[1][0][1][6][1];
-
-        $cert_details['signatureAlgorithm'] = $cert_data[1][1][1][0][1];
-        $cert_details['signature'] = $cert_data[1][2][1];
-
-        // issuer
-        $issuer = array();
-        foreach ($cert_details['certificate']['issuer'] as $value) {
-            $issuer[$value[1][1][0][1]] = $value[1][1][1][1];
-        }
-        $cert_details['certificate']['issuer'] = $issuer;
-
-        // subject
-        $subject = array();
-        foreach ($cert_details['certificate']['subject'] as $value) {
-            $subject[$value[1][1][0][1]] = $value[1][1][1][1];
-        }
-        $cert_details['certificate']['subject'] = $subject;
-
-        // validity
-        $vals = $cert_details['certificate']['validity'];
-        $cert_details['certificate']['validity'] = array();
-        $cert_details['certificate']['validity']['notbefore'] = $vals[0][1];
-        $cert_details['certificate']['validity']['notafter'] = $vals[1][1];
-        foreach ($cert_details['certificate']['validity'] as $key => $val) {
-            $year = substr($val, 0, 2);
-            $month = substr($val, 2, 2);
-            $day = substr($val, 4, 2);
-            $hour = substr($val, 6, 2);
-            $minute = substr($val, 8, 2);
-            if (($val[11] == '-') || ($val[9] == '+')) {
-                // handle time zone offset here
-                $seconds = 0;
-            } elseif (String::upper($val[11]) == 'Z') {
-                $seconds = 0;
-            } else {
-                $seconds = substr($val, 10, 2);
-                if (($val[11] == '-') || ($val[9] == '+')) {
-                    // handle time zone offset here
-                }
-            }
-            $cert_details['certificate']['validity'][$key] = mktime ($hour, $minute, $seconds, $month, $day, $year);
-        }
-
-        // Split the Public Key into components.
-        $subjectPublicKeyInfo = array();
-        $subjectPublicKeyInfo['algorithm'] = $cert_details['certificate']['subjectPublicKeyInfo'][0][1][0][1];
-        if ($subjectPublicKeyInfo['algorithm'] == 'rsaEncryption') {
-            $subjectPublicKey = $this->_parseASN($cert_details['certificate']['subjectPublicKeyInfo'][1][1]);
-            $subjectPublicKeyInfo['subjectPublicKey']['modulus'] = $subjectPublicKey[1][0][1];
-            $subjectPublicKeyInfo['subjectPublicKey']['publicExponent'] = $subjectPublicKey[1][1][1];
-        }
-        $cert_details['certificate']['subjectPublicKeyInfo'] = $subjectPublicKeyInfo;
-
-        if (isset($cert_data[1][0][1][7]) &&
-            is_array($cert_data[1][0][1][7][1])) {
-            foreach ($cert_data[1][0][1][7][1] as $ext) {
-                $oid = $ext[1][0][1];
-                $cert_details['certificate']['extensions'][$oid] = $ext[1][1];
-            }
-        }
-
-        $i = 9;
-
-        while (isset($cert_data[1][0][1][$i]) &&
-               is_array($cert_data[1][0][1][$i][1])) {
-            $oid = $cert_data[1][0][1][$i][1][0][1];
-            $cert_details['certificate']['extensions'][$oid] = $cert_data[1][0][1][$i][1][1];
-            ++$i;
-        }
-
-        foreach ($cert_details['certificate']['extensions'] as $oid => $val) {
-            switch ($oid) {
-            case 'netscape-base-url':
-            case 'netscape-revocation-url':
-            case 'netscape-ca-revocation-url':
-            case 'netscape-cert-renewal-url':
-            case 'netscape-ca-policy-url':
-            case 'netscape-ssl-server-name':
-            case 'netscape-comment':
-                $val = $this->_parseASN($val[1]);
-                $cert_details['certificate']['extensions'][$oid] = $val[1];
-                break;
-
-            case 'id-ce-subjectAltName':
-                $val = $this->_parseASN($val[1]);
-                $cert_details['certificate']['extensions'][$oid] = '';
-                foreach ($val[1] as $name) {
-                    if (!empty($cert_details['certificate']['extensions'][$oid])) {
-                        $cert_details['certificate']['extensions'][$oid] .= ', ';
-                    }
-                    $cert_details['certificate']['extensions'][$oid] .= $name[1];
-                }
-                break;
-
-            case 'netscape-cert-type':
-                $val = $this->_parseASN($val[1]);
-                $val = ord($val[1]);
-                $newVal = '';
-
-                if ($val & 0x80) {
-                    $newVal .= empty($newVal) ? 'SSL client' : ', SSL client';
-                }
-                if ($val & 0x40) {
-                    $newVal .= empty($newVal) ? 'SSL server' : ', SSL server';
-                }
-                if ($val & 0x20) {
-                    $newVal .= empty($newVal) ? 'S/MIME' : ', S/MIME';
-                }
-                if ($val & 0x10) {
-                    $newVal .= empty($newVal) ? 'Object Signing' : ', Object Signing';
-                }
-                if ($val & 0x04) {
-                    $newVal .= empty($newVal) ? 'SSL CA' : ', SSL CA';
-                }
-                if ($val & 0x02) {
-                    $newVal .= empty($newVal) ? 'S/MIME CA' : ', S/MIME CA';
-                }
-                if ($val & 0x01) {
-                    $newVal .= empty($newVal) ? 'Object Signing CA' : ', Object Signing CA';
-                }
-
-                $cert_details['certificate']['extensions'][$oid] = $newVal;
-                break;
-
-            case 'id-ce-extKeyUsage':
-                $val = $this->_parseASN($val[1]);
-                $val = $val[1];
-
-                $newVal = '';
-                if ($val[0][1] != 'sequence') {
-                    $val = array($val);
-                } else {
-                    $val = $val[1][1];
-                }
-                foreach ($val as $usage) {
-                    if ($usage[1] == 'id_kp_clientAuth') {
-                        $newVal .= empty($newVal) ? 'TLS Web Client Authentication' : ', TLS Web Client Authentication';
-                    } else {
-                        $newVal .= empty($newVal) ? $usage[1] : ', ' . $usage[1];
-                    }
-                }
-                $cert_details['certificate']['extensions'][$oid] = $newVal;
-                break;
-
-            case 'id-ce-subjectKeyIdentifier':
-                $val = $this->_parseASN($val[1]);
-                $val = $val[1];
-
-                $newVal = '';
-
-                for ($i = 0; $i < strlen($val); $i++) {
-                    $newVal .= sprintf("%02x:", ord($val[$i]));
-                }
-                $cert_details['certificate']['extensions'][$oid] = $newVal;
-                break;
-
-            case 'id-ce-authorityKeyIdentifier':
-                $val = $this->_parseASN($val[1]);
-                if ($val[0] == 'string') {
-                    $val = $val[1];
-
-                    $newVal = '';
-                    for ($i = 0; $i < strlen($val); $i++) {
-                        $newVal .= sprintf("%02x:", ord($val[$i]));
-                    }
-                    $cert_details['certificate']['extensions'][$oid] = $newVal;
-                } else {
-                    $cert_details['certificate']['extensions'][$oid] = _("Unsupported Extension");
-                }
-                break;
-
-            case 'id-ce-basicConstraints':
-            case 'default':
-                $cert_details['certificate']['extensions'][$oid] = _("Unsupported Extension");
-                break;
-            }
-        }
-
-        return $cert_details;
-    }
-
-    /**
-     * Attempt to parse ASN.1 formated data.
-     *
-     * @param string $data  ASN.1 formated data
-     *
-     * @return array  Array contained the extracted values.
-     */
-    protected function _parseASN($data)
-    {
-        $result = array();
-
-        while (strlen($data) > 1) {
-            $class = ord($data[0]);
-            switch ($class) {
-            case 0x30:
-                // Sequence
-                $len = ord($data[1]);
-                $bytes = 0;
-                if ($len & 0x80) {
-                    $bytes = $len & 0x0f;
-                    $len = 0;
-                    for ($i = 0; $i < $bytes; $i++) {
-                        $len = ($len << 8) | ord($data[$i + 2]);
-                    }
-                }
-                $sequence_data = substr($data, 2 + $bytes, $len);
-                $data = substr($data, 2 + $bytes + $len);
-
-                $values = $this->_parseASN($sequence_data);
-                if (!is_array($values) || is_string($values[0])) {
-                    $values = array($values);
-                }
-                $sequence_values = array();
-                $i = 0;
-                foreach ($values as $val) {
-                    if ($val[0] == 'extension') {
-                        $sequence_values['extensions'][] = $val;
-                    } else {
-                        $sequence_values[$i++] = $val;
-                    }
-                }
-                $result[] = array('sequence', $sequence_values);
-                break;
-
-            case 0x31:
-                // Set of
-                $len = ord($data[1]);
-                $bytes = 0;
-                if ($len & 0x80) {
-                    $bytes = $len & 0x0f;
-                    $len = 0;
-                    for ($i = 0; $i < $bytes; $i++) {
-                        $len = ($len << 8) | ord($data[$i + 2]);
-                    }
-                }
-                $sequence_data = substr($data, 2 + $bytes, $len);
-                $data = substr($data, 2 + $bytes + $len);
-                $result[] = array('set', $this->_parseASN($sequence_data));
-                break;
-
-            case 0x01:
-                // Boolean type
-                $boolean_value = (ord($data[2]) == 0xff);
-                $data = substr($data, 3);
-                $result[] = array('boolean', $boolean_value);
-                break;
-
-            case 0x02:
-                // Integer type
-                $len = ord($data[1]);
-                $bytes = 0;
-                if ($len & 0x80) {
-                    $bytes = $len & 0x0f;
-                    $len = 0;
-                    for ($i = 0; $i < $bytes; $i++) {
-                        $len = ($len << 8) | ord($data[$i + 2]);
-                    }
-                }
-
-                $integer_data = substr($data, 2 + $bytes, $len);
-                $data = substr($data, 2 + $bytes + $len);
-
-                $value = 0;
-                if ($len <= 4) {
-                    /* Method works fine for small integers */
-                    for ($i = 0; $i < strlen($integer_data); $i++) {
-                        $value = ($value << 8) | ord($integer_data[$i]);
-                    }
-                } else {
-                    /* Method works for arbitrary length integers */
-                    if (Util::extensionExists('bcmath')) {
-                        for ($i = 0; $i < strlen($integer_data); $i++) {
-                            $value = bcadd(bcmul($value, 256), ord($integer_data[$i]));
-                        }
-                    } else {
-                        $value = -1;
-                    }
-                }
-                $result[] = array('integer(' . $len . ')', $value);
-                break;
-
-            case 0x03:
-                // Bitstring type
-                $len = ord($data[1]);
-                $bytes = 0;
-                if ($len & 0x80) {
-                    $bytes = $len & 0x0f;
-                    $len = 0;
-                    for ($i = 0; $i < $bytes; $i++) {
-                        $len = ($len << 8) | ord($data[$i + 2]);
-                    }
-                }
-                $bitstring_data = substr($data, 3 + $bytes, $len);
-                $data = substr($data, 2 + $bytes + $len);
-                $result[] = array('bit string', $bitstring_data);
-                break;
-
-            case 0x04:
-                // Octetstring type
-                $len = ord($data[1]);
-                $bytes = 0;
-                if ($len & 0x80) {
-                    $bytes = $len & 0x0f;
-                    $len = 0;
-                    for ($i = 0; $i < $bytes; $i++) {
-                        $len = ($len << 8) | ord($data[$i + 2]);
-                    }
-                }
-                $octectstring_data = substr($data, 2 + $bytes, $len);
-                $data = substr($data, 2 + $bytes + $len);
-                $result[] = array('octet string', $octectstring_data);
-                break;
-
-            case 0x05:
-                // Null type
-                $data = substr($data, 2);
-                $result[] = array('null', null);
-                break;
-
-            case 0x06:
-                // Object identifier type
-                $len = ord($data[1]);
-                $bytes = 0;
-                if ($len & 0x80) {
-                    $bytes = $len & 0x0f;
-                    $len = 0;
-                    for ($i = 0; $i < $bytes; $i++) {
-                        $len = ($len << 8) | ord($data[$i + 2]);
-                    }
-                }
-                $oid_data = substr($data, 2 + $bytes, $len);
-                $data = substr($data, 2 + $bytes + $len);
-
-                // Unpack the OID
-                $plain  = floor(ord($oid_data[0]) / 40);
-                $plain .= '.' . ord($oid_data[0]) % 40;
-
-                $value = 0;
-                $i = 1;
-                while ($i < strlen($oid_data)) {
-                    $value = $value << 7;
-                    $value = $value | (ord($oid_data[$i]) & 0x7f);
-
-                    if (!(ord($oid_data[$i]) & 0x80)) {
-                        $plain .= '.' . $value;
-                        $value = 0;
-                    }
-                    $i++;
-                }
-
-                if (isset($this->_oids[$plain])) {
-                    $result[] = array('oid', $this->_oids[$plain]);
-                } else {
-                    $result[] = array('oid', $plain);
-                }
-
-                break;
-
-            case 0x12:
-            case 0x13:
-            case 0x14:
-            case 0x15:
-            case 0x16:
-            case 0x81:
-            case 0x80:
-                // Character string type
-                $len = ord($data[1]);
-                $bytes = 0;
-                if ($len & 0x80) {
-                    $bytes = $len & 0x0f;
-                    $len = 0;
-                    for ($i = 0; $i < $bytes; $i++) {
-                        $len = ($len << 8) | ord($data[$i + 2]);
-                    }
-                }
-                $string_data = substr($data, 2 + $bytes, $len);
-                $data = substr($data, 2 + $bytes + $len);
-                $result[] = array('string', $string_data);
-                break;
-
-            case 0x17:
-                // Time types
-                $len = ord($data[1]);
-                $bytes = 0;
-                if ($len & 0x80) {
-                    $bytes = $len & 0x0f;
-                    $len = 0;
-                    for ($i = 0; $i < $bytes; $i++) {
-                        $len = ($len << 8) | ord($data[$i + 2]);
-                    }
-                }
-                $time_data = substr($data, 2 + $bytes, $len);
-                $data = substr($data, 2 + $bytes + $len);
-                $result[] = array('utctime', $time_data);
-                break;
-
-            case 0x82:
-                // X509v3 extensions?
-                $len = ord($data[1]);
-                $bytes = 0;
-                if ($len & 0x80) {
-                    $bytes = $len & 0x0f;
-                    $len = 0;
-                    for ($i = 0; $i < $bytes; $i++) {
-                        $len = ($len << 8) | ord($data[$i + 2]);
-                    }
-                }
-                $sequence_data = substr($data, 2 + $bytes, $len);
-                $data = substr($data, 2 + $bytes + $len);
-                $result[] = array('extension', 'X509v3 extensions');
-                $result[] = $this->_parseASN($sequence_data);
-                break;
-
-            case 0xa0:
-            case 0xa3:
-                // Extensions
-                $extension_data = substr($data, 0, 2);
-                $data = substr($data, 2);
-                $result[] = array('extension', dechex($extension_data));
-                break;
-
-            case 0xe6:
-                $extension_data = substr($data, 0, 1);
-                $data = substr($data, 1);
-                $result[] = array('extension', dechex($extension_data));
-                break;
-
-            case 0xa1:
-                $extension_data = substr($data, 0, 1);
-                $data = substr($data, 6);
-                $result[] = array('extension', dechex($extension_data));
-                break;
-
-            default:
-                // Unknown
-                $result[] = array('UNKNOWN', dechex($data));
-                $data = '';
-                break;
-            }
-        }
-
-        return (count($result) > 1) ? $result : array_pop($result);
-    }
-
-    /**
-     * Decrypt an S/MIME signed message using a public key.
-     *
-     * @param string $text   The text to be verified.
-     * @param array $params  The parameters needed for verification.
-     *
-     * @return string  The verification message.
-     *                 Returns PEAR_Error object on error.
-     */
-    protected function _decryptSignature($text, $params)
-    {
-        return PEAR::raiseError('_decryptSignature() ' . _("not yet implemented"));
-    }
-
-    /**
-     * Check for the presence of the OpenSSL extension to PHP.
-     *
-     * @return boolean  Returns true if the openssl extension is available.
-     *                  Returns a PEAR_Error if not.
-     */
-    public function checkForOpenSSL()
-    {
-        if (!Util::extensionExists('openssl')) {
-            return PEAR::raiseError(_("The openssl module is required for the Horde_Crypt_smime:: class."));
-        }
-    }
-
-    /**
-     * Extract the email address from a public key.
-     *
-     * @param string $key  The public key.
-     *
-     * @return mixed Returns the first email address found, or null if
-     * there are none.
-     */
-    public function getEmailFromKey($key)
-    {
-        $key_info = openssl_x509_parse($key);
-        if (!is_array($key_info)) {
-            return null;
-        }
-
-        if (isset($key_info['subject'])) {
-            if (isset($key_info['subject']['Email'])) {
-                return $key_info['subject']['Email'];
-            } elseif (isset($key_info['subject']['emailAddress'])) {
-                return $key_info['subject']['emailAddress'];
-            }
-        }
-
-        // Check subjectAltName per http://www.ietf.org/rfc/rfc3850.txt
-        if (isset($key_info['extensions']['subjectAltName'])) {
-            $names = preg_split('/\s*,\s*/', $key_info['extensions']['subjectAltName'], -1, PREG_SPLIT_NO_EMPTY);
-            foreach ($names as $name) {
-                if (strpos($name, ':') === false) {
-                    continue;
-                }
-                list($kind, $value) = explode(':', $name, 2);
-                if (String::lower($kind) == 'email') {
-                    return $value;
-                }
-            }
-        }
-
-        return null;
-    }
-
-    /**
-     * Convert a PKCS 12 encrypted certificate package into a private key,
-     * public key, and any additional keys.
-     *
-     * @param string $text   The PKCS 12 data.
-     * @param array $params  The parameters needed for parsing.
-     * <pre>
-     * Parameters:
-     * ===========
-     * 'sslpath' => The path to the OpenSSL binary. (REQUIRED)
-     * 'password' => The password to use to decrypt the data. (Optional)
-     * 'newpassword' => The password to use to encrypt the private key.
-     *                  (Optional)
-     * </pre>
-     *
-     * @return stdClass  An object.
-     *                   'private' -  The private key in PEM format.
-     *                   'public'  -  The public key in PEM format.
-     *                   'certs'   -  An array of additional certs.
-     *                   Returns PEAR_Error on error.
-     */
-    public function parsePKCS12Data($pkcs12, $params)
-    {
-        /* Check for availability of OpenSSL PHP extension. */
-        $openssl = $this->checkForOpenSSL();
-        if (is_a($openssl, 'PEAR_Error')) {
-            return $openssl;
-        }
-
-        if (!isset($params['sslpath'])) {
-            return PEAR::raiseError(_("No path to the OpenSSL binary provided. The OpenSSL binary is necessary to work with PKCS 12 data."), 'horde.error');
-        }
-        $sslpath = escapeshellcmd($params['sslpath']);
-
-        /* Create temp files for input/output. */
-        $input = $this->_createTempFile('horde-smime');
-        $output = $this->_createTempFile('horde-smime');
-
-        $ob = new stdClass;
-
-        /* Write text to file */
-        file_put_contents($input, $pkcs12);
-        unset($pkcs12);
-
-        /* Extract the private key from the file first. */
-        $cmdline = $sslpath . ' pkcs12 -in ' . $input . ' -out ' . $output . ' -nocerts';
-        if (isset($params['password'])) {
-            $cmdline .= ' -passin stdin';
-            if (!empty($params['newpassword'])) {
-                $cmdline .= ' -passout stdin';
-            } else {
-                $cmdline .= ' -nodes';
-            }
-            $fd = popen($cmdline, 'w');
-            fwrite($fd, $params['password'] . "\n");
-            if (!empty($params['newpassword'])) {
-                fwrite($fd, $params['newpassword'] . "\n");
-            }
-            pclose($fd);
-        } else {
-            $cmdline .= ' -nodes';
-            exec($cmdline);
-        }
-        $ob->private = trim(file_get_contents($output));
-        if (empty($ob->private)) {
-            return PEAR::raiseError(_("Password incorrect"), 'horde.error');
-        }
-
-        /* Extract the client public key next. */
-        $cmdline = $sslpath . ' pkcs12 -in ' . $input . ' -out ' . $output . ' -nokeys -clcerts';
-        if (isset($params['password'])) {
-            $cmdline .= ' -passin stdin';
-            $fd = popen($cmdline, 'w');
-            fwrite($fd, $params['password'] . "\n");
-            pclose($fd);
-        } else {
-            exec($cmdline);
-        }
-        $ob->public = trim(file_get_contents($output));
-
-        /* Extract the CA public key next. */
-        $cmdline = $sslpath . ' pkcs12 -in ' . $input . ' -out ' . $output . ' -nokeys -cacerts';
-        if (isset($params['password'])) {
-            $cmdline .= ' -passin stdin';
-            $fd = popen($cmdline, 'w');
-            fwrite($fd, $params['password'] . "\n");
-            pclose($fd);
-        } else {
-            exec($cmdline);
-        }
-        $ob->certs = trim(file_get_contents($output));
-
-        return $ob;
-    }
-
-    /**
-     * The Content-Type parameters PHP's openssl_pkcs7_* functions return are
-     * deprecated.  Fix these headers to the correct ones (see RFC 2311).
-     *
-     * @param string $text  The PKCS7 data.
-     * @param string $type  Is this 'message' or 'signature' data?
-     *
-     * @return string  The PKCS7 data with the correct Content-Type parameter.
-     */
-    protected function _fixContentType($text, $type)
-    {
-        if ($type == 'message') {
-            $from = 'application/x-pkcs7-mime';
-            $to = 'application/pkcs7-mime';
-        } else {
-            $from = 'application/x-pkcs7-signature';
-            $to = 'application/pkcs7-signature';
-        }
-        return str_replace('Content-Type: ' . $from, 'Content-Type: ' . $to, $text);
-    }
-
-}
index 8b911b9..b7e42bc 100644 (file)
@@ -30,8 +30,8 @@ http://pear.php.net/dtd/package-2.0.xsd">
    <dir name="lib">
     <dir name="Horde">
      <dir name="Crypt">
-      <file name="pgp.php" role="php" />
-      <file name="smime.php" role="php" />
+      <file name="Pgp.php" role="php" />
+      <file name="Smime.php" role="php" />
      </dir> <!-- /lib/Horde/Crypt -->
      <file name="Crypt.php" role="php" />
     </dir> <!-- /lib/Horde -->
@@ -99,8 +99,8 @@ http://pear.php.net/dtd/package-2.0.xsd">
  </dependencies>
  <phprelease>
   <filelist>
-   <install name="lib/Horde/Crypt/pgp.php" as="Horde/Crypt/pgp.php" />
-   <install name="lib/Horde/Crypt/smime.php" as="Horde/Crypt/smime.php" />
+   <install name="lib/Horde/Crypt/Pgp.php" as="Horde/Crypt/Pgp.php" />
+   <install name="lib/Horde/Crypt/Smime.php" as="Horde/Crypt/Smime.php" />
    <install name="lib/Horde/Crypt.php" as="Horde/Crypt.php" />
   </filelist>
  </phprelease>
index ea02f31..f294dcd 100644 (file)
@@ -13,7 +13,7 @@ require $filedir . '/../../../lib/Horde/Crypt.php';
 $_SERVER['HTTPS'] = 'on';
 $browser = &Browser::singleton();
 
-$pgp = Horde_Crypt::factory('pgp', array(
+$pgp = Horde_Crypt::factory('Pgp', array(
     'program' => '/usr/bin/gpg',
     'temp' => Util::getTempDir()
 ));
index fab7acd..77de298 100644 (file)
@@ -8,4 +8,4 @@ require 'Horde/String.php';
 require 'Horde/Util.php';
 require dirname(__FILE__) . '/../../../lib/Horde/Crypt.php';
 
-$smime = Horde_Crypt::factory('smime', array('temp' => Util::getTempDir()));
+$smime = Horde_Crypt::factory('Smime', array('temp' => Util::getTempDir()));