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