diff options
Diffstat (limited to 'program/lib/Crypt/GPG/Engine.php')
-rw-r--r-- | program/lib/Crypt/GPG/Engine.php | 560 |
1 files changed, 404 insertions, 156 deletions
diff --git a/program/lib/Crypt/GPG/Engine.php b/program/lib/Crypt/GPG/Engine.php index 081be8e21..601541443 100644 --- a/program/lib/Crypt/GPG/Engine.php +++ b/program/lib/Crypt/GPG/Engine.php @@ -30,9 +30,9 @@ * @package Crypt_GPG * @author Nathan Fredrickson <nathan@silverorange.com> * @author Michael Gauthier <mike@silverorange.com> - * @copyright 2005-2010 silverorange + * @copyright 2005-2013 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 $ + * @version CVS: $Id$ * @link http://pear.php.net/package/Crypt_GPG * @link http://www.gnupg.org/ */ @@ -48,6 +48,16 @@ require_once 'Crypt/GPG.php'; require_once 'Crypt/GPG/Exceptions.php'; /** + * Byte string operations. + */ +require_once 'Crypt/GPG/ByteUtils.php'; + +/** + * Process control methods. + */ +require_once 'Crypt/GPG/ProcessControl.php'; + +/** * Standard PEAR exception is used if GPG binary is not found. */ require_once 'PEAR/Exception.php'; @@ -70,7 +80,7 @@ require_once 'PEAR/Exception.php'; * @package Crypt_GPG * @author Nathan Fredrickson <nathan@silverorange.com> * @author Michael Gauthier <mike@silverorange.com> - * @copyright 2005-2010 silverorange + * @copyright 2005-2013 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/ @@ -163,6 +173,17 @@ class Crypt_GPG_Engine private $_binary = ''; /** + * Location of GnuPG agent binary + * + * Only used for GnuPG 2.x + * + * @var string + * @see Crypt_GPG_Engine::__construct() + * @see Crypt_GPG_Engine::_getAgent() + */ + private $_agent = ''; + + /** * Directory containing the GPG key files * * This property only contains the path when the <i>homedir</i> option @@ -228,6 +249,15 @@ class Crypt_GPG_Engine private $_pipes = array(); /** + * Array of pipes used for communication with the gpg-agent binary + * + * This is an array of file descriptor resources. + * + * @var array + */ + private $_agentPipes = array(); + + /** * Array of currently opened pipes * * This array is used to keep track of remaining opened pipes so they can @@ -248,6 +278,20 @@ class Crypt_GPG_Engine private $_process = null; /** + * A handle for the gpg-agent process + * + * @var resource + */ + private $_agentProcess = null; + + /** + * GPG agent daemon socket and PID for running gpg-agent + * + * @var string + */ + private $_agentInfo = null; + + /** * Whether or not the operating system is Darwin (OS X) * * @var boolean @@ -367,18 +411,6 @@ class Crypt_GPG_Engine */ 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() @@ -432,6 +464,14 @@ class Crypt_GPG_Engine * operating system. The option * <kbd>gpgBinary</kbd> is a * deprecated alias for this option. + * - <kbd>string agent</kbd> - the location of the GnuPG agent + * binary. The gpg-agent is only + * used for GnuPG 2.x. If not + * specified, the engine attempts + * to auto-detect the gpg-agent + * binary location using a list of + * know default locations for the + * current operating system. * - <kbd>boolean debug</kbd> - whether or not to use debug mode. * When debug mode is on, all * communication to and from the GPG @@ -457,24 +497,38 @@ class Crypt_GPG_Engine * @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. + * + * @throws PEAR_Exception if the provided <kbd>agent</kbd> is invalid, or + * if no <kbd>agent</kbd> is provided and no suitable gpg-agent + * cound 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'; + if (extension_loaded('posix')) { + // note: this requires the package OS dep exclude 'windows' + $info = posix_getpwuid(posix_getuid()); + $this->_homedir = $info['dir'].'/.gnupg'; + } else { + if (isset($_SERVER['HOME'])) { + $this->_homedir = $_SERVER['HOME']; + } else { + $this->_homedir = getenv('HOME'); + } + } + + if ($this->_homedir === false) { + throw new Crypt_GPG_FileException( + 'Could not locate homedir. Please specify the homedir ' . + 'to use with the \'homedir\' option when instantiating ' . + 'the Crypt_GPG object.' + ); + } } // attempt to create homedir if it does not exist @@ -484,16 +538,40 @@ class Crypt_GPG_Engine // 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); + 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 + ); } } + // check homedir permissions (See Bug #19833) + if (!is_executable($this->_homedir)) { + throw new Crypt_GPG_FileException( + 'The \'homedir\' "' . $this->_homedir . '" is not enterable ' . + 'by the current user. Please check the permissions on your ' . + 'homedir and make sure the current user can both enter and ' . + 'write to the directory.', + 0, + $this->_homedir + ); + } + if (!is_writeable($this->_homedir)) { + throw new Crypt_GPG_FileException( + 'The \'homedir\' "' . $this->_homedir . '" is not writable ' . + 'by the current user. Please check the permissions on your ' . + 'homedir and make sure the current user can both enter and ' . + 'write to the directory.', + 0, + $this->_homedir + ); + } + // get binary if (array_key_exists('binary', $options)) { $this->_binary = (string)$options['binary']; @@ -505,9 +583,26 @@ class Crypt_GPG_Engine } 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.'); + 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.' + ); + } + + // get agent + if (array_key_exists('agent', $options)) { + $this->_agent = (string)$options['agent']; + } else { + $this->_agent = $this->_getAgent(); + } + + if ($this->_agent == '' || !is_executable($this->_agent)) { + throw new PEAR_Exception( + 'gpg-agent binary not found. If you are sure the gpg-agent ' . + 'is installed, please specify the location of the gpg-agent ' . + 'binary using the \'agent\' driver option.' + ); } /* @@ -891,7 +986,7 @@ class Crypt_GPG_Engine } $matches = array(); - $expression = '/gpg \(GnuPG\) (\S+)/'; + $expression = '#gpg \(GnuPG[A-Za-z0-9/]*?\) (\S+)#'; if (preg_match($expression, $info, $matches) === 1) { $this->_version = $matches[1]; @@ -1114,6 +1209,9 @@ class Crypt_GPG_Engine $fdCommand = $this->_pipes[self::FD_COMMAND]; $fdMessage = $this->_pipes[self::FD_MESSAGE]; + // select loop delay in milliseconds + $delay = 0; + while (true) { $inputStreams = array(); @@ -1166,15 +1264,15 @@ class Crypt_GPG_Engine $outputStreams[] = $this->_output; } - if ($this->_commandBuffer != '') { + if ($this->_commandBuffer != '' && is_resource($fdCommand)) { $outputStreams[] = $fdCommand; } - if ($messageBuffer != '') { + if ($messageBuffer != '' && is_resource($fdMessage)) { $outputStreams[] = $fdMessage; } - if ($inputBuffer != '') { + if ($inputBuffer != '' && is_resource($fdInput)) { $outputStreams[] = $fdInput; } @@ -1209,33 +1307,41 @@ class Crypt_GPG_Engine } // write input (to GPG) - if (in_array($fdInput, $outputStreams)) { + if (in_array($fdInput, $outputStreams, true)) { $this->_debug('GPG is ready for input'); - $chunk = self::_byteSubstring( + $chunk = Crypt_GPG_ByteUtils::substr( $inputBuffer, 0, self::CHUNK_SIZE ); - $length = self::_byteLength($chunk); + $length = Crypt_GPG_ByteUtils::strlen($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 - ); + if ($length === 0) { + // If we wrote 0 bytes it was either EAGAIN or EPIPE. Since + // the pipe was seleted for writing, we assume it was EPIPE. + // There's no way to get the actual erorr code in PHP. See + // PHP Bug #39598. https://bugs.php.net/bug.php?id=39598 + $this->_debug('=> broken pipe on GPG input'); + $this->_debug('=> closing pipe GPG input'); + $this->_closePipe(self::FD_INPUT); + } else { + $this->_debug('=> wrote ' . $length . ' bytes'); + $inputBuffer = Crypt_GPG_ByteUtils::substr( + $inputBuffer, + $length + ); + } } // read input (from PHP stream) - if (in_array($this->_input, $inputStreams)) { + if (in_array($this->_input, $inputStreams, true)) { $this->_debug('input stream is ready for reading'); $this->_debug( '=> about to read ' . self::CHUNK_SIZE . @@ -1243,36 +1349,48 @@ class Crypt_GPG_Engine ); $chunk = fread($this->_input, self::CHUNK_SIZE); - $length = self::_byteLength($chunk); + $length = Crypt_GPG_ByteUtils::strlen($chunk); $inputBuffer .= $chunk; $this->_debug('=> read ' . $length . ' bytes'); } // write message (to GPG) - if (in_array($fdMessage, $outputStreams)) { + if (in_array($fdMessage, $outputStreams, true)) { $this->_debug('GPG is ready for message data'); - $chunk = self::_byteSubstring( + $chunk = Crypt_GPG_ByteUtils::substr( $messageBuffer, 0, self::CHUNK_SIZE ); - $length = self::_byteLength($chunk); + $length = Crypt_GPG_ByteUtils::strlen($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); + if ($length === 0) { + // If we wrote 0 bytes it was either EAGAIN or EPIPE. Since + // the pipe was seleted for writing, we assume it was EPIPE. + // There's no way to get the actual erorr code in PHP. See + // PHP Bug #39598. https://bugs.php.net/bug.php?id=39598 + $this->_debug('=> broken pipe on GPG message'); + $this->_debug('=> closing pipe GPG message'); + $this->_closePipe(self::FD_MESSAGE); + } else { + $this->_debug('=> wrote ' . $length . ' bytes'); + $messageBuffer = Crypt_GPG_ByteUtils::substr( + $messageBuffer, + $length + ); + } } // read message (from PHP stream) - if (in_array($this->_message, $inputStreams)) { + if (in_array($this->_message, $inputStreams, true)) { $this->_debug('message stream is ready for reading'); $this->_debug( '=> about to read ' . self::CHUNK_SIZE . @@ -1280,14 +1398,14 @@ class Crypt_GPG_Engine ); $chunk = fread($this->_message, self::CHUNK_SIZE); - $length = self::_byteLength($chunk); + $length = Crypt_GPG_ByteUtils::strlen($chunk); $messageBuffer .= $chunk; $this->_debug('=> read ' . $length . ' bytes'); } // read output (from GPG) - if (in_array($fdOutput, $inputStreams)) { + if (in_array($fdOutput, $inputStreams, true)) { $this->_debug('GPG output stream ready for reading'); $this->_debug( '=> about to read ' . self::CHUNK_SIZE . @@ -1295,23 +1413,23 @@ class Crypt_GPG_Engine ); $chunk = fread($fdOutput, self::CHUNK_SIZE); - $length = self::_byteLength($chunk); + $length = Crypt_GPG_ByteUtils::strlen($chunk); $outputBuffer .= $chunk; $this->_debug('=> read ' . $length . ' bytes'); } // write output (to PHP stream) - if (in_array($this->_output, $outputStreams)) { + if (in_array($this->_output, $outputStreams, true)) { $this->_debug('output stream is ready for data'); - $chunk = self::_byteSubstring( + $chunk = Crypt_GPG_ByteUtils::substr( $outputBuffer, 0, self::CHUNK_SIZE ); - $length = self::_byteLength($chunk); + $length = Crypt_GPG_ByteUtils::strlen($chunk); $this->_debug( '=> about to write ' . $length . ' bytes to output stream' @@ -1321,11 +1439,14 @@ class Crypt_GPG_Engine $this->_debug('=> wrote ' . $length . ' bytes'); - $outputBuffer = self::_byteSubstring($outputBuffer, $length); + $outputBuffer = Crypt_GPG_ByteUtils::substr( + $outputBuffer, + $length + ); } // read error (from GPG) - if (in_array($fdError, $inputStreams)) { + if (in_array($fdError, $inputStreams, true)) { $this->_debug('GPG error stream ready for reading'); $this->_debug( '=> about to read ' . self::CHUNK_SIZE . @@ -1333,14 +1454,14 @@ class Crypt_GPG_Engine ); $chunk = fread($fdError, self::CHUNK_SIZE); - $length = self::_byteLength($chunk); + $length = Crypt_GPG_ByteUtils::strlen($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); + $line = Crypt_GPG_ByteUtils::substr($errorBuffer, 0, $pos); foreach ($this->_errorHandlers as $handler) { array_unshift($handler['args'], $line); call_user_func_array( @@ -1350,15 +1471,15 @@ class Crypt_GPG_Engine array_shift($handler['args']); } - $errorBuffer = self::_byteSubString( + $errorBuffer = Crypt_GPG_ByteUtils::substr( $errorBuffer, - $pos + self::_byteLength(PHP_EOL) + $pos + Crypt_GPG_ByteUtils::strlen(PHP_EOL) ); } } // read status (from GPG) - if (in_array($fdStatus, $inputStreams)) { + if (in_array($fdStatus, $inputStreams, true)) { $this->_debug('GPG status stream ready for reading'); $this->_debug( '=> about to read ' . self::CHUNK_SIZE . @@ -1366,17 +1487,17 @@ class Crypt_GPG_Engine ); $chunk = fread($fdStatus, self::CHUNK_SIZE); - $length = self::_byteLength($chunk); + $length = Crypt_GPG_ByteUtils::strlen($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); + $line = Crypt_GPG_ByteUtils::substr($statusBuffer, 0, $pos); // only pass lines beginning with magic prefix - if (self::_byteSubstring($line, 0, 9) == '[GNUPG:] ') { - $line = self::_byteSubstring($line, 9); + if (Crypt_GPG_ByteUtils::substr($line, 0, 9) == '[GNUPG:] ') { + $line = Crypt_GPG_ByteUtils::substr($line, 9); foreach ($this->_statusHandlers as $handler) { array_unshift($handler['args'], $line); call_user_func_array( @@ -1387,38 +1508,60 @@ class Crypt_GPG_Engine array_shift($handler['args']); } } - $statusBuffer = self::_byteSubString( + $statusBuffer = Crypt_GPG_ByteUtils::substr( $statusBuffer, - $pos + self::_byteLength(PHP_EOL) + $pos + Crypt_GPG_ByteUtils::strlen(PHP_EOL) ); } } // write command (to GPG) - if (in_array($fdCommand, $outputStreams)) { + if (in_array($fdCommand, $outputStreams, true)) { $this->_debug('GPG is ready for command data'); // send commands - $chunk = self::_byteSubstring( + $chunk = Crypt_GPG_ByteUtils::substr( $this->_commandBuffer, 0, self::CHUNK_SIZE ); - $length = self::_byteLength($chunk); + $length = Crypt_GPG_ByteUtils::strlen($chunk); $this->_debug( '=> about to write ' . $length . ' bytes to GPG command' ); $length = fwrite($fdCommand, $chunk, $length); + if ($length === 0) { + // If we wrote 0 bytes it was either EAGAIN or EPIPE. Since + // the pipe was seleted for writing, we assume it was EPIPE. + // There's no way to get the actual erorr code in PHP. See + // PHP Bug #39598. https://bugs.php.net/bug.php?id=39598 + $this->_debug('=> broken pipe on GPG command'); + $this->_debug('=> closing pipe GPG command'); + $this->_closePipe(self::FD_COMMAND); + } else { + $this->_debug('=> wrote ' . $length); + $this->_commandBuffer = Crypt_GPG_ByteUtils::substr( + $this->_commandBuffer, + $length + ); + } + } - $this->_debug('=> wrote ' . $length); + if (count($outputStreams) === 0 || count($inputStreams) === 0) { + // we have an I/O imbalance, increase the select loop delay + // to smooth things out + $delay += 10; + } else { + // things are running smoothly, decrease the delay + $delay -= 8; + $delay = max(0, $delay); + } - $this->_commandBuffer = self::_byteSubstring( - $this->_commandBuffer, - $length - ); + if ($delay > 0) { + usleep($delay); } } // end loop while streams are open @@ -1449,12 +1592,83 @@ class Crypt_GPG_Engine { $version = $this->getVersion(); + // 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'; + $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'; + // If using GnuPG 2.x start the gpg-agent + if (version_compare($version, '2.0.0', 'ge')) { + $agentCommandLine = $this->_agent; + + $agentArguments = array( + '--options /dev/null', // ignore any saved options + '--csh', // output is easier to parse + '--keep-display', // prevent passing --display to pinentry + '--no-grab', + '--ignore-cache-for-signing', + '--pinentry-touch-file /dev/null', + '--disable-scdaemon', + '--no-use-standard-socket', + '--pinentry-program ' . escapeshellarg($this->_getPinEntry()) + ); + + if ($this->_homedir) { + $agentArguments[] = '--homedir ' . + escapeshellarg($this->_homedir); + } + + + $agentCommandLine .= ' ' . implode(' ', $agentArguments) + . ' --daemon'; + + $agentDescriptorSpec = array( + self::FD_INPUT => array('pipe', $rb), // stdin + self::FD_OUTPUT => array('pipe', $wb), // stdout + self::FD_ERROR => array('pipe', $wb) // stderr + ); + + $this->_debug('OPENING GPG-AGENT SUBPROCESS WITH THE FOLLOWING COMMAND:'); + $this->_debug($agentCommandLine); + + $this->_agentProcess = proc_open( + $agentCommandLine, + $agentDescriptorSpec, + $this->_agentPipes, + null, + $env, + array('binary_pipes' => true) + ); + + if (!is_resource($this->_agentProcess)) { + throw new Crypt_GPG_OpenSubprocessException( + 'Unable to open gpg-agent subprocess.', + 0, + $agentCommandLine + ); + } + + // Get GPG_AGENT_INFO and set environment variable for gpg process. + // This is a blocking read, but is only 1 line. + $agentInfo = fread( + $this->_agentPipes[self::FD_OUTPUT], + self::CHUNK_SIZE + ); + + $agentInfo = explode(' ', $agentInfo, 3); + $this->_agentInfo = $agentInfo[2]; + $env['GPG_AGENT_INFO'] = $this->_agentInfo; + + // gpg-agent daemon is started, we can close the launching process + $this->_closeAgentLaunchProcess(); + } + $commandLine = $this->_binary; $defaultArguments = array( @@ -1511,11 +1725,6 @@ class Crypt_GPG_Engine $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 @@ -1525,7 +1734,7 @@ class Crypt_GPG_Engine self::FD_MESSAGE => array('pipe', $rb) // message ); - $this->_debug('OPENING SUBPROCESS WITH THE FOLLOWING COMMAND:'); + $this->_debug('OPENING GPG SUBPROCESS WITH THE FOLLOWING COMMAND:'); $this->_debug($commandLine); $this->_process = proc_open( @@ -1542,6 +1751,11 @@ class Crypt_GPG_Engine 'Unable to open GPG subprocess.', 0, $commandLine); } + // Set streams as non-blocking. See Bug #18618. + foreach ($this->_pipes as $pipe) { + stream_set_blocking($pipe, 0); + } + $this->_openPipes = $this->_pipes; $this->_errorCode = Crypt_GPG::ERROR_NONE; } @@ -1562,8 +1776,11 @@ class Crypt_GPG_Engine */ private function _closeSubprocess() { + // clear PINs from environment if they were set + $_ENV['PINENTRY_USER_DATA'] = null; + if (is_resource($this->_process)) { - $this->_debug('CLOSING SUBPROCESS'); + $this->_debug('CLOSING GPG SUBPROCESS'); // close remaining open pipes foreach (array_keys($this->_openPipes) as $pipeNumber) { @@ -1590,9 +1807,55 @@ class Crypt_GPG_Engine $this->_process = null; $this->_pipes = array(); } + + $this->_closeAgentLaunchProcess(); + + if ($this->_agentInfo !== null) { + $this->_debug('STOPPING GPG-AGENT DAEMON'); + + $parts = explode(':', $this->_agentInfo, 3); + $pid = $parts[1]; + $process = new Crypt_GPG_ProcessControl($pid); + + // terminate agent daemon + $process->terminate(); + + while ($process->isRunning()) { + usleep(10000); // 10 ms + $process->terminate(); + } + + $this->_agentInfo = null; + + $this->_debug('GPG-AGENT DAEMON STOPPED'); + } } // }}} + // {{ _closeAgentLaunchProcess() + + private function _closeAgentLaunchProcess() + { + if (is_resource($this->_agentProcess)) { + $this->_debug('CLOSING GPG-AGENT LAUNCH PROCESS'); + + // close agent pipes + foreach ($this->_agentPipes as $pipe) { + fflush($pipe); + fclose($pipe); + } + + // close agent launching process + proc_close($this->_agentProcess); + + $this->_agentProcess = null; + $this->_agentPipes = array(); + + $this->_debug('GPG-AGENT LAUNCH PROCESS CLOSED'); + } + } + + // }} // {{{ _closePipe() /** @@ -1658,6 +1921,55 @@ class Crypt_GPG_Engine } // }}} + // {{ _getAgent() + + private function _getAgent() + { + $agent = ''; + + if ($this->_isDarwin) { + $agentFiles = array( + '/opt/local/bin/gpg-agent', // MacPorts + '/usr/local/bin/gpg-agent', // Mac GPG + '/sw/bin/gpg-agent', // Fink + '/usr/bin/gpg-agent' + ); + } else { + $agentFiles = array( + '/usr/bin/gpg-agent', + '/usr/local/bin/gpg-agent' + ); + } + + foreach ($agentFiles as $agentFile) { + if (is_executable($agentFile)) { + $agent = $agentFile; + break; + } + } + + return $agent; + } + + // }} + // {{ _getPinEntry() + + private function _getPinEntry() + { + // Check if we're running directly from git or if we're using a + // PEAR-packaged version + $pinEntry = '@bin-dir@' . DIRECTORY_SEPARATOR . 'crypt-gpg-pinentry'; + + if ($pinEntry[0] === '@') { + $pinEntry = dirname(__FILE__) . DIRECTORY_SEPARATOR . '..' + . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'scripts' + . DIRECTORY_SEPARATOR . 'crypt-gpg-pinentry'; + } + + return $pinEntry; + } + + // }} // {{{ _debug() /** @@ -1672,7 +1984,7 @@ class Crypt_GPG_Engine private function _debug($text) { if ($this->_debug) { - if (array_key_exists('SHELL', $_ENV)) { + if (php_sapi_name() === 'cli') { foreach (explode(PHP_EOL, $text) as $line) { echo "Crypt_GPG DEBUG: ", $line, PHP_EOL; } @@ -1687,70 +1999,6 @@ class Crypt_GPG_Engine } // }}} - // {{{ _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); - } - - // }}} } // }}} |