Crypt_GPG
[ class tree: Crypt_GPG ] [ index: Crypt_GPG ] [ all elements ]

Source for file Engine.php

Documentation is available at Engine.php

  1. <?php
  2.  
  3. /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
  4.  
  5. /**
  6.  * Crypt_GPG is a package to use GPG from PHP
  7.  *
  8.  * This file contains an engine that handles GPG subprocess control and I/O.
  9.  * PHP's process manipulation functions are used to handle the GPG subprocess.
  10.  *
  11.  * PHP version 5
  12.  *
  13.  * LICENSE:
  14.  *
  15.  * This library is free software; you can redistribute it and/or modify
  16.  * it under the terms of the GNU Lesser General Public License as
  17.  * published by the Free Software Foundation; either version 2.1 of the
  18.  * License, or (at your option) any later version.
  19.  *
  20.  * This library is distributed in the hope that it will be useful,
  21.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  22.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  23.  * Lesser General Public License for more details.
  24.  *
  25.  * You should have received a copy of the GNU Lesser General Public
  26.  * License along with this library; if not, write to the Free Software
  27.  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  28.  *
  29.  * @category  Encryption
  30.  * @package   Crypt_GPG
  31.  * @author    Nathan Fredrickson <nathan@silverorange.com>
  32.  * @author    Michael Gauthier <mike@silverorange.com>
  33.  * @copyright 2005-2013 silverorange
  34.  * @license   http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
  35.  * @version   CVS: $Id$
  36.  * @link      http://pear.php.net/package/Crypt_GPG
  37.  * @link      http://www.gnupg.org/
  38.  */
  39.  
  40. /**
  41.  * Crypt_GPG base class.
  42.  */
  43. require_once 'Crypt/GPG.php';
  44.  
  45. /**
  46.  * GPG exception classes.
  47.  */
  48. require_once 'Crypt/GPG/Exceptions.php';
  49.  
  50. /**
  51.  * Byte string operations.
  52.  */
  53. require_once 'Crypt/GPG/ByteUtils.php';
  54.  
  55. /**
  56.  * Process control methods.
  57.  */
  58. require_once 'Crypt/GPG/ProcessControl.php';
  59.  
  60. /**
  61.  * Standard PEAR exception is used if GPG binary is not found.
  62.  */
  63. require_once 'PEAR/Exception.php';
  64.  
  65. // {{{ class Crypt_GPG_Engine
  66.  
  67. /**
  68.  * Native PHP Crypt_GPG I/O engine
  69.  *
  70.  * This class is used internally by Crypt_GPG and does not need be used
  71.  * directly. See the {@link Crypt_GPG} class for end-user API.
  72.  *
  73.  * This engine uses PHP's native process control functions to directly control
  74.  * the GPG process. The GPG executable is required to be on the system.
  75.  *
  76.  * All data is passed to the GPG subprocess using file descriptors. This is the
  77.  * most secure method of passing data to the GPG subprocess.
  78.  *
  79.  * @category  Encryption
  80.  * @package   Crypt_GPG
  81.  * @author    Nathan Fredrickson <nathan@silverorange.com>
  82.  * @author    Michael Gauthier <mike@silverorange.com>
  83.  * @copyright 2005-2013 silverorange
  84.  * @license   http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
  85.  * @link      http://pear.php.net/package/Crypt_GPG
  86.  * @link      http://www.gnupg.org/
  87.  */
  88. {
  89.     // {{{ constants
  90.  
  91.     /**
  92.      * Size of data chunks that are sent to and retrieved from the IPC pipes.
  93.      *
  94.      * PHP reads 8192 bytes. If this is set to less than 8192, PHP reads 8192
  95.      * and buffers the rest so we might as well just read 8192.
  96.      *
  97.      * Using values other than 8192 also triggers PHP bugs.
  98.      *
  99.      * @see http://bugs.php.net/bug.php?id=35224
  100.      */
  101.     const CHUNK_SIZE = 8192;
  102.  
  103.     /**
  104.      * Standard input file descriptor. This is used to pass data to the GPG
  105.      * process.
  106.      */
  107.     const FD_INPUT = 0;
  108.  
  109.     /**
  110.      * Standard output file descriptor. This is used to receive normal output
  111.      * from the GPG process.
  112.      */
  113.     const FD_OUTPUT = 1;
  114.  
  115.     /**
  116.      * Standard output file descriptor. This is used to receive error output
  117.      * from the GPG process.
  118.      */
  119.     const FD_ERROR = 2;
  120.  
  121.     /**
  122.      * GPG status output file descriptor. The status file descriptor outputs
  123.      * detailed information for many GPG commands. See the second section of
  124.      * the file <b>doc/DETAILS</b> in the
  125.      * {@link http://www.gnupg.org/download/ GPG package} for a detailed
  126.      * description of GPG's status output.
  127.      */
  128.     const FD_STATUS = 3;
  129.  
  130.     /**
  131.      * Command input file descriptor. This is used for methods requiring
  132.      * passphrases.
  133.      */
  134.     const FD_COMMAND = 4;
  135.  
  136.     /**
  137.      * Extra message input file descriptor. This is used for passing signed
  138.      * data when verifying a detached signature.
  139.      */
  140.     const FD_MESSAGE = 5;
  141.  
  142.     /**
  143.      * Minimum version of GnuPG that is supported.
  144.      */
  145.     const MIN_VERSION = '1.0.2';
  146.  
  147.     // }}}
  148.     // {{{ private class properties
  149.  
  150.     /**
  151.      * Whether or not to use debugging mode
  152.      *
  153.      * When set to true, every GPG command is echoed before it is run. Sensitive
  154.      * data is always handled using pipes and is not specified as part of the
  155.      * command. As a result, sensitive data is never displayed when debug is
  156.      * enabled. Sensitive data includes private key data and passphrases.
  157.      *
  158.      * Debugging is off by default.
  159.      *
  160.      * @var boolean 
  161.      * @see Crypt_GPG_Engine::__construct()
  162.      */
  163.     private $_debug = false;
  164.  
  165.     /**
  166.      * Location of GPG binary
  167.      *
  168.      * @var string 
  169.      * @see Crypt_GPG_Engine::__construct()
  170.      * @see Crypt_GPG_Engine::_getBinary()
  171.      */
  172.     private $_binary '';
  173.  
  174.     /**
  175.      * Location of GnuPG agent binary
  176.      *
  177.      * Only used for GnuPG 2.x
  178.      *
  179.      * @var string 
  180.      * @see Crypt_GPG_Engine::__construct()
  181.      * @see Crypt_GPG_Engine::_getAgent()
  182.      */
  183.     private $_agent '';
  184.  
  185.     /**
  186.      * Directory containing the GPG key files
  187.      *
  188.      * This property only contains the path when the <i>homedir</i> option
  189.      * is specified in the constructor.
  190.      *
  191.      * @var string 
  192.      * @see Crypt_GPG_Engine::__construct()
  193.      */
  194.     private $_homedir '';
  195.  
  196.     /**
  197.      * File path of the public keyring
  198.      *
  199.      * This property only contains the file path when the <i>public_keyring</i>
  200.      * option is specified in the constructor.
  201.      *
  202.      * If the specified file path starts with <kbd>~/</kbd>, the path is
  203.      * relative to the <i>homedir</i> if specified, otherwise to
  204.      * <kbd>~/.gnupg</kbd>.
  205.      *
  206.      * @var string 
  207.      * @see Crypt_GPG_Engine::__construct()
  208.      */
  209.     private $_publicKeyring '';
  210.  
  211.     /**
  212.      * File path of the private (secret) keyring
  213.      *
  214.      * This property only contains the file path when the <i>private_keyring</i>
  215.      * option is specified in the constructor.
  216.      *
  217.      * If the specified file path starts with <kbd>~/</kbd>, the path is
  218.      * relative to the <i>homedir</i> if specified, otherwise to
  219.      * <kbd>~/.gnupg</kbd>.
  220.      *
  221.      * @var string 
  222.      * @see Crypt_GPG_Engine::__construct()
  223.      */
  224.     private $_privateKeyring '';
  225.  
  226.     /**
  227.      * File path of the trust database
  228.      *
  229.      * This property only contains the file path when the <i>trust_db</i>
  230.      * option is specified in the constructor.
  231.      *
  232.      * If the specified file path starts with <kbd>~/</kbd>, the path is
  233.      * relative to the <i>homedir</i> if specified, otherwise to
  234.      * <kbd>~/.gnupg</kbd>.
  235.      *
  236.      * @var string 
  237.      * @see Crypt_GPG_Engine::__construct()
  238.      */
  239.     private $_trustDb '';
  240.  
  241.     /**
  242.      * Array of pipes used for communication with the GPG binary
  243.      *
  244.      * This is an array of file descriptor resources.
  245.      *
  246.      * @var array 
  247.      */
  248.     private $_pipes = array();
  249.  
  250.     /**
  251.      * Array of pipes used for communication with the gpg-agent binary
  252.      *
  253.      * This is an array of file descriptor resources.
  254.      *
  255.      * @var array 
  256.      */
  257.     private $_agentPipes = array();
  258.  
  259.     /**
  260.      * Array of currently opened pipes
  261.      *
  262.      * This array is used to keep track of remaining opened pipes so they can
  263.      * be closed when the GPG subprocess is finished. This array is a subset of
  264.      * the {@link Crypt_GPG_Engine::$_pipes} array and contains opened file
  265.      * descriptor resources.
  266.      *
  267.      * @var array 
  268.      * @see Crypt_GPG_Engine::_closePipe()
  269.      */
  270.     private $_openPipes = array();
  271.  
  272.     /**
  273.      * A handle for the GPG process
  274.      *
  275.      * @var resource 
  276.      */
  277.     private $_process = null;
  278.  
  279.     /**
  280.      * A handle for the gpg-agent process
  281.      *
  282.      * @var resource 
  283.      */
  284.     private $_agentProcess = null;
  285.  
  286.     /**
  287.      * GPG agent daemon socket and PID for running gpg-agent
  288.      *
  289.      * @var string 
  290.      */
  291.     private $_agentInfo = null;
  292.  
  293.     /**
  294.      * Whether or not the operating system is Darwin (OS X)
  295.      *
  296.      * @var boolean 
  297.      */
  298.     private $_isDarwin = false;
  299.  
  300.     /**
  301.      * Commands to be sent to GPG's command input stream
  302.      *
  303.      * @var string 
  304.      * @see Crypt_GPG_Engine::sendCommand()
  305.      */
  306.     private $_commandBuffer '';
  307.  
  308.     /**
  309.      * Array of status line handlers
  310.      *
  311.      * @var array 
  312.      * @see Crypt_GPG_Engine::addStatusHandler()
  313.      */
  314.     private $_statusHandlers = array();
  315.  
  316.     /**
  317.      * Array of error line handlers
  318.      *
  319.      * @var array 
  320.      * @see Crypt_GPG_Engine::addErrorHandler()
  321.      */
  322.     private $_errorHandlers = array();
  323.  
  324.     /**
  325.      * The error code of the current operation
  326.      *
  327.      * @var integer 
  328.      * @see Crypt_GPG_Engine::getErrorCode()
  329.      */
  330.     private $_errorCode = Crypt_GPG::ERROR_NONE;
  331.  
  332.     /**
  333.      * File related to the error code of the current operation
  334.      *
  335.      * @var string 
  336.      * @see Crypt_GPG_Engine::getErrorFilename()
  337.      */
  338.     private $_errorFilename '';
  339.  
  340.     /**
  341.      * Key id related to the error code of the current operation
  342.      *
  343.      * @var string 
  344.      * @see Crypt_GPG_Engine::getErrorKeyId()
  345.      */
  346.     private $_errorkeyId '';
  347.  
  348.     /**
  349.      * The number of currently needed passphrases
  350.      *
  351.      * If this is not zero when the GPG command is completed, the error code is
  352.      * set to {@link Crypt_GPG::ERROR_MISSING_PASSPHRASE}.
  353.      *
  354.      * @var integer 
  355.      */
  356.     private $_needPassphrase = 0;
  357.  
  358.     /**
  359.      * The input source
  360.      *
  361.      * This is data to send to GPG. Either a string or a stream resource.
  362.      *
  363.      * @var string|resource
  364.      * @see Crypt_GPG_Engine::setInput()
  365.      */
  366.     private $_input = null;
  367.  
  368.     /**
  369.      * The extra message input source
  370.      *
  371.      * Either a string or a stream resource.
  372.      *
  373.      * @var string|resource
  374.      * @see Crypt_GPG_Engine::setMessage()
  375.      */
  376.     private $_message = null;
  377.  
  378.     /**
  379.      * The output location
  380.      *
  381.      * This is where the output from GPG is sent. Either a string or a stream
  382.      * resource.
  383.      *
  384.      * @var string|resource
  385.      * @see Crypt_GPG_Engine::setOutput()
  386.      */
  387.     private $_output '';
  388.  
  389.     /**
  390.      * The GPG operation to execute
  391.      *
  392.      * @var string 
  393.      * @see Crypt_GPG_Engine::setOperation()
  394.      */
  395.     private $_operation;
  396.  
  397.     /**
  398.      * Arguments for the current operation
  399.      *
  400.      * @var array 
  401.      * @see Crypt_GPG_Engine::setOperation()
  402.      */
  403.     private $_arguments = array();
  404.  
  405.     /**
  406.      * The version number of the GPG binary
  407.      *
  408.      * @var string 
  409.      * @see Crypt_GPG_Engine::getVersion()
  410.      */
  411.     private $_version '';
  412.  
  413.     // }}}
  414.     // {{{ __construct()
  415.  
  416.     /**
  417.      * Creates a new GPG engine
  418.      *
  419.      * Available options are:
  420.      *
  421.      * - <kbd>string  homedir</kbd>        - the directory where the GPG
  422.      *                                       keyring files are stored. If not
  423.      *                                       specified, Crypt_GPG uses the
  424.      *                                       default of <kbd>~/.gnupg</kbd>.
  425.      * - <kbd>string  publicKeyring</kbd>  - the file path of the public
  426.      *                                       keyring. Use this if the public
  427.      *                                       keyring is not in the homedir, or
  428.      *                                       if the keyring is in a directory
  429.      *                                       not writable by the process
  430.      *                                       invoking GPG (like Apache). Then
  431.      *                                       you can specify the path to the
  432.      *                                       keyring with this option
  433.      *                                       (/foo/bar/pubring.gpg), and specify
  434.      *                                       a writable directory (like /tmp)
  435.      *                                       using the <i>homedir</i> option.
  436.      * - <kbd>string  privateKeyring</kbd> - the file path of the private
  437.      *                                       keyring. Use this if the private
  438.      *                                       keyring is not in the homedir, or
  439.      *                                       if the keyring is in a directory
  440.      *                                       not writable by the process
  441.      *                                       invoking GPG (like Apache). Then
  442.      *                                       you can specify the path to the
  443.      *                                       keyring with this option
  444.      *                                       (/foo/bar/secring.gpg), and specify
  445.      *                                       a writable directory (like /tmp)
  446.      *                                       using the <i>homedir</i> option.
  447.      * - <kbd>string  trustDb</kbd>        - the file path of the web-of-trust
  448.      *                                       database. Use this if the trust
  449.      *                                       database is not in the homedir, or
  450.      *                                       if the database is in a directory
  451.      *                                       not writable by the process
  452.      *                                       invoking GPG (like Apache). Then
  453.      *                                       you can specify the path to the
  454.      *                                       trust database with this option
  455.      *                                       (/foo/bar/trustdb.gpg), and specify
  456.      *                                       a writable directory (like /tmp)
  457.      *                                       using the <i>homedir</i> option.
  458.      * - <kbd>string  binary</kbd>         - the location of the GPG binary. If
  459.      *                                       not specified, the driver attempts
  460.      *                                       to auto-detect the GPG binary
  461.      *                                       location using a list of known
  462.      *                                       default locations for the current
  463.      *                                       operating system. The option
  464.      *                                       <kbd>gpgBinary</kbd> is a
  465.      *                                       deprecated alias for this option.
  466.      * - <kbd>string  agent</kbd>          - the location of the GnuPG agent
  467.      *                                       binary. The gpg-agent is only
  468.      *                                       used for GnuPG 2.x. If not
  469.      *                                       specified, the engine attempts
  470.      *                                       to auto-detect the gpg-agent
  471.      *                                       binary location using a list of
  472.      *                                       know default locations for the
  473.      *                                       current operating system.
  474.      * - <kbd>boolean debug</kbd>          - whether or not to use debug mode.
  475.      *                                       When debug mode is on, all
  476.      *                                       communication to and from the GPG
  477.      *                                       subprocess is logged. This can be
  478.      *                                       useful to diagnose errors when
  479.      *                                       using Crypt_GPG.
  480.      *
  481.      * @param array $options optional. An array of options used to create the
  482.      *                        GPG object. All options are optional and are
  483.      *                        represented as key-value pairs.
  484.      *
  485.      * @throws Crypt_GPG_FileException if the <kbd>homedir</kbd> does not exist
  486.      *          and cannot be created. This can happen if <kbd>homedir</kbd> is
  487.      *          not specified, Crypt_GPG is run as the web user, and the web
  488.      *          user has no home directory. This exception is also thrown if any
  489.      *          of the options <kbd>publicKeyring</kbd>,
  490.      *          <kbd>privateKeyring</kbd> or <kbd>trustDb</kbd> options are
  491.      *          specified but the files do not exist or are are not readable.
  492.      *          This can happen if the user running the Crypt_GPG process (for
  493.      *          example, the Apache user) does not have permission to read the
  494.      *          files.
  495.      *
  496.      * @throws PEAR_Exception if the provided <kbd>binary</kbd> is invalid, or
  497.      *          if no <kbd>binary</kbd> is provided and no suitable binary could
  498.      *          be found.
  499.      *
  500.      * @throws PEAR_Exception if the provided <kbd>agent</kbd> is invalid, or
  501.      *          if no <kbd>agent</kbd> is provided and no suitable gpg-agent
  502.      *          cound be found.
  503.      */
  504.     public function __construct(array $options = array())
  505.     {
  506.         $this->_isDarwin (strncmp(strtoupper(PHP_OS)'DARWIN'6=== 0);
  507.  
  508.         // get homedir
  509.         if (array_key_exists('homedir'$options)) {
  510.             $this->_homedir = (string)$options['homedir'];
  511.         else {
  512.             if (extension_loaded('posix')) {
  513.                 // note: this requires the package OS dep exclude 'windows'
  514.                 $info posix_getpwuid(posix_getuid());
  515.                 $this->_homedir $info['dir'].'/.gnupg';
  516.             else {
  517.                 if (isset($_SERVER['HOME'])) {
  518.                     $this->_homedir $_SERVER['HOME'];
  519.                 else {
  520.                     $this->_homedir getenv('HOME');
  521.                 }
  522.             }
  523.  
  524.             if ($this->_homedir === false{
  525.                 throw new Crypt_GPG_FileException(
  526.                     'Could not locate homedir. Please specify the homedir ' .
  527.                     'to use with the \'homedir\' option when instantiating ' .
  528.                     'the Crypt_GPG object.'
  529.                 );
  530.             }
  531.         }
  532.  
  533.         // attempt to create homedir if it does not exist
  534.         if (!is_dir($this->_homedir)) {
  535.             if (@mkdir($this->_homedir0777true)) {
  536.                 // Set permissions on homedir. Parent directories are created
  537.                 // with 0777, homedir is set to 0700.
  538.                 chmod($this->_homedir0700);
  539.             else {
  540.                 throw new Crypt_GPG_FileException(
  541.                     'The \'homedir\' "' $this->_homedir '" is not ' .
  542.                     'readable or does not exist and cannot be created. This ' .
  543.                     'can happen if \'homedir\' is not specified in the ' .
  544.                     'Crypt_GPG options, Crypt_GPG is run as the web user, ' .
  545.                     'and the web user has no home directory.',
  546.                     0,
  547.                     $this->_homedir
  548.                 );
  549.             }
  550.         }
  551.  
  552.         // check homedir permissions (See Bug #19833)
  553.         if (!is_executable($this->_homedir)) {
  554.             throw new Crypt_GPG_FileException(
  555.                 'The \'homedir\' "' $this->_homedir '" is not enterable ' .
  556.                 'by the current user. Please check the permissions on your ' .
  557.                 'homedir and make sure the current user can both enter and ' .
  558.                 'write to the directory.',
  559.                 0,
  560.                 $this->_homedir
  561.             );
  562.         }
  563.         if (!is_writeable($this->_homedir)) {
  564.             throw new Crypt_GPG_FileException(
  565.                 'The \'homedir\' "' $this->_homedir '" is not writable ' .
  566.                 'by the current user. Please check the permissions on your ' .
  567.                 'homedir and make sure the current user can both enter and ' .
  568.                 'write to the directory.',
  569.                 0,
  570.                 $this->_homedir
  571.             );
  572.         }
  573.  
  574.         // get binary
  575.         if (array_key_exists('binary'$options)) {
  576.             $this->_binary = (string)$options['binary'];
  577.         elseif (array_key_exists('gpgBinary'$options)) {
  578.             // deprecated alias
  579.             $this->_binary = (string)$options['gpgBinary'];
  580.         else {
  581.             $this->_binary $this->_getBinary();
  582.         }
  583.  
  584.         if ($this->_binary == '' || !is_executable($this->_binary)) {
  585.             throw new PEAR_Exception(
  586.                 'GPG binary not found. If you are sure the GPG binary is ' .
  587.                 'installed, please specify the location of the GPG binary ' .
  588.                 'using the \'binary\' driver option.'
  589.             );
  590.         }
  591.  
  592.         // get agent 
  593.         if (array_key_exists('agent'$options)) {
  594.             $this->_agent = (string)$options['agent'];
  595.         else {
  596.             $this->_agent $this->_getAgent();
  597.         }
  598.  
  599.         if ($this->_agent == '' || !is_executable($this->_agent)) {
  600.             throw new PEAR_Exception(
  601.                 'gpg-agent binary not found. If you are sure the gpg-agent ' .
  602.                 'is installed, please specify the location of the gpg-agent ' .
  603.                 'binary using the \'agent\' driver option.'
  604.             );
  605.         }
  606.  
  607.         /*
  608.          * Note:
  609.          *
  610.          * Normally, GnuPG expects keyrings to be in the homedir and expects
  611.          * to be able to write temporary files in the homedir. Sometimes,
  612.          * keyrings are not in the homedir, or location of the keyrings does
  613.          * not allow writing temporary files. In this case, the <i>homedir</i>
  614.          * option by itself is not enough to specify the keyrings because GnuPG
  615.          * can not write required temporary files. Additional options are
  616.          * provided so you can specify the location of the keyrings separately
  617.          * from the homedir.
  618.          */
  619.  
  620.         // get public keyring
  621.         if (array_key_exists('publicKeyring'$options)) {
  622.             $this->_publicKeyring = (string)$options['publicKeyring'];
  623.             if (!is_readable($this->_publicKeyring)) {
  624.                  throw new Crypt_GPG_FileException('The \'publicKeyring\' "' .
  625.                     $this->_publicKeyring '" does not exist or is ' .
  626.                     'not readable. Check the location and ensure the file ' .
  627.                     'permissions are correct.'0$this->_publicKeyring);
  628.             }
  629.         }
  630.  
  631.         // get private keyring
  632.         if (array_key_exists('privateKeyring'$options)) {
  633.             $this->_privateKeyring = (string)$options['privateKeyring'];
  634.             if (!is_readable($this->_privateKeyring)) {
  635.                  throw new Crypt_GPG_FileException('The \'privateKeyring\' "' .
  636.                     $this->_privateKeyring '" does not exist or is ' .
  637.                     'not readable. Check the location and ensure the file ' .
  638.                     'permissions are correct.'0$this->_privateKeyring);
  639.             }
  640.         }
  641.  
  642.         // get trust database
  643.         if (array_key_exists('trustDb'$options)) {
  644.             $this->_trustDb = (string)$options['trustDb'];
  645.             if (!is_readable($this->_trustDb)) {
  646.                  throw new Crypt_GPG_FileException('The \'trustDb\' "' .
  647.                     $this->_trustDb '" does not exist or is not readable. ' .
  648.                     'Check the location and ensure the file permissions are ' .
  649.                     'correct.'0$this->_trustDb);
  650.             }
  651.         }
  652.  
  653.         if (array_key_exists('debug'$options)) {
  654.             $this->_debug = (boolean)$options['debug'];
  655.         }
  656.     }
  657.  
  658.     // }}}
  659.     // {{{ __destruct()
  660.  
  661.     /**
  662.      * Closes open GPG subprocesses when this object is destroyed
  663.      *
  664.      * Subprocesses should never be left open by this class unless there is
  665.      * an unknown error and unexpected script termination occurs.
  666.      */
  667.     public function __destruct()
  668.     {
  669.         $this->_closeSubprocess();
  670.     }
  671.  
  672.     // }}}
  673.     // {{{ addErrorHandler()
  674.  
  675.     /**
  676.      * Adds an error handler method
  677.      *
  678.      * The method is run every time a new error line is received from the GPG
  679.      * subprocess. The handler method must accept the error line to be handled
  680.      * as its first parameter.
  681.      *
  682.      * @param callback $callback the callback method to use.
  683.      * @param array    $args     optional. Additional arguments to pass as
  684.      *                            parameters to the callback method.
  685.      *
  686.      * @return void 
  687.      */
  688.     public function addErrorHandler($callbackarray $args = array())
  689.     {
  690.         $this->_errorHandlers[= array(
  691.             'callback' => $callback,
  692.             'args'     => $args
  693.         );
  694.     }
  695.  
  696.     // }}}
  697.     // {{{ addStatusHandler()
  698.  
  699.     /**
  700.      * Adds a status handler method
  701.      *
  702.      * The method is run every time a new status line is received from the
  703.      * GPG subprocess. The handler method must accept the status line to be
  704.      * handled as its first parameter.
  705.      *
  706.      * @param callback $callback the callback method to use.
  707.      * @param array    $args     optional. Additional arguments to pass as
  708.      *                            parameters to the callback method.
  709.      *
  710.      * @return void 
  711.      */
  712.     public function addStatusHandler($callbackarray $args = array())
  713.     {
  714.         $this->_statusHandlers[= array(
  715.             'callback' => $callback,
  716.             'args'     => $args
  717.         );
  718.     }
  719.  
  720.     // }}}
  721.     // {{{ sendCommand()
  722.  
  723.     /**
  724.      * Sends a command to the GPG subprocess over the command file-descriptor
  725.      * pipe
  726.      *
  727.      * @param string $command the command to send.
  728.      *
  729.      * @return void 
  730.      *
  731.      * @sensitive $command
  732.      */
  733.     public function sendCommand($command)
  734.     {
  735.         if (array_key_exists(self::FD_COMMAND$this->_openPipes)) {
  736.             $this->_commandBuffer .= $command . PHP_EOL;
  737.         }
  738.     }
  739.  
  740.     // }}}
  741.     // {{{ reset()
  742.  
  743.     /**
  744.      * Resets the GPG engine, preparing it for a new operation
  745.      *
  746.      * @return void 
  747.      *
  748.      * @see Crypt_GPG_Engine::run()
  749.      * @see Crypt_GPG_Engine::setOperation()
  750.      */
  751.     public function reset()
  752.     {
  753.         $this->_operation      '';
  754.         $this->_arguments      = array();
  755.         $this->_input          = null;
  756.         $this->_message        = null;
  757.         $this->_output         '';
  758.         $this->_errorCode      Crypt_GPG::ERROR_NONE;
  759.         $this->_needPassphrase = 0;
  760.         $this->_commandBuffer  '';
  761.  
  762.         $this->_statusHandlers = array();
  763.         $this->_errorHandlers  = array();
  764.  
  765.         $this->addStatusHandler(array($this'_handleErrorStatus'));
  766.         $this->addErrorHandler(array($this'_handleErrorError'));
  767.  
  768.         if ($this->_debug{
  769.             $this->addStatusHandler(array($this'_handleDebugStatus'));
  770.             $this->addErrorHandler(array($this'_handleDebugError'));
  771.         }
  772.     }
  773.  
  774.     // }}}
  775.     // {{{ run()
  776.  
  777.     /**
  778.      * Runs the current GPG operation
  779.      *
  780.      * This creates and manages the GPG subprocess.
  781.      *
  782.      * The operation must be set with {@link Crypt_GPG_Engine::setOperation()}
  783.      * before this method is called.
  784.      *
  785.      * @return void 
  786.      *
  787.      * @throws Crypt_GPG_InvalidOperationException if no operation is specified.
  788.      *
  789.      * @see Crypt_GPG_Engine::reset()
  790.      * @see Crypt_GPG_Engine::setOperation()
  791.      */
  792.     public function run()
  793.     {
  794.         if ($this->_operation === ''{
  795.             throw new Crypt_GPG_InvalidOperationException('No GPG operation ' .
  796.                 'specified. Use Crypt_GPG_Engine::setOperation() before ' .
  797.                 'calling Crypt_GPG_Engine::run().');
  798.         }
  799.  
  800.         $this->_openSubprocess();
  801.         $this->_process();
  802.         $this->_closeSubprocess();
  803.     }
  804.  
  805.     // }}}
  806.     // {{{ getErrorCode()
  807.  
  808.     /**
  809.      * Gets the error code of the last executed operation
  810.      *
  811.      * This value is only meaningful after {@link Crypt_GPG_Engine::run()} has
  812.      * been executed.
  813.      *
  814.      * @return integer the error code of the last executed operation.
  815.      */
  816.     public function getErrorCode()
  817.     {
  818.         return $this->_errorCode;
  819.     }
  820.  
  821.     // }}}
  822.     // {{{ getErrorFilename()
  823.  
  824.     /**
  825.      * Gets the file related to the error code of the last executed operation
  826.      *
  827.      * This value is only meaningful after {@link Crypt_GPG_Engine::run()} has
  828.      * been executed. If there is no file related to the error, an empty string
  829.      * is returned.
  830.      *
  831.      * @return string the file related to the error code of the last executed
  832.      *                 operation.
  833.      */
  834.     public function getErrorFilename()
  835.     {
  836.         return $this->_errorFilename;
  837.     }
  838.  
  839.     // }}}
  840.     // {{{ getErrorKeyId()
  841.  
  842.     /**
  843.      * Gets the key id related to the error code of the last executed operation
  844.      *
  845.      * This value is only meaningful after {@link Crypt_GPG_Engine::run()} has
  846.      * been executed. If there is no key id related to the error, an empty
  847.      * string is returned.
  848.      *
  849.      * @return string the key id related to the error code of the last executed
  850.      *                 operation.
  851.      */
  852.     public function getErrorKeyId()
  853.     {
  854.         return $this->_errorKeyId;
  855.     }
  856.  
  857.     // }}}
  858.     // {{{ setInput()
  859.  
  860.     /**
  861.      * Sets the input source for the current GPG operation
  862.      *
  863.      * @param string|resource&$input either a reference to the string
  864.      *                                 containing the input data or an open
  865.      *                                 stream resource containing the input
  866.      *                                 data.
  867.      *
  868.      * @return void 
  869.      */
  870.     public function setInput(&$input)
  871.     {
  872.         $this->_input =$input;
  873.     }
  874.  
  875.     // }}}
  876.     // {{{ setMessage()
  877.  
  878.     /**
  879.      * Sets the message source for the current GPG operation
  880.      *
  881.      * Detached signature data should be specified here.
  882.      *
  883.      * @param string|resource&$message either a reference to the string
  884.      *                                   containing the message data or an open
  885.      *                                   stream resource containing the message
  886.      *                                   data.
  887.      *
  888.      * @return void 
  889.      */
  890.     public function setMessage(&$message)
  891.     {
  892.         $this->_message =$message;
  893.     }
  894.  
  895.     // }}}
  896.     // {{{ setOutput()
  897.  
  898.     /**
  899.      * Sets the output destination for the current GPG operation
  900.      *
  901.      * @param string|resource&$output either a reference to the string in
  902.      *                                  which to store GPG output or an open
  903.      *                                  stream resource to which the output data
  904.      *                                  should be written.
  905.      *
  906.      * @return void 
  907.      */
  908.     public function setOutput(&$output)
  909.     {
  910.         $this->_output =$output;
  911.     }
  912.  
  913.     // }}}
  914.     // {{{ setOperation()
  915.  
  916.     /**
  917.      * Sets the operation to perform
  918.      *
  919.      * @param string $operation the operation to perform. This should be one
  920.      *                           of GPG's operations. For example,
  921.      *                           <kbd>--encrypt</kbd>, <kbd>--decrypt</kbd>,
  922.      *                           <kbd>--sign</kbd>, etc.
  923.      * @param array  $arguments optional. Additional arguments for the GPG
  924.      *                           subprocess. See the GPG manual for specific
  925.      *                           values.
  926.      *
  927.      * @return void 
  928.      *
  929.      * @see Crypt_GPG_Engine::reset()
  930.      * @see Crypt_GPG_Engine::run()
  931.      */
  932.     public function setOperation($operationarray $arguments = array())
  933.     {
  934.         $this->_operation $operation;
  935.         $this->_arguments $arguments;
  936.     }
  937.  
  938.     // }}}
  939.     // {{{ getVersion()
  940.  
  941.     /**
  942.      * Gets the version of the GnuPG binary
  943.      *
  944.      * @return string a version number string containing the version of GnuPG
  945.      *                 being used. This value is suitable to use with PHP's
  946.      *                 version_compare() function.
  947.      *
  948.      * @throws Crypt_GPG_Exception if an unknown or unexpected error occurs.
  949.      *          Use the <kbd>debug</kbd> option and file a bug report if these
  950.      *          exceptions occur.
  951.      *
  952.      * @throws Crypt_GPG_UnsupportedException if the provided binary is not
  953.      *          GnuPG or if the GnuPG version is less than 1.0.2.
  954.      */
  955.     public function getVersion()
  956.     {
  957.         if ($this->_version == ''{
  958.  
  959.             $options = array(
  960.                 'homedir' => $this->_homedir,
  961.                 'binary'  => $this->_binary,
  962.                 'debug'   => $this->_debug
  963.             );
  964.  
  965.             $engine = new self($options);
  966.             $info   '';
  967.  
  968.             // Set a garbage version so we do not end up looking up the version
  969.             // recursively.
  970.             $engine->_version = '1.0.0';
  971.  
  972.             $engine->reset();
  973.             $engine->setOutput($info);
  974.             $engine->setOperation('--version');
  975.             $engine->run();
  976.  
  977.             $code $this->getErrorCode();
  978.  
  979.             if ($code !== Crypt_GPG::ERROR_NONE{
  980.                 throw new Crypt_GPG_Exception(
  981.                     'Unknown error getting GnuPG version information. Please ' .
  982.                     'use the \'debug\' option when creating the Crypt_GPG ' .
  983.                     'object, and file a bug report at ' Crypt_GPG::BUG_URI,
  984.                     $code);
  985.             }
  986.  
  987.             $matches    = array();
  988.             $expression '#gpg \(GnuPG[A-Za-z0-9/]*?\) (\S+)#';
  989.  
  990.             if (preg_match($expression$info$matches=== 1{
  991.                 $this->_version $matches[1];
  992.             else {
  993.                 throw new Crypt_GPG_Exception(
  994.                     'No GnuPG version information provided by the binary "' .
  995.                     $this->_binary '". Are you sure it is GnuPG?');
  996.             }
  997.  
  998.             if (version_compare($this->_versionself::MIN_VERSION'lt')) {
  999.                 throw new Crypt_GPG_Exception(
  1000.                     'The version of GnuPG being used (' $this->_version .
  1001.                     ') is not supported by Crypt_GPG. The minimum version ' .
  1002.                     'required by Crypt_GPG is ' . self::MIN_VERSION);
  1003.             }
  1004.         }
  1005.  
  1006.  
  1007.         return $this->_version;
  1008.     }
  1009.  
  1010.     // }}}
  1011.     // {{{ _handleErrorStatus()
  1012.  
  1013.     /**
  1014.      * Handles error values in the status output from GPG
  1015.      *
  1016.      * This method is responsible for setting the
  1017.      * {@link Crypt_GPG_Engine::$_errorCode}. See <b>doc/DETAILS</b> in the
  1018.      * {@link http://www.gnupg.org/download/ GPG distribution} for detailed
  1019.      * information on GPG's status output.
  1020.      *
  1021.      * @param string $line the status line to handle.
  1022.      *
  1023.      * @return void 
  1024.      */
  1025.     private function _handleErrorStatus($line)
  1026.     {
  1027.         $tokens explode(' '$line);
  1028.         switch ($tokens[0]{
  1029.         case 'BAD_PASSPHRASE':
  1030.             $this->_errorCode Crypt_GPG::ERROR_BAD_PASSPHRASE;
  1031.             break;
  1032.  
  1033.         case 'MISSING_PASSPHRASE':
  1034.             $this->_errorCode Crypt_GPG::ERROR_MISSING_PASSPHRASE;
  1035.             break;
  1036.  
  1037.         case 'NODATA':
  1038.             $this->_errorCode Crypt_GPG::ERROR_NO_DATA;
  1039.             break;
  1040.  
  1041.         case 'DELETE_PROBLEM':
  1042.             if ($tokens[1== '1'{
  1043.                 $this->_errorCode Crypt_GPG::ERROR_KEY_NOT_FOUND;
  1044.                 break;
  1045.             elseif ($tokens[1== '2'{
  1046.                 $this->_errorCode Crypt_GPG::ERROR_DELETE_PRIVATE_KEY;
  1047.                 break;
  1048.             }
  1049.             break;
  1050.  
  1051.         case 'IMPORT_RES':
  1052.             if ($tokens[12> 0{
  1053.                 $this->_errorCode Crypt_GPG::ERROR_DUPLICATE_KEY;
  1054.             }
  1055.             break;
  1056.  
  1057.         case 'NO_PUBKEY':
  1058.         case 'NO_SECKEY':
  1059.             $this->_errorKeyId $tokens[1];
  1060.             $this->_errorCode  Crypt_GPG::ERROR_KEY_NOT_FOUND;
  1061.             break;
  1062.  
  1063.         case 'NEED_PASSPHRASE':
  1064.             $this->_needPassphrase++;
  1065.             break;
  1066.  
  1067.         case 'GOOD_PASSPHRASE':
  1068.             $this->_needPassphrase--;
  1069.             break;
  1070.  
  1071.         case 'EXPSIG':
  1072.         case 'EXPKEYSIG':
  1073.         case 'REVKEYSIG':
  1074.         case 'BADSIG':
  1075.             $this->_errorCode Crypt_GPG::ERROR_BAD_SIGNATURE;
  1076.             break;
  1077.  
  1078.         }
  1079.     }
  1080.  
  1081.     // }}}
  1082.     // {{{ _handleErrorError()
  1083.  
  1084.     /**
  1085.      * Handles error values in the error output from GPG
  1086.      *
  1087.      * This method is responsible for setting the
  1088.      * {@link Crypt_GPG_Engine::$_errorCode}.
  1089.      *
  1090.      * @param string $line the error line to handle.
  1091.      *
  1092.      * @return void 
  1093.      */
  1094.     private function _handleErrorError($line)
  1095.     {
  1096.         if ($this->_errorCode === Crypt_GPG::ERROR_NONE{
  1097.             $pattern '/no valid OpenPGP data found/';
  1098.             if (preg_match($pattern$line=== 1{
  1099.                 $this->_errorCode Crypt_GPG::ERROR_NO_DATA;
  1100.             }
  1101.         }
  1102.  
  1103.         if ($this->_errorCode === Crypt_GPG::ERROR_NONE{
  1104.             $pattern '/No secret key|secret key not available/';
  1105.             if (preg_match($pattern$line=== 1{
  1106.                 $this->_errorCode Crypt_GPG::ERROR_KEY_NOT_FOUND;
  1107.             }
  1108.         }
  1109.  
  1110.         if ($this->_errorCode === Crypt_GPG::ERROR_NONE{
  1111.             $pattern '/No public key|public key not found/';
  1112.             if (preg_match($pattern$line=== 1{
  1113.                 $this->_errorCode Crypt_GPG::ERROR_KEY_NOT_FOUND;
  1114.             }
  1115.         }
  1116.  
  1117.         if ($this->_errorCode === Crypt_GPG::ERROR_NONE{
  1118.             $matches = array();
  1119.             $pattern '/can\'t (?:access|open) `(.*?)\'/';
  1120.             if (preg_match($pattern$line$matches=== 1{
  1121.                 $this->_errorFilename $matches[1];
  1122.                 $this->_errorCode Crypt_GPG::ERROR_FILE_PERMISSIONS;
  1123.             }
  1124.         }
  1125.     }
  1126.  
  1127.     // }}}
  1128.     // {{{ _handleDebugStatus()
  1129.  
  1130.     /**
  1131.      * Displays debug output for status lines
  1132.      *
  1133.      * @param string $line the status line to handle.
  1134.      *
  1135.      * @return void 
  1136.      */
  1137.     private function _handleDebugStatus($line)
  1138.     {
  1139.         $this->_debug('STATUS: ' $line);
  1140.     }
  1141.  
  1142.     // }}}
  1143.     // {{{ _handleDebugError()
  1144.  
  1145.     /**
  1146.      * Displays debug output for error lines
  1147.      *
  1148.      * @param string $line the error line to handle.
  1149.      *
  1150.      * @return void 
  1151.      */
  1152.     private function _handleDebugError($line)
  1153.     {
  1154.         $this->_debug('ERROR: ' $line);
  1155.     }
  1156.  
  1157.     // }}}
  1158.     // {{{ _process()
  1159.  
  1160.     /**
  1161.      * Performs internal streaming operations for the subprocess using either
  1162.      * strings or streams as input / output points
  1163.      *
  1164.      * This is the main I/O loop for streaming to and from the GPG subprocess.
  1165.      *
  1166.      * The implementation of this method is verbose mainly for performance
  1167.      * reasons. Adding streams to a lookup array and looping the array inside
  1168.      * the main I/O loop would be siginficantly slower for large streams.
  1169.      *
  1170.      * @return void 
  1171.      *
  1172.      * @throws Crypt_GPG_Exception if there is an error selecting streams for
  1173.      *          reading or writing. If this occurs, please file a bug report at
  1174.      *          http://pear.php.net/bugs/report.php?package=Crypt_GPG.
  1175.      */
  1176.     private function _process()
  1177.     {
  1178.         $this->_debug('BEGIN PROCESSING');
  1179.  
  1180.         $this->_commandBuffer '';    // buffers input to GPG
  1181.         $messageBuffer        '';    // buffers input to GPG
  1182.         $inputBuffer          '';    // buffers input to GPG
  1183.         $outputBuffer         '';    // buffers output from GPG
  1184.         $statusBuffer         '';    // buffers output from GPG
  1185.         $errorBuffer          '';    // buffers output from GPG
  1186.         $inputComplete        = false; // input stream is completely buffered
  1187.         $messageComplete      = false; // message stream is completely buffered
  1188.  
  1189.         if (is_string($this->_input)) {
  1190.             $inputBuffer   $this->_input;
  1191.             $inputComplete = true;
  1192.         }
  1193.  
  1194.         if (is_string($this->_message)) {
  1195.             $messageBuffer   $this->_message;
  1196.             $messageComplete = true;
  1197.         }
  1198.  
  1199.         if (is_string($this->_output)) {
  1200.             $outputBuffer =$this->_output;
  1201.         }
  1202.  
  1203.         // convenience variables
  1204.         $fdInput   $this->_pipes[self::FD_INPUT];
  1205.         $fdOutput  $this->_pipes[self::FD_OUTPUT];
  1206.         $fdError   $this->_pipes[self::FD_ERROR];
  1207.         $fdStatus  $this->_pipes[self::FD_STATUS];
  1208.         $fdCommand $this->_pipes[self::FD_COMMAND];
  1209.         $fdMessage $this->_pipes[self::FD_MESSAGE];
  1210.  
  1211.         // select loop delay in milliseconds
  1212.         $delay = 0;
  1213.  
  1214.         while (true{
  1215.  
  1216.             $inputStreams     = array();
  1217.             $outputStreams    = array();
  1218.             $exceptionStreams = array();
  1219.  
  1220.             // set up input streams
  1221.             if (is_resource($this->_input&& !$inputComplete{
  1222.                 if (feof($this->_input)) {
  1223.                     $inputComplete = true;
  1224.                 else {
  1225.                     $inputStreams[$this->_input;
  1226.                 }
  1227.             }
  1228.  
  1229.             // close GPG input pipe if there is no more data
  1230.             if ($inputBuffer == '' && $inputComplete{
  1231.                 $this->_debug('=> closing GPG input pipe');
  1232.                 $this->_closePipe(self::FD_INPUT);
  1233.             }
  1234.  
  1235.             if (is_resource($this->_message&& !$messageComplete{
  1236.                 if (feof($this->_message)) {
  1237.                     $messageComplete = true;
  1238.                 else {
  1239.                     $inputStreams[$this->_message;
  1240.                 }
  1241.             }
  1242.  
  1243.             // close GPG message pipe if there is no more data
  1244.             if ($messageBuffer == '' && $messageComplete{
  1245.                 $this->_debug('=> closing GPG message pipe');
  1246.                 $this->_closePipe(self::FD_MESSAGE);
  1247.             }
  1248.  
  1249.             if (!feof($fdOutput)) {
  1250.                 $inputStreams[$fdOutput;
  1251.             }
  1252.  
  1253.             if (!feof($fdStatus)) {
  1254.                 $inputStreams[$fdStatus;
  1255.             }
  1256.  
  1257.             if (!feof($fdError)) {
  1258.                 $inputStreams[$fdError;
  1259.             }
  1260.  
  1261.             // set up output streams
  1262.             if ($outputBuffer != '' && is_resource($this->_output)) {
  1263.                 $outputStreams[$this->_output;
  1264.             }
  1265.  
  1266.             if ($this->_commandBuffer != '' && is_resource($fdCommand)) {
  1267.                 $outputStreams[$fdCommand;
  1268.             }
  1269.  
  1270.             if ($messageBuffer != '' && is_resource($fdMessage)) {
  1271.                 $outputStreams[$fdMessage;
  1272.             }
  1273.  
  1274.             if ($inputBuffer != '' && is_resource($fdInput)) {
  1275.                 $outputStreams[$fdInput;
  1276.             }
  1277.  
  1278.             // no streams left to read or write, we're all done
  1279.             if (count($inputStreams=== 0 && count($outputStreams=== 0{
  1280.                 break;
  1281.             }
  1282.  
  1283.             $this->_debug('selecting streams');
  1284.  
  1285.             $ready stream_select(
  1286.                 $inputStreams,
  1287.                 $outputStreams,
  1288.                 $exceptionStreams,
  1289.                 null
  1290.             );
  1291.  
  1292.             $this->_debug('=> got ' $ready);
  1293.  
  1294.             if ($ready === false{
  1295.                 throw new Crypt_GPG_Exception(
  1296.                     'Error selecting stream for communication with GPG ' .
  1297.                     'subprocess. Please file a bug report at: ' .
  1298.                     'http://pear.php.net/bugs/report.php?package=Crypt_GPG');
  1299.             }
  1300.  
  1301.             if ($ready === 0{
  1302.                 throw new Crypt_GPG_Exception(
  1303.                     'stream_select() returned 0. This can not happen! Please ' .
  1304.                     'file a bug report at: ' .
  1305.                     'http://pear.php.net/bugs/report.php?package=Crypt_GPG');
  1306.             }
  1307.  
  1308.             // write input (to GPG)
  1309.             if (in_array($fdInput$outputStreamstrue)) {
  1310.                 $this->_debug('GPG is ready for input');
  1311.  
  1312.                 $chunk Crypt_GPG_ByteUtils::substr(
  1313.                     $inputBuffer,
  1314.                     0,
  1315.                     self::CHUNK_SIZE
  1316.                 );
  1317.  
  1318.                 $length Crypt_GPG_ByteUtils::strlen($chunk);
  1319.  
  1320.                 $this->_debug(
  1321.                     '=> about to write ' $length ' bytes to GPG input'
  1322.                 );
  1323.  
  1324.                 $length fwrite($fdInput$chunk$length);
  1325.                 if ($length === 0{
  1326.                     // If we wrote 0 bytes it was either EAGAIN or EPIPE. Since
  1327.                     // the pipe was seleted for writing, we assume it was EPIPE.
  1328.                     // There's no way to get the actual erorr code in PHP. See
  1329.                     // PHP Bug #39598. https://bugs.php.net/bug.php?id=39598
  1330.                     $this->_debug('=> broken pipe on GPG input');
  1331.                     $this->_debug('=> closing pipe GPG input');
  1332.                     $this->_closePipe(self::FD_INPUT);
  1333.                 else {
  1334.                     $this->_debug('=> wrote ' $length ' bytes');
  1335.                     $inputBuffer Crypt_GPG_ByteUtils::substr(
  1336.                         $inputBuffer,
  1337.                         $length
  1338.                     );
  1339.                 }
  1340.             }
  1341.  
  1342.             // read input (from PHP stream)
  1343.             if (in_array($this->_input$inputStreamstrue)) {
  1344.                 $this->_debug('input stream is ready for reading');
  1345.                 $this->_debug(
  1346.                     '=> about to read ' . self::CHUNK_SIZE .
  1347.                     ' bytes from input stream'
  1348.                 );
  1349.  
  1350.                 $chunk        fread($this->_inputself::CHUNK_SIZE);
  1351.                 $length       Crypt_GPG_ByteUtils::strlen($chunk);
  1352.                 $inputBuffer .= $chunk;
  1353.  
  1354.                 $this->_debug('=> read ' $length ' bytes');
  1355.             }
  1356.  
  1357.             // write message (to GPG)
  1358.             if (in_array($fdMessage$outputStreamstrue)) {
  1359.                 $this->_debug('GPG is ready for message data');
  1360.  
  1361.                 $chunk Crypt_GPG_ByteUtils::substr(
  1362.                     $messageBuffer,
  1363.                     0,
  1364.                     self::CHUNK_SIZE
  1365.                 );
  1366.  
  1367.                 $length Crypt_GPG_ByteUtils::strlen($chunk);
  1368.  
  1369.                 $this->_debug(
  1370.                     '=> about to write ' $length ' bytes to GPG message'
  1371.                 );
  1372.  
  1373.                 $length fwrite($fdMessage$chunk$length);
  1374.                 if ($length === 0{
  1375.                     // If we wrote 0 bytes it was either EAGAIN or EPIPE. Since
  1376.                     // the pipe was seleted for writing, we assume it was EPIPE.
  1377.                     // There's no way to get the actual erorr code in PHP. See
  1378.                     // PHP Bug #39598. https://bugs.php.net/bug.php?id=39598
  1379.                     $this->_debug('=> broken pipe on GPG message');
  1380.                     $this->_debug('=> closing pipe GPG message');
  1381.                     $this->_closePipe(self::FD_MESSAGE);
  1382.                 else {
  1383.                     $this->_debug('=> wrote ' $length ' bytes');
  1384.                     $messageBuffer Crypt_GPG_ByteUtils::substr(
  1385.                         $messageBuffer,
  1386.                         $length
  1387.                     );
  1388.                 }
  1389.             }
  1390.  
  1391.             // read message (from PHP stream)
  1392.             if (in_array($this->_message$inputStreamstrue)) {
  1393.                 $this->_debug('message stream is ready for reading');
  1394.                 $this->_debug(
  1395.                     '=> about to read ' . self::CHUNK_SIZE .
  1396.                     ' bytes from message stream'
  1397.                 );
  1398.  
  1399.                 $chunk          fread($this->_messageself::CHUNK_SIZE);
  1400.                 $length         Crypt_GPG_ByteUtils::strlen($chunk);
  1401.                 $messageBuffer .= $chunk;
  1402.  
  1403.                 $this->_debug('=> read ' $length ' bytes');
  1404.             }
  1405.  
  1406.             // read output (from GPG)
  1407.             if (in_array($fdOutput$inputStreamstrue)) {
  1408.                 $this->_debug('GPG output stream ready for reading');
  1409.                 $this->_debug(
  1410.                     '=> about to read ' . self::CHUNK_SIZE .
  1411.                     ' bytes from GPG output'
  1412.                 );
  1413.  
  1414.                 $chunk         fread($fdOutputself::CHUNK_SIZE);
  1415.                 $length        Crypt_GPG_ByteUtils::strlen($chunk);
  1416.                 $outputBuffer .= $chunk;
  1417.  
  1418.                 $this->_debug('=> read ' $length ' bytes');
  1419.             }
  1420.  
  1421.             // write output (to PHP stream)
  1422.             if (in_array($this->_output$outputStreamstrue)) {
  1423.                 $this->_debug('output stream is ready for data');
  1424.  
  1425.                 $chunk Crypt_GPG_ByteUtils::substr(
  1426.                     $outputBuffer,
  1427.                     0,
  1428.                     self::CHUNK_SIZE
  1429.                 );
  1430.  
  1431.                 $length Crypt_GPG_ByteUtils::strlen($chunk);
  1432.  
  1433.                 $this->_debug(
  1434.                     '=> about to write ' $length ' bytes to output stream'
  1435.                 );
  1436.  
  1437.                 $length fwrite($this->_output$chunk$length);
  1438.  
  1439.                 $this->_debug('=> wrote ' $length ' bytes');
  1440.  
  1441.                 $outputBuffer Crypt_GPG_ByteUtils::substr(
  1442.                     $outputBuffer,
  1443.                     $length
  1444.                 );
  1445.             }
  1446.  
  1447.             // read error (from GPG)
  1448.             if (in_array($fdError$inputStreamstrue)) {
  1449.                 $this->_debug('GPG error stream ready for reading');
  1450.                 $this->_debug(
  1451.                     '=> about to read ' . self::CHUNK_SIZE .
  1452.                     ' bytes from GPG error'
  1453.                 );
  1454.  
  1455.                 $chunk        fread($fdErrorself::CHUNK_SIZE);
  1456.                 $length       Crypt_GPG_ByteUtils::strlen($chunk);
  1457.                 $errorBuffer .= $chunk;
  1458.  
  1459.                 $this->_debug('=> read ' $length ' bytes');
  1460.  
  1461.                 // pass lines to error handlers
  1462.                 while (($pos strpos($errorBufferPHP_EOL)) !== false{
  1463.                     $line Crypt_GPG_ByteUtils::substr($errorBuffer0$pos);
  1464.                     foreach ($this->_errorHandlers as $handler{
  1465.                         array_unshift($handler['args']$line);
  1466.                         call_user_func_array(
  1467.                             $handler['callback'],
  1468.                             $handler['args']
  1469.                         );
  1470.  
  1471.                         array_shift($handler['args']);
  1472.                     }
  1473.                     $errorBuffer Crypt_GPG_ByteUtils::substr(
  1474.                         $errorBuffer,
  1475.                         $pos Crypt_GPG_ByteUtils::strlen(PHP_EOL)
  1476.                     );
  1477.                 }
  1478.             }
  1479.  
  1480.             // read status (from GPG)
  1481.             if (in_array($fdStatus$inputStreamstrue)) {
  1482.                 $this->_debug('GPG status stream ready for reading');
  1483.                 $this->_debug(
  1484.                     '=> about to read ' . self::CHUNK_SIZE .
  1485.                     ' bytes from GPG status'
  1486.                 );
  1487.  
  1488.                 $chunk         fread($fdStatusself::CHUNK_SIZE);
  1489.                 $length        Crypt_GPG_ByteUtils::strlen($chunk);
  1490.                 $statusBuffer .= $chunk;
  1491.  
  1492.                 $this->_debug('=> read ' $length ' bytes');
  1493.  
  1494.                 // pass lines to status handlers
  1495.                 while (($pos strpos($statusBufferPHP_EOL)) !== false{
  1496.                     $line Crypt_GPG_ByteUtils::substr($statusBuffer0$pos);
  1497.                     // only pass lines beginning with magic prefix
  1498.                     if (Crypt_GPG_ByteUtils::substr($line09== '[GNUPG:] '{
  1499.                         $line Crypt_GPG_ByteUtils::substr($line9);
  1500.                         foreach ($this->_statusHandlers as $handler{
  1501.                             array_unshift($handler['args']$line);
  1502.                             call_user_func_array(
  1503.                                 $handler['callback'],
  1504.                                 $handler['args']
  1505.                             );
  1506.  
  1507.                             array_shift($handler['args']);
  1508.                         }
  1509.                     }
  1510.                     $statusBuffer Crypt_GPG_ByteUtils::substr(
  1511.                         $statusBuffer,
  1512.                         $pos Crypt_GPG_ByteUtils::strlen(PHP_EOL)
  1513.                     );
  1514.                 }
  1515.             }
  1516.  
  1517.             // write command (to GPG)
  1518.             if (in_array($fdCommand$outputStreamstrue)) {
  1519.                 $this->_debug('GPG is ready for command data');
  1520.  
  1521.                 // send commands
  1522.                 $chunk Crypt_GPG_ByteUtils::substr(
  1523.                     $this->_commandBuffer,
  1524.                     0,
  1525.                     self::CHUNK_SIZE
  1526.                 );
  1527.  
  1528.                 $length Crypt_GPG_ByteUtils::strlen($chunk);
  1529.  
  1530.                 $this->_debug(
  1531.                     '=> about to write ' $length ' bytes to GPG command'
  1532.                 );
  1533.  
  1534.                 $length fwrite($fdCommand$chunk$length);
  1535.                 if ($length === 0{
  1536.                     // If we wrote 0 bytes it was either EAGAIN or EPIPE. Since
  1537.                     // the pipe was seleted for writing, we assume it was EPIPE.
  1538.                     // There's no way to get the actual erorr code in PHP. See
  1539.                     // PHP Bug #39598. https://bugs.php.net/bug.php?id=39598
  1540.                     $this->_debug('=> broken pipe on GPG command');
  1541.                     $this->_debug('=> closing pipe GPG command');
  1542.                     $this->_closePipe(self::FD_COMMAND);
  1543.                 else {
  1544.                     $this->_debug('=> wrote ' $length);
  1545.                     $this->_commandBuffer Crypt_GPG_ByteUtils::substr(
  1546.                         $this->_commandBuffer,
  1547.                         $length
  1548.                     );
  1549.                 }
  1550.             }
  1551.  
  1552.             if (count($outputStreams=== 0 || count($inputStreams=== 0{
  1553.                 // we have an I/O imbalance, increase the select loop delay
  1554.                 // to smooth things out
  1555.                 $delay += 10;
  1556.             else {
  1557.                 // things are running smoothly, decrease the delay
  1558.                 $delay -= 8;
  1559.                 $delay max(0$delay);
  1560.             }
  1561.  
  1562.             if ($delay > 0{
  1563.                 usleep($delay);
  1564.             }
  1565.  
  1566.         // end loop while streams are open
  1567.  
  1568.         $this->_debug('END PROCESSING');
  1569.     }
  1570.  
  1571.     // }}}
  1572.     // {{{ _openSubprocess()
  1573.  
  1574.     /**
  1575.      * Opens an internal GPG subprocess for the current operation
  1576.      *
  1577.      * Opens a GPG subprocess, then connects the subprocess to some pipes. Sets
  1578.      * the private class property {@link Crypt_GPG_Engine::$_process} to
  1579.      * the new subprocess.
  1580.      *
  1581.      * @return void 
  1582.      *
  1583.      * @throws Crypt_GPG_OpenSubprocessException if the subprocess could not be
  1584.      *          opened.
  1585.      *
  1586.      * @see Crypt_GPG_Engine::setOperation()
  1587.      * @see Crypt_GPG_Engine::_closeSubprocess()
  1588.      * @see Crypt_GPG_Engine::$_process
  1589.      */
  1590.     private function _openSubprocess()
  1591.     {
  1592.         $version $this->getVersion();
  1593.  
  1594.         // Binary operations will not work on Windows with PHP < 5.2.6. This is
  1595.         // in case stream_select() ever works on Windows.
  1596.         $rb (version_compare(PHP_VERSION'5.2.6'< 0'r' 'rb';
  1597.         $wb (version_compare(PHP_VERSION'5.2.6'< 0'w' 'wb';
  1598.  
  1599.         $env $_ENV;
  1600.  
  1601.         // Newer versions of GnuPG return localized results. Crypt_GPG only
  1602.         // works with English, so set the locale to 'C' for the subprocess.
  1603.         $env['LC_ALL''C';
  1604.  
  1605.         // If using GnuPG 2.x start the gpg-agent
  1606.         if (version_compare($version'2.0.0''ge')) {
  1607.             $agentCommandLine $this->_agent;
  1608.  
  1609.             $agentArguments = array(
  1610.                 '--options /dev/null'// ignore any saved options
  1611.                 '--csh'// output is easier to parse
  1612.                 '--keep-display'// prevent passing --display to pinentry
  1613.                 '--no-grab',
  1614.                 '--ignore-cache-for-signing',
  1615.                 '--pinentry-touch-file /dev/null',
  1616.                 '--disable-scdaemon',
  1617.                 '--no-use-standard-socket',
  1618.                 '--pinentry-program ' escapeshellarg($this->_getPinEntry())
  1619.             );
  1620.  
  1621.             if ($this->_homedir{
  1622.                 $agentArguments['--homedir ' .
  1623.                     escapeshellarg($this->_homedir);
  1624.             }
  1625.  
  1626.  
  1627.             $agentCommandLine .= ' ' implode(' '$agentArguments)
  1628.                 . ' --daemon';
  1629.  
  1630.             $agentDescriptorSpec = array(
  1631.                 self::FD_INPUT   => array('pipe'$rb)// stdin
  1632.                 self::FD_OUTPUT  => array('pipe'$wb)// stdout
  1633.                 self::FD_ERROR   => array('pipe'$wb)  // stderr
  1634.             );
  1635.  
  1636.             $this->_debug('OPENING GPG-AGENT SUBPROCESS WITH THE FOLLOWING COMMAND:');
  1637.             $this->_debug($agentCommandLine);
  1638.  
  1639.             $this->_agentProcess proc_open(
  1640.                 $agentCommandLine,
  1641.                 $agentDescriptorSpec,
  1642.                 $this->_agentPipes,
  1643.                 null,
  1644.                 $env,
  1645.                 array('binary_pipes' => true)
  1646.             );
  1647.  
  1648.             if (!is_resource($this->_agentProcess)) {
  1649.                 throw new Crypt_GPG_OpenSubprocessException(
  1650.                     'Unable to open gpg-agent subprocess.',
  1651.                     0,
  1652.                     $agentCommandLine
  1653.                 );
  1654.             }
  1655.  
  1656.             // Get GPG_AGENT_INFO and set environment variable for gpg process.
  1657.             // This is a blocking read, but is only 1 line.
  1658.             $agentInfo fread(
  1659.                 $this->_agentPipes[self::FD_OUTPUT],
  1660.                 self::CHUNK_SIZE
  1661.             );
  1662.  
  1663.             $agentInfo             explode(' '$agentInfo3);
  1664.             $this->_agentInfo      $agentInfo[2];
  1665.             $env['GPG_AGENT_INFO'$this->_agentInfo;
  1666.  
  1667.             // gpg-agent daemon is started, we can close the launching process
  1668.             $this->_closeAgentLaunchProcess();
  1669.         }
  1670.  
  1671.         $commandLine $this->_binary;
  1672.  
  1673.         $defaultArguments = array(
  1674.             '--status-fd ' escapeshellarg(self::FD_STATUS),
  1675.             '--command-fd ' escapeshellarg(self::FD_COMMAND),
  1676.             '--no-secmem-warning',
  1677.             '--no-tty',
  1678.             '--no-default-keyring'// ignored if keying files are not specified
  1679.             '--no-options'          // prevent creation of ~/.gnupg directory
  1680.         );
  1681.  
  1682.         if (version_compare($version'1.0.7''ge')) {
  1683.             if (version_compare($version'2.0.0''lt')) {
  1684.                 $defaultArguments['--no-use-agent';
  1685.             }
  1686.             $defaultArguments['--no-permission-warning';
  1687.         }
  1688.  
  1689.         if (version_compare($version'1.4.2''ge')) {
  1690.             $defaultArguments['--exit-on-status-write-error';
  1691.         }
  1692.  
  1693.         if (version_compare($version'1.3.2''ge')) {
  1694.             $defaultArguments['--trust-model always';
  1695.         else {
  1696.             $defaultArguments['--always-trust';
  1697.         }
  1698.  
  1699.         $arguments array_merge($defaultArguments$this->_arguments);
  1700.  
  1701.         if ($this->_homedir{
  1702.             $arguments['--homedir ' escapeshellarg($this->_homedir);
  1703.  
  1704.             // the random seed file makes subsequent actions faster so only
  1705.             // disable it if we have to.
  1706.             if (!is_writeable($this->_homedir)) {
  1707.                 $arguments['--no-random-seed-file';
  1708.             }
  1709.         }
  1710.  
  1711.         if ($this->_publicKeyring{
  1712.             $arguments['--keyring ' escapeshellarg($this->_publicKeyring);
  1713.         }
  1714.  
  1715.         if ($this->_privateKeyring{
  1716.             $arguments['--secret-keyring ' .
  1717.                 escapeshellarg($this->_privateKeyring);
  1718.         }
  1719.  
  1720.         if ($this->_trustDb{
  1721.             $arguments['--trustdb-name ' escapeshellarg($this->_trustDb);
  1722.         }
  1723.  
  1724.         $commandLine .= ' ' implode(' '$arguments' ' .
  1725.             $this->_operation;
  1726.  
  1727.         $descriptorSpec = array(
  1728.             self::FD_INPUT   => array('pipe'$rb)// stdin
  1729.             self::FD_OUTPUT  => array('pipe'$wb)// stdout
  1730.             self::FD_ERROR   => array('pipe'$wb)// stderr
  1731.             self::FD_STATUS  => array('pipe'$wb)// status
  1732.             self::FD_COMMAND => array('pipe'$rb)// command
  1733.             self::FD_MESSAGE => array('pipe'$rb)  // message
  1734.         );
  1735.  
  1736.         $this->_debug('OPENING GPG SUBPROCESS WITH THE FOLLOWING COMMAND:');
  1737.         $this->_debug($commandLine);
  1738.  
  1739.         $this->_process proc_open(
  1740.             $commandLine,
  1741.             $descriptorSpec,
  1742.             $this->_pipes,
  1743.             null,
  1744.             $env,
  1745.             array('binary_pipes' => true)
  1746.         );
  1747.  
  1748.         if (!is_resource($this->_process)) {
  1749.             throw new Crypt_GPG_OpenSubprocessException(
  1750.                 'Unable to open GPG subprocess.'0$commandLine);
  1751.         }
  1752.  
  1753.         // Set streams as non-blocking. See Bug #18618.
  1754.         foreach ($this->_pipes as $pipe{
  1755.             stream_set_blocking($pipe0);
  1756.         }
  1757.  
  1758.         $this->_openPipes $this->_pipes;
  1759.         $this->_errorCode Crypt_GPG::ERROR_NONE;
  1760.     }
  1761.  
  1762.     // }}}
  1763.     // {{{ _closeSubprocess()
  1764.  
  1765.     /**
  1766.      * Closes a the internal GPG subprocess
  1767.      *
  1768.      * Closes the internal GPG subprocess. Sets the private class property
  1769.      * {@link Crypt_GPG_Engine::$_process} to null.
  1770.      *
  1771.      * @return void 
  1772.      *
  1773.      * @see Crypt_GPG_Engine::_openSubprocess()
  1774.      * @see Crypt_GPG_Engine::$_process
  1775.      */
  1776.     private function _closeSubprocess()
  1777.     {
  1778.         // clear PINs from environment if they were set
  1779.         $_ENV['PINENTRY_USER_DATA'= null;
  1780.  
  1781.         if (is_resource($this->_process)) {
  1782.             $this->_debug('CLOSING GPG SUBPROCESS');
  1783.  
  1784.             // close remaining open pipes
  1785.             foreach (array_keys($this->_openPipesas $pipeNumber{
  1786.                 $this->_closePipe($pipeNumber);
  1787.             }
  1788.  
  1789.             $exitCode proc_close($this->_process);
  1790.  
  1791.             if ($exitCode != 0{
  1792.                 $this->_debug(
  1793.                     '=> subprocess returned an unexpected exit code: ' .
  1794.                     $exitCode
  1795.                 );
  1796.  
  1797.                 if ($this->_errorCode === Crypt_GPG::ERROR_NONE{
  1798.                     if ($this->_needPassphrase > 0{
  1799.                         $this->_errorCode Crypt_GPG::ERROR_MISSING_PASSPHRASE;
  1800.                     else {
  1801.                         $this->_errorCode Crypt_GPG::ERROR_UNKNOWN;
  1802.                     }
  1803.                 }
  1804.             }
  1805.  
  1806.             $this->_process = null;
  1807.             $this->_pipes   = array();
  1808.         }
  1809.  
  1810.         $this->_closeAgentLaunchProcess();
  1811.  
  1812.         if ($this->_agentInfo !== null{
  1813.             $this->_debug('STOPPING GPG-AGENT DAEMON');
  1814.  
  1815.             $parts   explode(':'$this->_agentInfo3);
  1816.             $pid     $parts[1];
  1817.             $process = new Crypt_GPG_ProcessControl($pid);
  1818.  
  1819.             // terminate agent daemon
  1820.             $process->terminate();
  1821.  
  1822.             while ($process->isRunning()) {
  1823.                 usleep(10000)// 10 ms
  1824.                 $process->terminate();
  1825.             }
  1826.  
  1827.             $this->_agentInfo = null;
  1828.  
  1829.             $this->_debug('GPG-AGENT DAEMON STOPPED');
  1830.         }
  1831.     }
  1832.  
  1833.     // }}}
  1834.     // {{ _closeAgentLaunchProcess()
  1835.  
  1836.     private function _closeAgentLaunchProcess()
  1837.     {
  1838.         if (is_resource($this->_agentProcess)) {
  1839.             $this->_debug('CLOSING GPG-AGENT LAUNCH PROCESS');
  1840.  
  1841.             // close agent pipes
  1842.             foreach ($this->_agentPipes as $pipe{
  1843.                 fflush($pipe);
  1844.                 fclose($pipe);
  1845.             }
  1846.  
  1847.             // close agent launching process
  1848.             proc_close($this->_agentProcess);
  1849.  
  1850.             $this->_agentProcess = null;
  1851.             $this->_agentPipes   = array();
  1852.  
  1853.             $this->_debug('GPG-AGENT LAUNCH PROCESS CLOSED');
  1854.         }
  1855.     }
  1856.  
  1857.     // }}
  1858.     // {{{ _closePipe()
  1859.  
  1860.     /**
  1861.      * Closes an opened pipe used to communicate with the GPG subprocess
  1862.      *
  1863.      * If the pipe is already closed, it is ignored. If the pipe is open, it
  1864.      * is flushed and then closed.
  1865.      *
  1866.      * @param integer $pipeNumber the file descriptor number of the pipe to
  1867.      *                             close.
  1868.      *
  1869.      * @return void 
  1870.      */
  1871.     private function _closePipe($pipeNumber)
  1872.     {
  1873.         $pipeNumber intval($pipeNumber);
  1874.         if (array_key_exists($pipeNumber$this->_openPipes)) {
  1875.             fflush($this->_openPipes[$pipeNumber]);
  1876.             fclose($this->_openPipes[$pipeNumber]);
  1877.             unset($this->_openPipes[$pipeNumber]);
  1878.         }
  1879.     }
  1880.  
  1881.     // }}}
  1882.     // {{{ _getBinary()
  1883.  
  1884.     /**
  1885.      * Gets the name of the GPG binary for the current operating system
  1886.      *
  1887.      * This method is called if the '<kbd>binary</kbd>' option is <i>not</i>
  1888.      * specified when creating this driver.
  1889.      *
  1890.      * @return string the name of the GPG binary for the current operating
  1891.      *                 system. If no suitable binary could be found, an empty
  1892.      *                 string is returned.
  1893.      */
  1894.     private function _getBinary()
  1895.     {
  1896.         $binary '';
  1897.  
  1898.         if ($this->_isDarwin{
  1899.             $binaryFiles = array(
  1900.                 '/opt/local/bin/gpg'// MacPorts
  1901.                 '/usr/local/bin/gpg'// Mac GPG
  1902.                 '/sw/bin/gpg',        // Fink
  1903.                 '/usr/bin/gpg'
  1904.             );
  1905.         else {
  1906.             $binaryFiles = array(
  1907.                 '/usr/bin/gpg',
  1908.                 '/usr/local/bin/gpg'
  1909.             );
  1910.         }
  1911.  
  1912.         foreach ($binaryFiles as $binaryFile{
  1913.             if (is_executable($binaryFile)) {
  1914.                 $binary $binaryFile;
  1915.                 break;
  1916.             }
  1917.         }
  1918.  
  1919.         return $binary;
  1920.     }
  1921.  
  1922.     // }}}
  1923.     // {{ _getAgent()
  1924.  
  1925.     private function _getAgent()
  1926.     {
  1927.         $agent '';
  1928.  
  1929.         if ($this->_isDarwin{
  1930.             $agentFiles = array(
  1931.                 '/opt/local/bin/gpg-agent'// MacPorts
  1932.                 '/usr/local/bin/gpg-agent'// Mac GPG
  1933.                 '/sw/bin/gpg-agent',        // Fink
  1934.                 '/usr/bin/gpg-agent'
  1935.             );
  1936.         else {
  1937.             $agentFiles = array(
  1938.                 '/usr/bin/gpg-agent',
  1939.                 '/usr/local/bin/gpg-agent'
  1940.             );
  1941.         }
  1942.  
  1943.         foreach ($agentFiles as $agentFile{
  1944.             if (is_executable($agentFile)) {
  1945.                 $agent $agentFile;
  1946.                 break;
  1947.             }
  1948.         }
  1949.  
  1950.         return $agent;
  1951.     }
  1952.  
  1953.     // }}
  1954.     // {{ _getPinEntry()
  1955.  
  1956.     private function _getPinEntry()
  1957.     {
  1958.         // Check if we're running directly from git or if we're using a
  1959.         // PEAR-packaged version
  1960.         $pinEntry '@bin-dir@' . DIRECTORY_SEPARATOR . 'crypt-gpg-pinentry';
  1961.  
  1962.         if ($pinEntry[0=== '@'{
  1963.             $pinEntry dirname(__FILE__. DIRECTORY_SEPARATOR . '..'
  1964.                 . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'scripts'
  1965.                 . DIRECTORY_SEPARATOR . 'crypt-gpg-pinentry';
  1966.         }
  1967.  
  1968.         return $pinEntry;
  1969.     }
  1970.  
  1971.     // }}
  1972.     // {{{ _debug()
  1973.  
  1974.     /**
  1975.      * Displays debug text if debugging is turned on
  1976.      *
  1977.      * Debugging text is prepended with a debug identifier and echoed to stdout.
  1978.      *
  1979.      * @param string $text the debugging text to display.
  1980.      *
  1981.      * @return void 
  1982.      */
  1983.     private function _debug($text)
  1984.     {
  1985.         if ($this->_debug{
  1986.             if (php_sapi_name(=== 'cli'{
  1987.                 foreach (explode(PHP_EOL$textas $line{
  1988.                     echo "Crypt_GPG DEBUG: "$linePHP_EOL;
  1989.                 }
  1990.             else {
  1991.                 // running on a web server, format debug output nicely
  1992.                 foreach (explode(PHP_EOL$textas $line{
  1993.                     echo "Crypt_GPG DEBUG: <strong>"$line,
  1994.                         '</strong><br />'PHP_EOL;
  1995.                 }
  1996.             }
  1997.         }
  1998.     }
  1999.  
  2000.     // }}}
  2001. }
  2002.  
  2003. // }}}
  2004.  
  2005. ?>

Documentation generated on Wed, 13 Mar 2013 18:30:06 +0000 by phpDocumentor 1.4.3. PEAR Logo Copyright © PHP Group 2004.