diff options
author | thomascube <thomas@roundcube.net> | 2010-09-29 10:04:09 +0000 |
---|---|---|
committer | thomascube <thomas@roundcube.net> | 2010-09-29 10:04:09 +0000 |
commit | b9c08759f0433d9a24f0c8595c16624684f63e0d (patch) | |
tree | 472b055c14714e60cf60b1cf07955a30e99f505d /plugins/enigma/lib/Crypt/GPG/Engine.php | |
parent | 2baa16ae6d8355e0dfb38e400fd4115057b0680d (diff) |
Remove development stuff and update versions
Diffstat (limited to 'plugins/enigma/lib/Crypt/GPG/Engine.php')
-rw-r--r-- | plugins/enigma/lib/Crypt/GPG/Engine.php | 1758 |
1 files changed, 0 insertions, 1758 deletions
diff --git a/plugins/enigma/lib/Crypt/GPG/Engine.php b/plugins/enigma/lib/Crypt/GPG/Engine.php deleted file mode 100644 index 081be8e21..000000000 --- a/plugins/enigma/lib/Crypt/GPG/Engine.php +++ /dev/null @@ -1,1758 +0,0 @@ -<?php - -/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ - -/** - * Crypt_GPG is a package to use GPG from PHP - * - * This file contains an engine that handles GPG subprocess control and I/O. - * PHP's process manipulation functions are used to handle the GPG subprocess. - * - * PHP version 5 - * - * LICENSE: - * - * This library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation; either version 2.1 of the - * License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * @category Encryption - * @package Crypt_GPG - * @author Nathan Fredrickson <nathan@silverorange.com> - * @author Michael Gauthier <mike@silverorange.com> - * @copyright 2005-2010 silverorange - * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 - * @version CVS: $Id: Engine.php 302822 2010-08-26 17:30:57Z gauthierm $ - * @link http://pear.php.net/package/Crypt_GPG - * @link http://www.gnupg.org/ - */ - -/** - * Crypt_GPG base class. - */ -require_once 'Crypt/GPG.php'; - -/** - * GPG exception classes. - */ -require_once 'Crypt/GPG/Exceptions.php'; - -/** - * Standard PEAR exception is used if GPG binary is not found. - */ -require_once 'PEAR/Exception.php'; - -// {{{ class Crypt_GPG_Engine - -/** - * Native PHP Crypt_GPG I/O engine - * - * This class is used internally by Crypt_GPG and does not need be used - * directly. See the {@link Crypt_GPG} class for end-user API. - * - * This engine uses PHP's native process control functions to directly control - * the GPG process. The GPG executable is required to be on the system. - * - * All data is passed to the GPG subprocess using file descriptors. This is the - * most secure method of passing data to the GPG subprocess. - * - * @category Encryption - * @package Crypt_GPG - * @author Nathan Fredrickson <nathan@silverorange.com> - * @author Michael Gauthier <mike@silverorange.com> - * @copyright 2005-2010 silverorange - * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 - * @link http://pear.php.net/package/Crypt_GPG - * @link http://www.gnupg.org/ - */ -class Crypt_GPG_Engine -{ - // {{{ constants - - /** - * Size of data chunks that are sent to and retrieved from the IPC pipes. - * - * PHP reads 8192 bytes. If this is set to less than 8192, PHP reads 8192 - * and buffers the rest so we might as well just read 8192. - * - * Using values other than 8192 also triggers PHP bugs. - * - * @see http://bugs.php.net/bug.php?id=35224 - */ - const CHUNK_SIZE = 8192; - - /** - * Standard input file descriptor. This is used to pass data to the GPG - * process. - */ - const FD_INPUT = 0; - - /** - * Standard output file descriptor. This is used to receive normal output - * from the GPG process. - */ - const FD_OUTPUT = 1; - - /** - * Standard output file descriptor. This is used to receive error output - * from the GPG process. - */ - const FD_ERROR = 2; - - /** - * GPG status output file descriptor. The status file descriptor outputs - * detailed information for many GPG commands. See the second section of - * the file <b>doc/DETAILS</b> in the - * {@link http://www.gnupg.org/download/ GPG package} for a detailed - * description of GPG's status output. - */ - const FD_STATUS = 3; - - /** - * Command input file descriptor. This is used for methods requiring - * passphrases. - */ - const FD_COMMAND = 4; - - /** - * Extra message input file descriptor. This is used for passing signed - * data when verifying a detached signature. - */ - const FD_MESSAGE = 5; - - /** - * Minimum version of GnuPG that is supported. - */ - const MIN_VERSION = '1.0.2'; - - // }}} - // {{{ private class properties - - /** - * Whether or not to use debugging mode - * - * When set to true, every GPG command is echoed before it is run. Sensitive - * data is always handled using pipes and is not specified as part of the - * command. As a result, sensitive data is never displayed when debug is - * enabled. Sensitive data includes private key data and passphrases. - * - * Debugging is off by default. - * - * @var boolean - * @see Crypt_GPG_Engine::__construct() - */ - private $_debug = false; - - /** - * Location of GPG binary - * - * @var string - * @see Crypt_GPG_Engine::__construct() - * @see Crypt_GPG_Engine::_getBinary() - */ - private $_binary = ''; - - /** - * Directory containing the GPG key files - * - * This property only contains the path when the <i>homedir</i> option - * is specified in the constructor. - * - * @var string - * @see Crypt_GPG_Engine::__construct() - */ - private $_homedir = ''; - - /** - * File path of the public keyring - * - * This property only contains the file path when the <i>public_keyring</i> - * option is specified in the constructor. - * - * If the specified file path starts with <kbd>~/</kbd>, the path is - * relative to the <i>homedir</i> if specified, otherwise to - * <kbd>~/.gnupg</kbd>. - * - * @var string - * @see Crypt_GPG_Engine::__construct() - */ - private $_publicKeyring = ''; - - /** - * File path of the private (secret) keyring - * - * This property only contains the file path when the <i>private_keyring</i> - * option is specified in the constructor. - * - * If the specified file path starts with <kbd>~/</kbd>, the path is - * relative to the <i>homedir</i> if specified, otherwise to - * <kbd>~/.gnupg</kbd>. - * - * @var string - * @see Crypt_GPG_Engine::__construct() - */ - private $_privateKeyring = ''; - - /** - * File path of the trust database - * - * This property only contains the file path when the <i>trust_db</i> - * option is specified in the constructor. - * - * If the specified file path starts with <kbd>~/</kbd>, the path is - * relative to the <i>homedir</i> if specified, otherwise to - * <kbd>~/.gnupg</kbd>. - * - * @var string - * @see Crypt_GPG_Engine::__construct() - */ - private $_trustDb = ''; - - /** - * Array of pipes used for communication with the GPG binary - * - * This is an array of file descriptor resources. - * - * @var array - */ - private $_pipes = array(); - - /** - * Array of currently opened pipes - * - * This array is used to keep track of remaining opened pipes so they can - * be closed when the GPG subprocess is finished. This array is a subset of - * the {@link Crypt_GPG_Engine::$_pipes} array and contains opened file - * descriptor resources. - * - * @var array - * @see Crypt_GPG_Engine::_closePipe() - */ - private $_openPipes = array(); - - /** - * A handle for the GPG process - * - * @var resource - */ - private $_process = null; - - /** - * Whether or not the operating system is Darwin (OS X) - * - * @var boolean - */ - private $_isDarwin = false; - - /** - * Commands to be sent to GPG's command input stream - * - * @var string - * @see Crypt_GPG_Engine::sendCommand() - */ - private $_commandBuffer = ''; - - /** - * Array of status line handlers - * - * @var array - * @see Crypt_GPG_Engine::addStatusHandler() - */ - private $_statusHandlers = array(); - - /** - * Array of error line handlers - * - * @var array - * @see Crypt_GPG_Engine::addErrorHandler() - */ - private $_errorHandlers = array(); - - /** - * The error code of the current operation - * - * @var integer - * @see Crypt_GPG_Engine::getErrorCode() - */ - private $_errorCode = Crypt_GPG::ERROR_NONE; - - /** - * File related to the error code of the current operation - * - * @var string - * @see Crypt_GPG_Engine::getErrorFilename() - */ - private $_errorFilename = ''; - - /** - * Key id related to the error code of the current operation - * - * @var string - * @see Crypt_GPG_Engine::getErrorKeyId() - */ - private $_errorkeyId = ''; - - /** - * The number of currently needed passphrases - * - * If this is not zero when the GPG command is completed, the error code is - * set to {@link Crypt_GPG::ERROR_MISSING_PASSPHRASE}. - * - * @var integer - */ - private $_needPassphrase = 0; - - /** - * The input source - * - * This is data to send to GPG. Either a string or a stream resource. - * - * @var string|resource - * @see Crypt_GPG_Engine::setInput() - */ - private $_input = null; - - /** - * The extra message input source - * - * Either a string or a stream resource. - * - * @var string|resource - * @see Crypt_GPG_Engine::setMessage() - */ - private $_message = null; - - /** - * The output location - * - * This is where the output from GPG is sent. Either a string or a stream - * resource. - * - * @var string|resource - * @see Crypt_GPG_Engine::setOutput() - */ - private $_output = ''; - - /** - * The GPG operation to execute - * - * @var string - * @see Crypt_GPG_Engine::setOperation() - */ - private $_operation; - - /** - * Arguments for the current operation - * - * @var array - * @see Crypt_GPG_Engine::setOperation() - */ - private $_arguments = array(); - - /** - * The version number of the GPG binary - * - * @var string - * @see Crypt_GPG_Engine::getVersion() - */ - private $_version = ''; - - /** - * Cached value indicating whether or not mbstring function overloading is - * on for strlen - * - * This is cached for optimal performance inside the I/O loop. - * - * @var boolean - * @see Crypt_GPG_Engine::_byteLength() - * @see Crypt_GPG_Engine::_byteSubstring() - */ - private static $_mbStringOverload = null; - - // }}} - // {{{ __construct() - - /** - * Creates a new GPG engine - * - * Available options are: - * - * - <kbd>string homedir</kbd> - the directory where the GPG - * keyring files are stored. If not - * specified, Crypt_GPG uses the - * default of <kbd>~/.gnupg</kbd>. - * - <kbd>string publicKeyring</kbd> - the file path of the public - * keyring. Use this if the public - * keyring is not in the homedir, or - * if the keyring is in a directory - * not writable by the process - * invoking GPG (like Apache). Then - * you can specify the path to the - * keyring with this option - * (/foo/bar/pubring.gpg), and specify - * a writable directory (like /tmp) - * using the <i>homedir</i> option. - * - <kbd>string privateKeyring</kbd> - the file path of the private - * keyring. Use this if the private - * keyring is not in the homedir, or - * if the keyring is in a directory - * not writable by the process - * invoking GPG (like Apache). Then - * you can specify the path to the - * keyring with this option - * (/foo/bar/secring.gpg), and specify - * a writable directory (like /tmp) - * using the <i>homedir</i> option. - * - <kbd>string trustDb</kbd> - the file path of the web-of-trust - * database. Use this if the trust - * database is not in the homedir, or - * if the database is in a directory - * not writable by the process - * invoking GPG (like Apache). Then - * you can specify the path to the - * trust database with this option - * (/foo/bar/trustdb.gpg), and specify - * a writable directory (like /tmp) - * using the <i>homedir</i> option. - * - <kbd>string binary</kbd> - the location of the GPG binary. If - * not specified, the driver attempts - * to auto-detect the GPG binary - * location using a list of known - * default locations for the current - * operating system. The option - * <kbd>gpgBinary</kbd> is a - * deprecated alias for this option. - * - <kbd>boolean debug</kbd> - whether or not to use debug mode. - * When debug mode is on, all - * communication to and from the GPG - * subprocess is logged. This can be - * useful to diagnose errors when - * using Crypt_GPG. - * - * @param array $options optional. An array of options used to create the - * GPG object. All options are optional and are - * represented as key-value pairs. - * - * @throws Crypt_GPG_FileException if the <kbd>homedir</kbd> does not exist - * and cannot be created. This can happen if <kbd>homedir</kbd> is - * not specified, Crypt_GPG is run as the web user, and the web - * user has no home directory. This exception is also thrown if any - * of the options <kbd>publicKeyring</kbd>, - * <kbd>privateKeyring</kbd> or <kbd>trustDb</kbd> options are - * specified but the files do not exist or are are not readable. - * This can happen if the user running the Crypt_GPG process (for - * example, the Apache user) does not have permission to read the - * files. - * - * @throws PEAR_Exception if the provided <kbd>binary</kbd> is invalid, or - * if no <kbd>binary</kbd> is provided and no suitable binary could - * be found. - */ - public function __construct(array $options = array()) - { - $this->_isDarwin = (strncmp(strtoupper(PHP_OS), 'DARWIN', 6) === 0); - - // populate mbstring overloading cache if not set - if (self::$_mbStringOverload === null) { - self::$_mbStringOverload = (extension_loaded('mbstring') - && (ini_get('mbstring.func_overload') & 0x02) === 0x02); - } - - // get homedir - if (array_key_exists('homedir', $options)) { - $this->_homedir = (string)$options['homedir']; - } else { - // note: this requires the package OS dep exclude 'windows' - $info = posix_getpwuid(posix_getuid()); - $this->_homedir = $info['dir'].'/.gnupg'; - } - - // attempt to create homedir if it does not exist - if (!is_dir($this->_homedir)) { - if (@mkdir($this->_homedir, 0777, true)) { - // Set permissions on homedir. Parent directories are created - // with 0777, homedir is set to 0700. - chmod($this->_homedir, 0700); - } else { - throw new Crypt_GPG_FileException('The \'homedir\' "' . - $this->_homedir . '" is not readable or does not exist '. - 'and cannot be created. This can happen if \'homedir\' '. - 'is not specified in the Crypt_GPG options, Crypt_GPG is '. - 'run as the web user, and the web user has no home '. - 'directory.', - 0, $this->_homedir); - } - } - - // get binary - if (array_key_exists('binary', $options)) { - $this->_binary = (string)$options['binary']; - } elseif (array_key_exists('gpgBinary', $options)) { - // deprecated alias - $this->_binary = (string)$options['gpgBinary']; - } else { - $this->_binary = $this->_getBinary(); - } - - if ($this->_binary == '' || !is_executable($this->_binary)) { - throw new PEAR_Exception('GPG binary not found. If you are sure '. - 'the GPG binary is installed, please specify the location of '. - 'the GPG binary using the \'binary\' driver option.'); - } - - /* - * Note: - * - * Normally, GnuPG expects keyrings to be in the homedir and expects - * to be able to write temporary files in the homedir. Sometimes, - * keyrings are not in the homedir, or location of the keyrings does - * not allow writing temporary files. In this case, the <i>homedir</i> - * option by itself is not enough to specify the keyrings because GnuPG - * can not write required temporary files. Additional options are - * provided so you can specify the location of the keyrings separately - * from the homedir. - */ - - // get public keyring - if (array_key_exists('publicKeyring', $options)) { - $this->_publicKeyring = (string)$options['publicKeyring']; - if (!is_readable($this->_publicKeyring)) { - throw new Crypt_GPG_FileException('The \'publicKeyring\' "' . - $this->_publicKeyring . '" does not exist or is ' . - 'not readable. Check the location and ensure the file ' . - 'permissions are correct.', 0, $this->_publicKeyring); - } - } - - // get private keyring - if (array_key_exists('privateKeyring', $options)) { - $this->_privateKeyring = (string)$options['privateKeyring']; - if (!is_readable($this->_privateKeyring)) { - throw new Crypt_GPG_FileException('The \'privateKeyring\' "' . - $this->_privateKeyring . '" does not exist or is ' . - 'not readable. Check the location and ensure the file ' . - 'permissions are correct.', 0, $this->_privateKeyring); - } - } - - // get trust database - if (array_key_exists('trustDb', $options)) { - $this->_trustDb = (string)$options['trustDb']; - if (!is_readable($this->_trustDb)) { - throw new Crypt_GPG_FileException('The \'trustDb\' "' . - $this->_trustDb . '" does not exist or is not readable. ' . - 'Check the location and ensure the file permissions are ' . - 'correct.', 0, $this->_trustDb); - } - } - - if (array_key_exists('debug', $options)) { - $this->_debug = (boolean)$options['debug']; - } - } - - // }}} - // {{{ __destruct() - - /** - * Closes open GPG subprocesses when this object is destroyed - * - * Subprocesses should never be left open by this class unless there is - * an unknown error and unexpected script termination occurs. - */ - public function __destruct() - { - $this->_closeSubprocess(); - } - - // }}} - // {{{ addErrorHandler() - - /** - * Adds an error handler method - * - * The method is run every time a new error line is received from the GPG - * subprocess. The handler method must accept the error line to be handled - * as its first parameter. - * - * @param callback $callback the callback method to use. - * @param array $args optional. Additional arguments to pass as - * parameters to the callback method. - * - * @return void - */ - public function addErrorHandler($callback, array $args = array()) - { - $this->_errorHandlers[] = array( - 'callback' => $callback, - 'args' => $args - ); - } - - // }}} - // {{{ addStatusHandler() - - /** - * Adds a status handler method - * - * The method is run every time a new status line is received from the - * GPG subprocess. The handler method must accept the status line to be - * handled as its first parameter. - * - * @param callback $callback the callback method to use. - * @param array $args optional. Additional arguments to pass as - * parameters to the callback method. - * - * @return void - */ - public function addStatusHandler($callback, array $args = array()) - { - $this->_statusHandlers[] = array( - 'callback' => $callback, - 'args' => $args - ); - } - - // }}} - // {{{ sendCommand() - - /** - * Sends a command to the GPG subprocess over the command file-descriptor - * pipe - * - * @param string $command the command to send. - * - * @return void - * - * @sensitive $command - */ - public function sendCommand($command) - { - if (array_key_exists(self::FD_COMMAND, $this->_openPipes)) { - $this->_commandBuffer .= $command . PHP_EOL; - } - } - - // }}} - // {{{ reset() - - /** - * Resets the GPG engine, preparing it for a new operation - * - * @return void - * - * @see Crypt_GPG_Engine::run() - * @see Crypt_GPG_Engine::setOperation() - */ - public function reset() - { - $this->_operation = ''; - $this->_arguments = array(); - $this->_input = null; - $this->_message = null; - $this->_output = ''; - $this->_errorCode = Crypt_GPG::ERROR_NONE; - $this->_needPassphrase = 0; - $this->_commandBuffer = ''; - - $this->_statusHandlers = array(); - $this->_errorHandlers = array(); - - $this->addStatusHandler(array($this, '_handleErrorStatus')); - $this->addErrorHandler(array($this, '_handleErrorError')); - - if ($this->_debug) { - $this->addStatusHandler(array($this, '_handleDebugStatus')); - $this->addErrorHandler(array($this, '_handleDebugError')); - } - } - - // }}} - // {{{ run() - - /** - * Runs the current GPG operation - * - * This creates and manages the GPG subprocess. - * - * The operation must be set with {@link Crypt_GPG_Engine::setOperation()} - * before this method is called. - * - * @return void - * - * @throws Crypt_GPG_InvalidOperationException if no operation is specified. - * - * @see Crypt_GPG_Engine::reset() - * @see Crypt_GPG_Engine::setOperation() - */ - public function run() - { - if ($this->_operation === '') { - throw new Crypt_GPG_InvalidOperationException('No GPG operation ' . - 'specified. Use Crypt_GPG_Engine::setOperation() before ' . - 'calling Crypt_GPG_Engine::run().'); - } - - $this->_openSubprocess(); - $this->_process(); - $this->_closeSubprocess(); - } - - // }}} - // {{{ getErrorCode() - - /** - * Gets the error code of the last executed operation - * - * This value is only meaningful after {@link Crypt_GPG_Engine::run()} has - * been executed. - * - * @return integer the error code of the last executed operation. - */ - public function getErrorCode() - { - return $this->_errorCode; - } - - // }}} - // {{{ getErrorFilename() - - /** - * Gets the file related to the error code of the last executed operation - * - * This value is only meaningful after {@link Crypt_GPG_Engine::run()} has - * been executed. If there is no file related to the error, an empty string - * is returned. - * - * @return string the file related to the error code of the last executed - * operation. - */ - public function getErrorFilename() - { - return $this->_errorFilename; - } - - // }}} - // {{{ getErrorKeyId() - - /** - * Gets the key id related to the error code of the last executed operation - * - * This value is only meaningful after {@link Crypt_GPG_Engine::run()} has - * been executed. If there is no key id related to the error, an empty - * string is returned. - * - * @return string the key id related to the error code of the last executed - * operation. - */ - public function getErrorKeyId() - { - return $this->_errorKeyId; - } - - // }}} - // {{{ setInput() - - /** - * Sets the input source for the current GPG operation - * - * @param string|resource &$input either a reference to the string - * containing the input data or an open - * stream resource containing the input - * data. - * - * @return void - */ - public function setInput(&$input) - { - $this->_input =& $input; - } - - // }}} - // {{{ setMessage() - - /** - * Sets the message source for the current GPG operation - * - * Detached signature data should be specified here. - * - * @param string|resource &$message either a reference to the string - * containing the message data or an open - * stream resource containing the message - * data. - * - * @return void - */ - public function setMessage(&$message) - { - $this->_message =& $message; - } - - // }}} - // {{{ setOutput() - - /** - * Sets the output destination for the current GPG operation - * - * @param string|resource &$output either a reference to the string in - * which to store GPG output or an open - * stream resource to which the output data - * should be written. - * - * @return void - */ - public function setOutput(&$output) - { - $this->_output =& $output; - } - - // }}} - // {{{ setOperation() - - /** - * Sets the operation to perform - * - * @param string $operation the operation to perform. This should be one - * of GPG's operations. For example, - * <kbd>--encrypt</kbd>, <kbd>--decrypt</kbd>, - * <kbd>--sign</kbd>, etc. - * @param array $arguments optional. Additional arguments for the GPG - * subprocess. See the GPG manual for specific - * values. - * - * @return void - * - * @see Crypt_GPG_Engine::reset() - * @see Crypt_GPG_Engine::run() - */ - public function setOperation($operation, array $arguments = array()) - { - $this->_operation = $operation; - $this->_arguments = $arguments; - } - - // }}} - // {{{ getVersion() - - /** - * Gets the version of the GnuPG binary - * - * @return string a version number string containing the version of GnuPG - * being used. This value is suitable to use with PHP's - * version_compare() function. - * - * @throws Crypt_GPG_Exception if an unknown or unexpected error occurs. - * Use the <kbd>debug</kbd> option and file a bug report if these - * exceptions occur. - * - * @throws Crypt_GPG_UnsupportedException if the provided binary is not - * GnuPG or if the GnuPG version is less than 1.0.2. - */ - public function getVersion() - { - if ($this->_version == '') { - - $options = array( - 'homedir' => $this->_homedir, - 'binary' => $this->_binary, - 'debug' => $this->_debug - ); - - $engine = new self($options); - $info = ''; - - // Set a garbage version so we do not end up looking up the version - // recursively. - $engine->_version = '1.0.0'; - - $engine->reset(); - $engine->setOutput($info); - $engine->setOperation('--version'); - $engine->run(); - - $code = $this->getErrorCode(); - - if ($code !== Crypt_GPG::ERROR_NONE) { - throw new Crypt_GPG_Exception( - 'Unknown error getting GnuPG version information. Please ' . - 'use the \'debug\' option when creating the Crypt_GPG ' . - 'object, and file a bug report at ' . Crypt_GPG::BUG_URI, - $code); - } - - $matches = array(); - $expression = '/gpg \(GnuPG\) (\S+)/'; - - if (preg_match($expression, $info, $matches) === 1) { - $this->_version = $matches[1]; - } else { - throw new Crypt_GPG_Exception( - 'No GnuPG version information provided by the binary "' . - $this->_binary . '". Are you sure it is GnuPG?'); - } - - if (version_compare($this->_version, self::MIN_VERSION, 'lt')) { - throw new Crypt_GPG_Exception( - 'The version of GnuPG being used (' . $this->_version . - ') is not supported by Crypt_GPG. The minimum version ' . - 'required by Crypt_GPG is ' . self::MIN_VERSION); - } - } - - - return $this->_version; - } - - // }}} - // {{{ _handleErrorStatus() - - /** - * Handles error values in the status output from GPG - * - * This method is responsible for setting the - * {@link Crypt_GPG_Engine::$_errorCode}. See <b>doc/DETAILS</b> in the - * {@link http://www.gnupg.org/download/ GPG distribution} for detailed - * information on GPG's status output. - * - * @param string $line the status line to handle. - * - * @return void - */ - private function _handleErrorStatus($line) - { - $tokens = explode(' ', $line); - switch ($tokens[0]) { - case 'BAD_PASSPHRASE': - $this->_errorCode = Crypt_GPG::ERROR_BAD_PASSPHRASE; - break; - - case 'MISSING_PASSPHRASE': - $this->_errorCode = Crypt_GPG::ERROR_MISSING_PASSPHRASE; - break; - - case 'NODATA': - $this->_errorCode = Crypt_GPG::ERROR_NO_DATA; - break; - - case 'DELETE_PROBLEM': - if ($tokens[1] == '1') { - $this->_errorCode = Crypt_GPG::ERROR_KEY_NOT_FOUND; - break; - } elseif ($tokens[1] == '2') { - $this->_errorCode = Crypt_GPG::ERROR_DELETE_PRIVATE_KEY; - break; - } - break; - - case 'IMPORT_RES': - if ($tokens[12] > 0) { - $this->_errorCode = Crypt_GPG::ERROR_DUPLICATE_KEY; - } - break; - - case 'NO_PUBKEY': - case 'NO_SECKEY': - $this->_errorKeyId = $tokens[1]; - $this->_errorCode = Crypt_GPG::ERROR_KEY_NOT_FOUND; - break; - - case 'NEED_PASSPHRASE': - $this->_needPassphrase++; - break; - - case 'GOOD_PASSPHRASE': - $this->_needPassphrase--; - break; - - case 'EXPSIG': - case 'EXPKEYSIG': - case 'REVKEYSIG': - case 'BADSIG': - $this->_errorCode = Crypt_GPG::ERROR_BAD_SIGNATURE; - break; - - } - } - - // }}} - // {{{ _handleErrorError() - - /** - * Handles error values in the error output from GPG - * - * This method is responsible for setting the - * {@link Crypt_GPG_Engine::$_errorCode}. - * - * @param string $line the error line to handle. - * - * @return void - */ - private function _handleErrorError($line) - { - if ($this->_errorCode === Crypt_GPG::ERROR_NONE) { - $pattern = '/no valid OpenPGP data found/'; - if (preg_match($pattern, $line) === 1) { - $this->_errorCode = Crypt_GPG::ERROR_NO_DATA; - } - } - - if ($this->_errorCode === Crypt_GPG::ERROR_NONE) { - $pattern = '/No secret key|secret key not available/'; - if (preg_match($pattern, $line) === 1) { - $this->_errorCode = Crypt_GPG::ERROR_KEY_NOT_FOUND; - } - } - - if ($this->_errorCode === Crypt_GPG::ERROR_NONE) { - $pattern = '/No public key|public key not found/'; - if (preg_match($pattern, $line) === 1) { - $this->_errorCode = Crypt_GPG::ERROR_KEY_NOT_FOUND; - } - } - - if ($this->_errorCode === Crypt_GPG::ERROR_NONE) { - $matches = array(); - $pattern = '/can\'t (?:access|open) `(.*?)\'/'; - if (preg_match($pattern, $line, $matches) === 1) { - $this->_errorFilename = $matches[1]; - $this->_errorCode = Crypt_GPG::ERROR_FILE_PERMISSIONS; - } - } - } - - // }}} - // {{{ _handleDebugStatus() - - /** - * Displays debug output for status lines - * - * @param string $line the status line to handle. - * - * @return void - */ - private function _handleDebugStatus($line) - { - $this->_debug('STATUS: ' . $line); - } - - // }}} - // {{{ _handleDebugError() - - /** - * Displays debug output for error lines - * - * @param string $line the error line to handle. - * - * @return void - */ - private function _handleDebugError($line) - { - $this->_debug('ERROR: ' . $line); - } - - // }}} - // {{{ _process() - - /** - * Performs internal streaming operations for the subprocess using either - * strings or streams as input / output points - * - * This is the main I/O loop for streaming to and from the GPG subprocess. - * - * The implementation of this method is verbose mainly for performance - * reasons. Adding streams to a lookup array and looping the array inside - * the main I/O loop would be siginficantly slower for large streams. - * - * @return void - * - * @throws Crypt_GPG_Exception if there is an error selecting streams for - * reading or writing. If this occurs, please file a bug report at - * http://pear.php.net/bugs/report.php?package=Crypt_GPG. - */ - private function _process() - { - $this->_debug('BEGIN PROCESSING'); - - $this->_commandBuffer = ''; // buffers input to GPG - $messageBuffer = ''; // buffers input to GPG - $inputBuffer = ''; // buffers input to GPG - $outputBuffer = ''; // buffers output from GPG - $statusBuffer = ''; // buffers output from GPG - $errorBuffer = ''; // buffers output from GPG - $inputComplete = false; // input stream is completely buffered - $messageComplete = false; // message stream is completely buffered - - if (is_string($this->_input)) { - $inputBuffer = $this->_input; - $inputComplete = true; - } - - if (is_string($this->_message)) { - $messageBuffer = $this->_message; - $messageComplete = true; - } - - if (is_string($this->_output)) { - $outputBuffer =& $this->_output; - } - - // convenience variables - $fdInput = $this->_pipes[self::FD_INPUT]; - $fdOutput = $this->_pipes[self::FD_OUTPUT]; - $fdError = $this->_pipes[self::FD_ERROR]; - $fdStatus = $this->_pipes[self::FD_STATUS]; - $fdCommand = $this->_pipes[self::FD_COMMAND]; - $fdMessage = $this->_pipes[self::FD_MESSAGE]; - - while (true) { - - $inputStreams = array(); - $outputStreams = array(); - $exceptionStreams = array(); - - // set up input streams - if (is_resource($this->_input) && !$inputComplete) { - if (feof($this->_input)) { - $inputComplete = true; - } else { - $inputStreams[] = $this->_input; - } - } - - // close GPG input pipe if there is no more data - if ($inputBuffer == '' && $inputComplete) { - $this->_debug('=> closing GPG input pipe'); - $this->_closePipe(self::FD_INPUT); - } - - if (is_resource($this->_message) && !$messageComplete) { - if (feof($this->_message)) { - $messageComplete = true; - } else { - $inputStreams[] = $this->_message; - } - } - - // close GPG message pipe if there is no more data - if ($messageBuffer == '' && $messageComplete) { - $this->_debug('=> closing GPG message pipe'); - $this->_closePipe(self::FD_MESSAGE); - } - - if (!feof($fdOutput)) { - $inputStreams[] = $fdOutput; - } - - if (!feof($fdStatus)) { - $inputStreams[] = $fdStatus; - } - - if (!feof($fdError)) { - $inputStreams[] = $fdError; - } - - // set up output streams - if ($outputBuffer != '' && is_resource($this->_output)) { - $outputStreams[] = $this->_output; - } - - if ($this->_commandBuffer != '') { - $outputStreams[] = $fdCommand; - } - - if ($messageBuffer != '') { - $outputStreams[] = $fdMessage; - } - - if ($inputBuffer != '') { - $outputStreams[] = $fdInput; - } - - // no streams left to read or write, we're all done - if (count($inputStreams) === 0 && count($outputStreams) === 0) { - break; - } - - $this->_debug('selecting streams'); - - $ready = stream_select( - $inputStreams, - $outputStreams, - $exceptionStreams, - null - ); - - $this->_debug('=> got ' . $ready); - - if ($ready === false) { - throw new Crypt_GPG_Exception( - 'Error selecting stream for communication with GPG ' . - 'subprocess. Please file a bug report at: ' . - 'http://pear.php.net/bugs/report.php?package=Crypt_GPG'); - } - - if ($ready === 0) { - throw new Crypt_GPG_Exception( - 'stream_select() returned 0. This can not happen! Please ' . - 'file a bug report at: ' . - 'http://pear.php.net/bugs/report.php?package=Crypt_GPG'); - } - - // write input (to GPG) - if (in_array($fdInput, $outputStreams)) { - $this->_debug('GPG is ready for input'); - - $chunk = self::_byteSubstring( - $inputBuffer, - 0, - self::CHUNK_SIZE - ); - - $length = self::_byteLength($chunk); - - $this->_debug( - '=> about to write ' . $length . ' bytes to GPG input' - ); - - $length = fwrite($fdInput, $chunk, $length); - - $this->_debug('=> wrote ' . $length . ' bytes'); - - $inputBuffer = self::_byteSubstring( - $inputBuffer, - $length - ); - } - - // read input (from PHP stream) - if (in_array($this->_input, $inputStreams)) { - $this->_debug('input stream is ready for reading'); - $this->_debug( - '=> about to read ' . self::CHUNK_SIZE . - ' bytes from input stream' - ); - - $chunk = fread($this->_input, self::CHUNK_SIZE); - $length = self::_byteLength($chunk); - $inputBuffer .= $chunk; - - $this->_debug('=> read ' . $length . ' bytes'); - } - - // write message (to GPG) - if (in_array($fdMessage, $outputStreams)) { - $this->_debug('GPG is ready for message data'); - - $chunk = self::_byteSubstring( - $messageBuffer, - 0, - self::CHUNK_SIZE - ); - - $length = self::_byteLength($chunk); - - $this->_debug( - '=> about to write ' . $length . ' bytes to GPG message' - ); - - $length = fwrite($fdMessage, $chunk, $length); - $this->_debug('=> wrote ' . $length . ' bytes'); - - $messageBuffer = self::_byteSubstring($messageBuffer, $length); - } - - // read message (from PHP stream) - if (in_array($this->_message, $inputStreams)) { - $this->_debug('message stream is ready for reading'); - $this->_debug( - '=> about to read ' . self::CHUNK_SIZE . - ' bytes from message stream' - ); - - $chunk = fread($this->_message, self::CHUNK_SIZE); - $length = self::_byteLength($chunk); - $messageBuffer .= $chunk; - - $this->_debug('=> read ' . $length . ' bytes'); - } - - // read output (from GPG) - if (in_array($fdOutput, $inputStreams)) { - $this->_debug('GPG output stream ready for reading'); - $this->_debug( - '=> about to read ' . self::CHUNK_SIZE . - ' bytes from GPG output' - ); - - $chunk = fread($fdOutput, self::CHUNK_SIZE); - $length = self::_byteLength($chunk); - $outputBuffer .= $chunk; - - $this->_debug('=> read ' . $length . ' bytes'); - } - - // write output (to PHP stream) - if (in_array($this->_output, $outputStreams)) { - $this->_debug('output stream is ready for data'); - - $chunk = self::_byteSubstring( - $outputBuffer, - 0, - self::CHUNK_SIZE - ); - - $length = self::_byteLength($chunk); - - $this->_debug( - '=> about to write ' . $length . ' bytes to output stream' - ); - - $length = fwrite($this->_output, $chunk, $length); - - $this->_debug('=> wrote ' . $length . ' bytes'); - - $outputBuffer = self::_byteSubstring($outputBuffer, $length); - } - - // read error (from GPG) - if (in_array($fdError, $inputStreams)) { - $this->_debug('GPG error stream ready for reading'); - $this->_debug( - '=> about to read ' . self::CHUNK_SIZE . - ' bytes from GPG error' - ); - - $chunk = fread($fdError, self::CHUNK_SIZE); - $length = self::_byteLength($chunk); - $errorBuffer .= $chunk; - - $this->_debug('=> read ' . $length . ' bytes'); - - // pass lines to error handlers - while (($pos = strpos($errorBuffer, PHP_EOL)) !== false) { - $line = self::_byteSubstring($errorBuffer, 0, $pos); - foreach ($this->_errorHandlers as $handler) { - array_unshift($handler['args'], $line); - call_user_func_array( - $handler['callback'], - $handler['args'] - ); - - array_shift($handler['args']); - } - $errorBuffer = self::_byteSubString( - $errorBuffer, - $pos + self::_byteLength(PHP_EOL) - ); - } - } - - // read status (from GPG) - if (in_array($fdStatus, $inputStreams)) { - $this->_debug('GPG status stream ready for reading'); - $this->_debug( - '=> about to read ' . self::CHUNK_SIZE . - ' bytes from GPG status' - ); - - $chunk = fread($fdStatus, self::CHUNK_SIZE); - $length = self::_byteLength($chunk); - $statusBuffer .= $chunk; - - $this->_debug('=> read ' . $length . ' bytes'); - - // pass lines to status handlers - while (($pos = strpos($statusBuffer, PHP_EOL)) !== false) { - $line = self::_byteSubstring($statusBuffer, 0, $pos); - // only pass lines beginning with magic prefix - if (self::_byteSubstring($line, 0, 9) == '[GNUPG:] ') { - $line = self::_byteSubstring($line, 9); - foreach ($this->_statusHandlers as $handler) { - array_unshift($handler['args'], $line); - call_user_func_array( - $handler['callback'], - $handler['args'] - ); - - array_shift($handler['args']); - } - } - $statusBuffer = self::_byteSubString( - $statusBuffer, - $pos + self::_byteLength(PHP_EOL) - ); - } - } - - // write command (to GPG) - if (in_array($fdCommand, $outputStreams)) { - $this->_debug('GPG is ready for command data'); - - // send commands - $chunk = self::_byteSubstring( - $this->_commandBuffer, - 0, - self::CHUNK_SIZE - ); - - $length = self::_byteLength($chunk); - - $this->_debug( - '=> about to write ' . $length . ' bytes to GPG command' - ); - - $length = fwrite($fdCommand, $chunk, $length); - - $this->_debug('=> wrote ' . $length); - - $this->_commandBuffer = self::_byteSubstring( - $this->_commandBuffer, - $length - ); - } - - } // end loop while streams are open - - $this->_debug('END PROCESSING'); - } - - // }}} - // {{{ _openSubprocess() - - /** - * Opens an internal GPG subprocess for the current operation - * - * Opens a GPG subprocess, then connects the subprocess to some pipes. Sets - * the private class property {@link Crypt_GPG_Engine::$_process} to - * the new subprocess. - * - * @return void - * - * @throws Crypt_GPG_OpenSubprocessException if the subprocess could not be - * opened. - * - * @see Crypt_GPG_Engine::setOperation() - * @see Crypt_GPG_Engine::_closeSubprocess() - * @see Crypt_GPG_Engine::$_process - */ - private function _openSubprocess() - { - $version = $this->getVersion(); - - $env = $_ENV; - - // Newer versions of GnuPG return localized results. Crypt_GPG only - // works with English, so set the locale to 'C' for the subprocess. - $env['LC_ALL'] = 'C'; - - $commandLine = $this->_binary; - - $defaultArguments = array( - '--status-fd ' . escapeshellarg(self::FD_STATUS), - '--command-fd ' . escapeshellarg(self::FD_COMMAND), - '--no-secmem-warning', - '--no-tty', - '--no-default-keyring', // ignored if keying files are not specified - '--no-options' // prevent creation of ~/.gnupg directory - ); - - if (version_compare($version, '1.0.7', 'ge')) { - if (version_compare($version, '2.0.0', 'lt')) { - $defaultArguments[] = '--no-use-agent'; - } - $defaultArguments[] = '--no-permission-warning'; - } - - if (version_compare($version, '1.4.2', 'ge')) { - $defaultArguments[] = '--exit-on-status-write-error'; - } - - if (version_compare($version, '1.3.2', 'ge')) { - $defaultArguments[] = '--trust-model always'; - } else { - $defaultArguments[] = '--always-trust'; - } - - $arguments = array_merge($defaultArguments, $this->_arguments); - - if ($this->_homedir) { - $arguments[] = '--homedir ' . escapeshellarg($this->_homedir); - - // the random seed file makes subsequent actions faster so only - // disable it if we have to. - if (!is_writeable($this->_homedir)) { - $arguments[] = '--no-random-seed-file'; - } - } - - if ($this->_publicKeyring) { - $arguments[] = '--keyring ' . escapeshellarg($this->_publicKeyring); - } - - if ($this->_privateKeyring) { - $arguments[] = '--secret-keyring ' . - escapeshellarg($this->_privateKeyring); - } - - if ($this->_trustDb) { - $arguments[] = '--trustdb-name ' . escapeshellarg($this->_trustDb); - } - - $commandLine .= ' ' . implode(' ', $arguments) . ' ' . - $this->_operation; - - // Binary operations will not work on Windows with PHP < 5.2.6. This is - // in case stream_select() ever works on Windows. - $rb = (version_compare(PHP_VERSION, '5.2.6') < 0) ? 'r' : 'rb'; - $wb = (version_compare(PHP_VERSION, '5.2.6') < 0) ? 'w' : 'wb'; - - $descriptorSpec = array( - self::FD_INPUT => array('pipe', $rb), // stdin - self::FD_OUTPUT => array('pipe', $wb), // stdout - self::FD_ERROR => array('pipe', $wb), // stderr - self::FD_STATUS => array('pipe', $wb), // status - self::FD_COMMAND => array('pipe', $rb), // command - self::FD_MESSAGE => array('pipe', $rb) // message - ); - - $this->_debug('OPENING SUBPROCESS WITH THE FOLLOWING COMMAND:'); - $this->_debug($commandLine); - - $this->_process = proc_open( - $commandLine, - $descriptorSpec, - $this->_pipes, - null, - $env, - array('binary_pipes' => true) - ); - - if (!is_resource($this->_process)) { - throw new Crypt_GPG_OpenSubprocessException( - 'Unable to open GPG subprocess.', 0, $commandLine); - } - - $this->_openPipes = $this->_pipes; - $this->_errorCode = Crypt_GPG::ERROR_NONE; - } - - // }}} - // {{{ _closeSubprocess() - - /** - * Closes a the internal GPG subprocess - * - * Closes the internal GPG subprocess. Sets the private class property - * {@link Crypt_GPG_Engine::$_process} to null. - * - * @return void - * - * @see Crypt_GPG_Engine::_openSubprocess() - * @see Crypt_GPG_Engine::$_process - */ - private function _closeSubprocess() - { - if (is_resource($this->_process)) { - $this->_debug('CLOSING SUBPROCESS'); - - // close remaining open pipes - foreach (array_keys($this->_openPipes) as $pipeNumber) { - $this->_closePipe($pipeNumber); - } - - $exitCode = proc_close($this->_process); - - if ($exitCode != 0) { - $this->_debug( - '=> subprocess returned an unexpected exit code: ' . - $exitCode - ); - - if ($this->_errorCode === Crypt_GPG::ERROR_NONE) { - if ($this->_needPassphrase > 0) { - $this->_errorCode = Crypt_GPG::ERROR_MISSING_PASSPHRASE; - } else { - $this->_errorCode = Crypt_GPG::ERROR_UNKNOWN; - } - } - } - - $this->_process = null; - $this->_pipes = array(); - } - } - - // }}} - // {{{ _closePipe() - - /** - * Closes an opened pipe used to communicate with the GPG subprocess - * - * If the pipe is already closed, it is ignored. If the pipe is open, it - * is flushed and then closed. - * - * @param integer $pipeNumber the file descriptor number of the pipe to - * close. - * - * @return void - */ - private function _closePipe($pipeNumber) - { - $pipeNumber = intval($pipeNumber); - if (array_key_exists($pipeNumber, $this->_openPipes)) { - fflush($this->_openPipes[$pipeNumber]); - fclose($this->_openPipes[$pipeNumber]); - unset($this->_openPipes[$pipeNumber]); - } - } - - // }}} - // {{{ _getBinary() - - /** - * Gets the name of the GPG binary for the current operating system - * - * This method is called if the '<kbd>binary</kbd>' option is <i>not</i> - * specified when creating this driver. - * - * @return string the name of the GPG binary for the current operating - * system. If no suitable binary could be found, an empty - * string is returned. - */ - private function _getBinary() - { - $binary = ''; - - if ($this->_isDarwin) { - $binaryFiles = array( - '/opt/local/bin/gpg', // MacPorts - '/usr/local/bin/gpg', // Mac GPG - '/sw/bin/gpg', // Fink - '/usr/bin/gpg' - ); - } else { - $binaryFiles = array( - '/usr/bin/gpg', - '/usr/local/bin/gpg' - ); - } - - foreach ($binaryFiles as $binaryFile) { - if (is_executable($binaryFile)) { - $binary = $binaryFile; - break; - } - } - - return $binary; - } - - // }}} - // {{{ _debug() - - /** - * Displays debug text if debugging is turned on - * - * Debugging text is prepended with a debug identifier and echoed to stdout. - * - * @param string $text the debugging text to display. - * - * @return void - */ - private function _debug($text) - { - if ($this->_debug) { - if (array_key_exists('SHELL', $_ENV)) { - foreach (explode(PHP_EOL, $text) as $line) { - echo "Crypt_GPG DEBUG: ", $line, PHP_EOL; - } - } else { - // running on a web server, format debug output nicely - foreach (explode(PHP_EOL, $text) as $line) { - echo "Crypt_GPG DEBUG: <strong>", $line, - '</strong><br />', PHP_EOL; - } - } - } - } - - // }}} - // {{{ _byteLength() - - /** - * Gets the length of a string in bytes even if mbstring function - * overloading is turned on - * - * This is used for stream-based communication with the GPG subprocess. - * - * @param string $string the string for which to get the length. - * - * @return integer the length of the string in bytes. - * - * @see Crypt_GPG_Engine::$_mbStringOverload - */ - private static function _byteLength($string) - { - if (self::$_mbStringOverload) { - return mb_strlen($string, '8bit'); - } - - return strlen((binary)$string); - } - - // }}} - // {{{ _byteSubstring() - - /** - * Gets the substring of a string in bytes even if mbstring function - * overloading is turned on - * - * This is used for stream-based communication with the GPG subprocess. - * - * @param string $string the input string. - * @param integer $start the starting point at which to get the substring. - * @param integer $length optional. The length of the substring. - * - * @return string the extracted part of the string. Unlike the default PHP - * <kbd>substr()</kbd> function, the returned value is - * always a string and never false. - * - * @see Crypt_GPG_Engine::$_mbStringOverload - */ - private static function _byteSubstring($string, $start, $length = null) - { - if (self::$_mbStringOverload) { - if ($length === null) { - return mb_substr( - $string, - $start, - self::_byteLength($string) - $start, '8bit' - ); - } - - return mb_substr($string, $start, $length, '8bit'); - } - - if ($length === null) { - return (string)substr((binary)$string, $start); - } - - return (string)substr((binary)$string, $start, $length); - } - - // }}} -} - -// }}} - -?> |