[ Index ]
 

Code source de Horde 3.1.3

Accédez au Source d'autres logiciels libresSoutenez Angelica Josefina !

title

Body

[fermer]

/lib/Horde/Crypt/ -> pgp.php (source)

   1  <?php
   2  
   3  require_once 'Horde/Crypt.php';
   4  
   5  /**
   6   * Armor Header Lines - From RFC 2440
   7   *
   8   *  An Armor Header Line consists of the appropriate header line text
   9   *  surrounded by five (5) dashes ('-', 0x2D) on either side of the header
  10   *  line text. The header line text is chosen based upon the type of data that
  11   *  is being encoded in Armor, and how it is being encoded. Header line texts
  12   *  include the following strings:
  13   *
  14   *  All Armor Header Lines are prefixed with 'PGP'.
  15   *
  16   *  The Armor Tail Line is composed in the same manner as the Armor Header
  17   *  Line, except the string "BEGIN" is replaced by the string "END."
  18   */
  19  
  20  /** Used for signed, encrypted, or compressed files. */
  21  define('PGP_ARMOR_MESSAGE', 1);
  22  
  23  /** Used for signed files. */
  24  define('PGP_ARMOR_SIGNED_MESSAGE', 2);
  25  
  26  /** Used for armoring public keys. */
  27  define('PGP_ARMOR_PUBLIC_KEY', 3);
  28  
  29  /** Used for armoring private keys. */
  30  define('PGP_ARMOR_PRIVATE_KEY', 4);
  31  
  32  /**
  33   * Used for detached signatures, PGP/MIME signatures, and natures following
  34   * clearsigned messages.
  35   */
  36  define('PGP_ARMOR_SIGNATURE', 5);
  37  
  38  /** Regular text contained in an PGP message. */
  39  define('PGP_ARMOR_TEXT', 6);
  40  
  41  
  42  /** The default public PGP keyserver to use. */
  43  define('PGP_KEYSERVER_PUBLIC', 'wwwkeys.pgp.net');
  44  
  45  /**
  46   * The number of times the keyserver refuses connection before an error is
  47   * returned.
  48   */
  49  define('PGP_KEYSERVER_REFUSE', 3);
  50  
  51  /**
  52   * The number of seconds that PHP will attempt to connect to the keyserver
  53   * before it will stop processing the request.
  54   */
  55  define('PGP_KEYSERVER_TIMEOUT', 10);
  56  
  57  
  58  /**
  59   * Horde_Crypt_pgp:: provides a framework for Horde applications to interact
  60   * with the GNU Privacy Guard program ("GnuPG").  GnuPG implements the OpenPGP
  61   * standard (RFC 2440).
  62   *
  63   * GnuPG Website: http://www.gnupg.org/
  64   *
  65   * This class has been developed with, and is only guaranteed to work with,
  66   * Version 1.21 or above of GnuPG.
  67   *
  68   * $Horde: framework/Crypt/Crypt/pgp.php,v 1.85.2.15 2006/07/20 09:02:38 jan Exp $
  69   *
  70   * Copyright 2002-2006 Michael Slusarz <slusarz@bigworm.colorado.edu>
  71   *
  72   * See the enclosed file COPYING for license information (LGPL). If you
  73   * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
  74   *
  75   * @author  Michael Slusarz <slusarz@bigworm.colorado.edu>
  76   * @since   Horde 3.0
  77   * @package Horde_Crypt
  78   */
  79  class Horde_Crypt_pgp extends Horde_Crypt {
  80  
  81      /**
  82       * Strings in armor header lines used to distinguish between the different
  83       * types of PGP decryption/encryption.
  84       *
  85       * @var array
  86       */
  87      var $_armor = array(
  88          'MESSAGE'            =>  PGP_ARMOR_MESSAGE,
  89          'SIGNED MESSAGE'     =>  PGP_ARMOR_SIGNED_MESSAGE,
  90          'PUBLIC KEY BLOCK'   =>  PGP_ARMOR_PUBLIC_KEY,
  91          'PRIVATE KEY BLOCK'  =>  PGP_ARMOR_PRIVATE_KEY,
  92          'SIGNATURE'          =>  PGP_ARMOR_SIGNATURE
  93      );
  94  
  95      /**
  96       * The list of PGP hash algorithms (from RFC 3156).
  97       *
  98       * @var array
  99       */
 100      var $_hashAlg = array(
 101          1  =>  'pgp-md5',
 102          2  =>  'pgp-sha1',
 103          3  =>  'pgp-ripemd160',
 104          5  =>  'pgp-md2',
 105          6  =>  'pgp-tiger192',
 106          7  =>  'pgp-haval-5-160',
 107          8  =>  'pgp-sha256',
 108          9  =>  'pgp-sha384',
 109          10 =>  'pgp-sha512',
 110      );
 111  
 112      /**
 113       * GnuPG program location/common options.
 114       *
 115       * @var array
 116       */
 117      var $_gnupg;
 118  
 119      /**
 120       * Filename of the temporary public keyring.
 121       *
 122       * @var string
 123       */
 124      var $_publicKeyring;
 125  
 126      /**
 127       * Filename of the temporary private keyring.
 128       *
 129       * @var string
 130       */
 131      var $_privateKeyring;
 132  
 133      /**
 134       * The existence of this property indicates that multiple recipient
 135       * encryption is available.
 136       *
 137       * @since Horde 3.1
 138       *
 139       * @deprecated
 140       *
 141       * @var boolean
 142       */
 143      var $multipleRecipientEncryption = true;
 144  
 145      /**
 146       * Constructor.
 147       *
 148       * @param array $params  Parameter array containing the path to the GnuPG
 149       *                       binary (key = 'program') and to a temporary
 150       *                       directory.
 151       */
 152      function Horde_Crypt_pgp($params = array())
 153      {
 154          $this->_tempdir = Util::createTempDir(true, $params['temp']);
 155  
 156          if (empty($params['program'])) {
 157              Horde::fatal(PEAR::raiseError(_("The location of the GnuPG binary must be given to the Crypt_pgp:: class.")), __FILE__, __LINE__);
 158          }
 159  
 160          /* Store the location of GnuPG and set common options. */
 161          $this->_gnupg = array(
 162              $params['program'],
 163              '--no-tty',
 164              '--no-secmem-warning',
 165              '--no-options',
 166              '--no-default-keyring',
 167              '--quiet',
 168              '--yes',
 169              '--homedir ' . $this->_tempdir
 170          );
 171  
 172          if (substr(PHP_OS, 0, 3) != 'WIN') {
 173              array_unshift($this->_gnupg, 'LANG= ;');
 174          }
 175      }
 176  
 177      /**
 178       * Generates a personal Public/Private keypair combination.
 179       *
 180       * @param string $realname    The name to use for the key.
 181       * @param string $email       The email to use for the key.
 182       * @param string $passphrase  The passphrase to use for the key.
 183       * @param string $comment     The comment to use for the key.
 184       * @param integer $keylength  The keylength to use for the key.
 185       *
 186       * @return array  An array consisting of the public key and the private
 187       *                key, or PEAR_Error on error.
 188       * <pre>
 189       * Return array:
 190       * Key            Value
 191       * --------------------------
 192       * 'public'   =>  Public Key
 193       * 'private'  =>  Private Key
 194       * </pre>
 195       */
 196      function generateKey($realname, $email, $passphrase, $comment = '',
 197                           $keylength = 1024)
 198      {
 199          /* Check for secure connection. */
 200          $secure_check = $this->requireSecureConnection();
 201          if (is_a($secure_check, 'PEAR_Error')) {
 202              return $secure_check;
 203          }
 204  
 205          /* Create temp files to hold the generated keys. */
 206          $pub_file = $this->_createTempFile('horde-pgp');
 207          $secret_file = $this->_createTempFile('horde-pgp');
 208  
 209          /* Create the config file necessary for GnuPG to run in batch mode. */
 210          /* TODO: Sanitize input, More user customizable? */
 211          $input = array();
 212          $input[] = '%pubring ' . $pub_file;
 213          $input[] = '%secring ' . $secret_file;
 214          $input[] = 'Key-Type: DSA';
 215          $input[] = 'Key-Length: 1024';
 216          $input[] = 'Subkey-Type: ELG-E';
 217          $input[] = 'Subkey-Length: ' . $keylength;
 218          $input[] = 'Name-Real: ' . $realname;
 219          if (!empty($comment)) {
 220              $input[] = 'Name-Comment: ' . $comment;
 221          }
 222          $input[] = 'Name-Email: ' . $email;
 223          $input[] = 'Expire-Date: 0';
 224          $input[] = 'Passphrase: ' . $passphrase;
 225          $input[] = '%commit';
 226  
 227          /* Run through gpg binary. */
 228          $cmdline = array(
 229              '--gen-key',
 230              '--batch',
 231              '--armor'
 232          );
 233          $this->_callGpg($cmdline, 'w', $input);
 234  
 235          /* Get the keys from the temp files. */
 236          $public_key = file($pub_file);
 237          $secret_key = file($secret_file);
 238  
 239          /* If either key is empty, something went wrong. */
 240          if (empty($public_key) || empty($secret_key)) {
 241              return PEAR::raiseError(_("Public/Private keypair not generated successfully."), 'horde.error');
 242          }
 243  
 244          return array('public' => $public_key, 'private' => $secret_key);
 245      }
 246  
 247      /**
 248       * Returns information on a PGP data block.
 249       *
 250       * @param string $pgpdata  The PGP data block.
 251       *
 252       * @return array  An array with information on the PGP data block. If an
 253       *                element is not present in the data block, it will
 254       *                likewise not be set in the array.
 255       * <pre>
 256       * Array Format:
 257       * -------------
 258       * [public_key]/[secret_key] => Array
 259       *   (
 260       *     [created] => Key creation - UNIX timestamp
 261       *     [expires] => Key expiration - UNIX timestamp (0 = never expires)
 262       *     [size]    => Size of the key in bits
 263       *   )
 264       *
 265       * [fingerprint] => Fingerprint of the PGP data (if available)
 266       *                  16-bit hex value
 267       *
 268       * [signature] => Array (
 269       *     [id{n}/'_SIGNATURE'] => Array (
 270       *         [name]        => Full Name
 271       *         [comment]     => Comment
 272       *         [email]       => E-mail Address
 273       *         [fingerprint] => 16-bit hex value
 274       *         [created]     => Signature creation - UNIX timestamp
 275       *         [micalg]      => The hash used to create the signature
 276       *         [sig_{hex}]   => Array [details of a sig verifying the ID] (
 277       *             [created]     => Signature creation - UNIX timestamp
 278       *             [fingerprint] => 16-bit hex value
 279       *             [micalg]      => The hash used to create the signature
 280       *         )
 281       *     )
 282       * )
 283       * </pre>
 284       *
 285       * Each user ID will be stored in the array 'signature' and have data
 286       * associated with it, including an array for information on each
 287       * signature that has signed that UID. Signatures not associated with a
 288       * UID (e.g. revocation signatures and sub keys) will be stored under the
 289       * special keyword '_SIGNATURE'.
 290       */
 291      function pgpPacketInformation($pgpdata)
 292      {
 293          $data_array = array();
 294          $fingerprint = '';
 295          $header = null;
 296          $input = $this->_createTempFile('horde-pgp');
 297          $sig_id = $uid_idx = 0;
 298  
 299          /* Store message in temporary file. */
 300          $fp = fopen($input, 'w+');
 301          fputs($fp, $pgpdata);
 302          fclose($fp);
 303  
 304          $cmdline = array(
 305              '--list-packets',
 306              $input
 307          );
 308          $result = $this->_callGpg($cmdline, 'r');
 309  
 310          foreach (explode("\n", $result->stdout) as $line) {
 311              /* Headers are prefaced with a ':' as the first character on the
 312                 line. */
 313              if (strpos($line, ':') === 0) {
 314                  $lowerLine = String::lower($line);
 315  
 316                  /* If we have a key (rather than a signature block), get the
 317                     key's fingerprint */
 318                  if (strpos($lowerLine, ':public key packet:') !== false ||
 319                      strpos($lowerLine, ':secret key packet:') !== false) {
 320                      $cmdline = array(
 321                          '--with-colons',
 322                          $input
 323                      );
 324                      $data = $this->_callGpg($cmdline, 'r');
 325                      if (preg_match("/(sec|pub):.*:.*:.*:([A-F0-9]{16}):/", $data->stdout, $matches)) {
 326                          $fingerprint = $matches[2];
 327                      }
 328                  }
 329  
 330                  if (strpos($lowerLine, ':public key packet:') !== false) {
 331                      $header = 'public_key';
 332                  } elseif (strpos($lowerLine, ':secret key packet:') !== false) {
 333                      $header = 'secret_key';
 334                  } elseif (strpos($lowerLine, ':user id packet:') !== false) {
 335                      $uid_idx++;
 336                      $line = preg_replace_callback('/\\\\x([0-9a-f]{2})/', create_function('$a', 'return chr(hexdec($a[1]));'), $line);
 337                      if (preg_match("/\"([^\<]+)\<([^\>]+)\>\"/", $line, $matches)) {
 338                          $header = 'id' . $uid_idx;
 339                          if (preg_match('/([^\(]+)\((.+)\)$/', trim($matches[1]), $comment_matches)) {
 340                              $data_array['signature'][$header]['name'] = trim($comment_matches[1]);
 341                              $data_array['signature'][$header]['comment'] = $comment_matches[2];
 342                          } else {
 343                              $data_array['signature'][$header]['name'] = trim($matches[1]);
 344                              $data_array['signature'][$header]['comment'] = '';
 345                          }
 346                          $data_array['signature'][$header]['email'] = $matches[2];
 347                          $data_array['signature'][$header]['fingerprint'] = $fingerprint;
 348                      }
 349                  } elseif (strpos($lowerLine, ':signature packet:') !== false) {
 350                      if (empty($header) || empty($uid_idx)) {
 351                          $header = '_SIGNATURE';
 352                      }
 353                      if (preg_match("/keyid\s+([0-9A-F]+)/i", $line, $matches)) {
 354                          $sig_id = $matches[1];
 355                          $data_array['signature'][$header]['sig_' . $sig_id]['fingerprint'] = $matches[1];
 356                          $data_array['fingerprint'] = $matches[1];
 357                      }
 358                  } else {
 359                      $header = null;
 360                  }
 361              } else {
 362                  if (($header == 'secret_key') || ($header == 'public_key')) {
 363                      if (preg_match("/created\s+(\d+),\s+expires\s+(\d+)/i", $line, $matches)) {
 364                          $data_array[$header]['created'] = $matches[1];
 365                          $data_array[$header]['expires'] = $matches[2];
 366                      } elseif (preg_match("/\s+[sp]key\[0\]:\s+\[(\d+)/i", $line, $matches)) {
 367                          $data_array[$header]['size'] = $matches[1];
 368                      }
 369                  } elseif ($header) {
 370                      if (preg_match("/version\s+\d+,\s+created\s+(\d+)/i", $line, $matches)) {
 371                          $data_array['signature'][$header]['sig_' . $sig_id]['created'] = $matches[1];
 372                      } elseif (preg_match("/digest algo\s+(\d{1})/", $line, $matches)) {
 373                          $micalg = $this->_hashAlg[$matches[1]];
 374                          $data_array['signature'][$header]['sig_' . $sig_id]['micalg'] = $micalg;
 375                          if ($header == '_SIGNATURE') {
 376                              /* Likely a signature block, not a key. */
 377                              $data_array['signature']['_SIGNATURE']['micalg'] = $micalg;
 378                          }
 379                          if ($sig_id == $fingerprint) {
 380                              /* Self signing signature - we can assume the
 381                                 micalg value from this signature is that for
 382                                 the key */
 383                              $data_array['signature']['_SIGNATURE']['micalg'] = $micalg;
 384                              $data_array['signature'][$header]['micalg'] = $micalg;
 385                          }
 386                      }
 387                  }
 388              }
 389          }
 390  
 391          $fingerprint && $data_array['fingerprint'] = $fingerprint;
 392  
 393          return $data_array;
 394      }
 395  
 396      /**
 397       * Returns human readable information on a PGP key.
 398       *
 399       * @param string $pgpdata  The PGP data block.
 400       *
 401       * @return string  Tabular information on the PGP key.
 402       */
 403      function pgpPrettyKey($pgpdata)
 404      {
 405          $msg = '';
 406          $packet_info = $this->pgpPacketInformation($pgpdata);
 407  
 408          if (!empty($packet_info['signature'])) {
 409              /* Making the property names the same width for all
 410               * localizations .*/
 411              $leftrow = array(_("Name"), _("Key Type"), _("Key Creation"),
 412                               _("Expiration Date"), _("Key Length"),
 413                               _("Comment"), _("E-Mail"), _("Hash-Algorithm"),
 414                               _("Key Fingerprint"));
 415              $leftwidth = array_map('strlen', $leftrow);
 416              $maxwidth  = max($leftwidth) + 2;
 417              array_walk($leftrow, create_function('&$s, $k, $m', '$s = $s . ":" . str_repeat(" ", $m - String::length($s));'), $maxwidth);
 418  
 419              foreach (array_keys($packet_info['signature']) as $uid_idx) {
 420                  if ($uid_idx == '_SIGNATURE') continue;
 421                  $key_info = $this->pgpPacketSignatureByUidIndex($pgpdata, $uid_idx);
 422                  $msg .= $leftrow[0] . stripcslashes($key_info['name']) . "\n";
 423                  $msg .= $leftrow[1] . (($key_info['key_type'] == 'public_key') ? _("Public Key") : _("Private Key")) . "\n";
 424                  $msg .= $leftrow[2] . strftime("%D", $key_info['key_created']) . "\n";
 425                  $msg .= $leftrow[3] . (empty($key_info['key_expires']) ? '[' . _("Never") . ']' : strftime("%D", $key_info['key_expires'])) . "\n";
 426                  $msg .= $leftrow[4] . $key_info['key_size'] . " Bytes\n";
 427                  $msg .= $leftrow[5] . (empty($key_info['comment']) ? '[' . _("None") . ']' : $key_info['comment']) . "\n";
 428                  $msg .= $leftrow[6] . $key_info['email'] . "\n";
 429                  $msg .= $leftrow[7] . (empty($key_info['micalg']) ? '[' . _("Unknown") . ']' : $key_info['micalg']) . "\n";
 430                  $msg .= $leftrow[8] . (empty($key_info['fingerprint']) ? '[' . _("Unknown") . ']' : $key_info['fingerprint']) . "\n\n";
 431              }
 432          }
 433  
 434          return $msg;
 435      }
 436  
 437      /**
 438       * Returns only information on the first ID that matches the email address
 439       * input.
 440       *
 441       * @param string $pgpdata  The PGP data block.
 442       * @param string $email    An e-mail address.
 443       *
 444       * @return array  An array with information on the PGP data block. If an
 445       *                element is not present in the data block, it will
 446       *                likewise not be set in the array.
 447       * <pre>
 448       * Array Fields:
 449       * -------------
 450       * key_created  =>  Key creation - UNIX timestamp
 451       * key_expires  =>  Key expiration - UNIX timestamp (0 = never expires)
 452       * key_size     =>  Size of the key in bits
 453       * key_type     =>  The key type (public_key or secret_key)
 454       * name         =>  Full Name
 455       * comment      =>  Comment
 456       * email        =>  E-mail Address
 457       * fingerprint  =>  16-bit hex value
 458       * created      =>  Signature creation - UNIX timestamp
 459       * micalg       =>  The hash used to create the signature
 460       * </pre>
 461       */
 462      function pgpPacketSignature($pgpdata, $email)
 463      {
 464          $data = $this->pgpPacketInformation($pgpdata);
 465          $key_type = null;
 466          $return_array = array();
 467  
 468          /* Check that [signature] key exists. */
 469          if (!isset($data['signature'])) {
 470              return $return_array;
 471          }
 472  
 473          /* Store the signature information now. */
 474          if (($email == '_SIGNATURE') &&
 475              isset($data['signature']['_SIGNATURE'])) {
 476              foreach ($data['signature'][$email] as $key => $value) {
 477                  $return_array[$key] = $value;
 478              }
 479          } else {
 480              $uid_idx = 1;
 481  
 482              while (isset($data['signature']['id' . $uid_idx])) {
 483                  if ($data['signature']['id' . $uid_idx]['email'] == $email) {
 484                      foreach ($data['signature']['id' . $uid_idx] as $key => $val) {
 485                          $return_array[$key] = $val;
 486                      }
 487                      break;
 488                  }
 489                  $uid_idx++;
 490              }
 491          }
 492  
 493          return $this->_pgpPacketSignature($data, $return_array);
 494      }
 495  
 496      /**
 497       * Returns information on a PGP signature embedded in PGP data.  Similar
 498       * to pgpPacketSignature(), but returns information by unique User ID
 499       * Index (format id{n} where n is an integer of 1 or greater).
 500       *
 501       * @param string $pgpdata   See pgpPacketSignature().
 502       * @param integer $uid_idx  The UID index.
 503       *
 504       * @return array  See pgpPacketSignature().
 505       */
 506      function pgpPacketSignatureByUidIndex($pgpdata, $uid_idx)
 507      {
 508          $data = $this->pgpPacketInformation($pgpdata);
 509          $key_type = null;
 510          $return_array = array();
 511  
 512          /* Search for the UID index. */
 513          if (!isset($data['signature']) ||
 514              !isset($data['signature'][$uid_idx])) {
 515              return $return_array;
 516          }
 517  
 518          /* Store the signature information now. */
 519          foreach ($data['signature'][$uid_idx] as $key => $value) {
 520              $return_array[$key] = $value;
 521          }
 522  
 523          return $this->_pgpPacketSignature($data, $return_array);
 524      }
 525  
 526      /**
 527       * Adds some data to the pgpPacketSignature*() function array.
 528       *
 529       * @access private
 530       *
 531       * @param array $data      See pgpPacketSignature().
 532       * @param array $retarray  The return array.
 533       *
 534       * @return array  The return array.
 535       */
 536      function _pgpPacketSignature($data, $retarray)
 537      {
 538          /* If empty, return now. */
 539          if (empty($retarray)) {
 540              return $retarray;
 541          }
 542  
 543          $key_type = null;
 544  
 545          /* Store any public/private key information. */
 546          if (isset($data['public_key'])) {
 547              $key_type = 'public_key';
 548          } elseif (isset($data['secret_key'])) {
 549              $key_type = 'secret_key';
 550          }
 551  
 552          if ($key_type) {
 553              $retarray['key_type'] = $key_type;
 554              if (isset($data[$key_type]['created'])) {
 555                  $retarray['key_created'] = $data[$key_type]['created'];
 556              }
 557              if (isset($data[$key_type]['expires'])) {
 558                  $retarray['key_expires'] = $data[$key_type]['expires'];
 559              }
 560              if (isset($data[$key_type]['size'])) {
 561                  $retarray['key_size'] = $data[$key_type]['size'];
 562              }
 563          }
 564  
 565          return $retarray;
 566      }
 567  
 568      /**
 569       * Returns the short fingerprint (Key ID) of the key used to sign a block
 570       * of PGP data.
 571       *
 572       * @param string $text  The PGP signed text block.
 573       *
 574       * @return string  The short fingerprint of the key used to sign $text.
 575       */
 576      function getSignersFingerprint($text)
 577      {
 578          $fingerprint = null;
 579  
 580          $input = $this->_createTempFile('horde-pgp');
 581  
 582          $fp = fopen($input, 'w+');
 583          fputs($fp, $text);
 584          fclose($fp);
 585  
 586          $cmdline = array(
 587              '--verify',
 588              $input,
 589              '2>&1'
 590          );
 591          $result = $this->_callGpg($cmdline, 'r');
 592          if (preg_match('/gpg:\sSignature\smade.*ID\s+([A-F0-9]{8})\s+/', $result->stdout, $matches)) {
 593              $fingerprint = $matches[1];
 594          }
 595  
 596          return $fingerprint;
 597      }
 598  
 599      /**
 600       * Verify a passphrase for a given public/private keypair.
 601       *
 602       * @param string $public_key   The user's PGP public key.
 603       * @param string $private_key  The user's PGP private key.
 604       * @param string $passphrase   The user's passphrase.
 605       *
 606       * @return boolean  Returns true on valid passphrase, false on invalid
 607       *                  passphrase, and PEAR_Error on error.
 608       */
 609      function verifyPassphrase($public_key, $private_key, $passphrase)
 610      {
 611          /* Check for secure connection. */
 612          $secure_check = $this->requireSecureConnection();
 613          if (is_a($secure_check, 'PEAR_Error')) {
 614              return $secure_check;
 615          }
 616  
 617          /* Encrypt a test message. */
 618          $result = $this->encrypt('Test', array('type' => 'message', 'pubkey' => $public_key));
 619          if (is_a($result, 'PEAR_Error')) {
 620              return false;
 621          }
 622  
 623          /* Try to decrypt the message. */
 624          $result = $this->decrypt($result, array('type' => 'message', 'pubkey' => $public_key, 'privkey' => $private_key, 'passphrase' => $passphrase));
 625          if (is_a($result, 'PEAR_Error')) {
 626              return false;
 627          }
 628  
 629          return true;
 630      }
 631  
 632      /**
 633       * Parses a message into text and PGP components.
 634       *
 635       * @param string $text  The text to parse.
 636       *
 637       * @return array  An array with the parsed text, returned in blocks of
 638       *                text corresponding to their actual order.
 639       * <pre>
 640       * Return array:
 641       * Key         Value
 642       * -------------------------------------------------
 643       * 'type'  =>  The type of data contained in block.
 644       *             Valid types are defined at the top of this class
 645       *             (the PGP_ARMOR_* constants).
 646       * 'data'  =>  The actual data for each section.
 647       * </pre>
 648       */
 649      function parsePGPData($text)
 650      {
 651          $data = array();
 652  
 653          $buffer = explode("\n", $text);
 654  
 655          /* Set $temp_array to be of type PGP_ARMOR_TEXT. */
 656          $temp_array = array();
 657          $temp_array['type'] = PGP_ARMOR_TEXT;
 658  
 659          foreach ($buffer as $value) {
 660              if (preg_match('/^-----(BEGIN|END) PGP ([^-]+)-----\s*$/', $value, $matches)) {
 661                  if (isset($temp_array['data'])) {
 662                      $data[] = $temp_array;
 663                  }
 664                  unset($temp_array);
 665                  $temp_array = array();
 666  
 667                  if ($matches[1] === 'BEGIN') {
 668                      $temp_array['type'] = $this->_armor[$matches[2]];
 669                      $temp_array['data'][] = $value;
 670                  } elseif ($matches[1] === 'END') {
 671                      $temp_array['type'] = PGP_ARMOR_TEXT;
 672                      $data[count($data) - 1]['data'][] = $value;
 673                  }
 674              } else {
 675                  $temp_array['data'][] = $value;
 676              }
 677          }
 678  
 679          if (isset($temp_array['data'])) {
 680              $data[] = $temp_array;
 681          }
 682  
 683          return $data;
 684      }
 685  
 686      /**
 687       * Returns a PGP public key from a public keyserver.
 688       *
 689       * @param string $fprint  The fingerprint of the PGP key.
 690       * @param string $server  The keyserver to use.
 691       * @param float $timeout  The keyserver timeout.
 692       *
 693       * @return string  The PGP public key, or PEAR_Error on error.
 694       */
 695      function getPublicKeyserver($fprint, $server = PGP_KEYSERVER_PUBLIC,
 696                                  $timeout = PGP_KEYSERVER_TIMEOUT)
 697      {
 698          /* Get the 8 character fingerprint string. */
 699          if (strpos($fprint, '0x') === 0) {
 700              $fprint = substr($fprint, 2);
 701          }
 702          if (strlen($fprint) > 8) {
 703              $fprint = substr($fprint, 8);
 704          }
 705          $fprint = '0x' . $fprint;
 706  
 707          /* Connect to the public keyserver. */
 708          $cmd = 'GET /pks/lookup?op=get&exact=on&search=' . $fprint . ' HTTP/1.0';
 709          $output = $this->_connectKeyserver($cmd, $server, $timeout);
 710          if (is_a($output, 'PEAR_Error')) {
 711              return $output;
 712          }
 713  
 714          /* Strip HTML Tags from output. */
 715          if (($start = strstr($output, '-----BEGIN'))) {
 716              $length = strpos($start, '-----END') + 34;
 717              return substr($start, 0, $length);
 718          } else {
 719              return PEAR::raiseError(_("Could not obtain public key from the keyserver."), 'horde.error');
 720          }
 721      }
 722  
 723      /**
 724       * Sends a PGP public key to a public keyserver.
 725       *
 726       * @param string $pubkey  The PGP public key
 727       * @param string $server  The keyserver to use.
 728       * @param float $timeout  The keyserver timeout.
 729       *
 730       * @return PEAR_Error  PEAR_Error on error/failure.
 731       */
 732      function putPublicKeyserver($pubkey, $server = PGP_KEYSERVER_PUBLIC,
 733                                  $timeout = PGP_KEYSERVER_TIMEOUT)
 734      {
 735          /* Get the fingerprint of the public key. */
 736          $info = $this->pgpPacketInformation($pubkey);
 737  
 738          /* See if the public key already exists on the keyserver. */
 739          if (!is_a($this->getPublicKeyserver($info['fingerprint'], $server, $timeout), 'PEAR_Error')) {
 740              return PEAR::raiseError(_("Key already exists on the public keyserver."), 'horde.warning');
 741          }
 742  
 743          /* Connect to the public keyserver. _connectKeyserver() returns a
 744             PEAR_Error object on error and the output text on success. */
 745          $pubkey = 'keytext=' . urlencode(rtrim($pubkey));
 746          $cmd = array(
 747              'POST /pks/add HTTP/1.0',
 748              'Host: ' . $server . ':11371',
 749              'User-Agent: Horde Application Framework 3.1',
 750              'Content-Type: application/x-www-form-urlencoded',
 751              'Content-Length: ' . strlen($pubkey),
 752              'Connection: close',
 753              '',
 754              $pubkey
 755          );
 756          $result = $this->_connectKeyserver(implode("\r\n", $cmd), $server, $timeout);
 757          if (is_a($result, 'PEAR_Error')) {
 758              return $result;
 759          }
 760      }
 761  
 762      /**
 763       * Connects to a public key server via HKP (Horrowitz Keyserver Protocol).
 764       *
 765       * @access private
 766       *
 767       * @param string $command  The PGP command to run.
 768       * @param string $server   The keyserver to use.
 769       * @param float $timeout   The timeout value.
 770       *
 771       * @return string  The text from standard output on success, or PEAR_Error
 772       *                 on error/failure.
 773       */
 774      function _connectKeyserver($command, $server, $timeout)
 775      {
 776          $connRefuse = 0;
 777          $output = '';
 778  
 779          /* Attempt to get the key from the keyserver. */
 780          do {
 781              $connError = false;
 782              $errno = $errstr = null;
 783  
 784              /* The HKP server is located on port 11371. */
 785              $fp = @fsockopen($server, '11371', $errno, $errstr, $timeout);
 786              if (!$fp) {
 787                  $connError = true;
 788              } else {
 789                  fputs($fp, $command . "\n\n");
 790                  while (!feof($fp)) {
 791                      $output .= fgets($fp, 1024);
 792                  }
 793                  fclose($fp);
 794              }
 795  
 796              if ($connError) {
 797                  if (++$connRefuse === PGP_KEYSERVER_REFUSE) {
 798                      if ($errno == 0) {
 799                          $output = PEAR::raiseError(_("Connection refused to the public keyserver."), 'horde.error');
 800                      } else {
 801                          $output = PEAR::raiseError(sprintf(_("Connection refused to the public keyserver. Reason: %s (%s)"), String::convertCharset($errstr, NLS::getExternalCharset()), $errno), 'horde.error');
 802                      }
 803                      break;
 804                  }
 805              }
 806          } while ($connError);
 807  
 808          return $output;
 809      }
 810  
 811      /**
 812       * Encrypts text using PGP.
 813       *
 814       * @param string $text   The text to be PGP encrypted.
 815       * @param array $params  The parameters needed for encryption.
 816       *                       See the individual _encrypt*() functions for the
 817       *                       parameter requirements.
 818       *
 819       * @return string  The encrypted message, or PEAR_Error on error.
 820       */
 821      function encrypt($text, $params = array())
 822      {
 823          if (isset($params['type'])) {
 824              if ($params['type'] === 'message') {
 825                  return $this->_encryptMessage($text, $params);
 826              } elseif ($params['type'] === 'signature') {
 827                  return $this->_encryptSignature($text, $params);
 828              }
 829          }
 830      }
 831  
 832      /**
 833       * Decrypts text using PGP.
 834       *
 835       * @param string $text   The text to be PGP decrypted.
 836       * @param array $params  The parameters needed for decryption.
 837       *                       See the individual _decrypt*() functions for the
 838       *                       parameter requirements.
 839       *
 840       * @return string  The decrypted message, or PEAR_Error on error.
 841       */
 842      function decrypt($text, $params = array())
 843      {
 844          if (isset($params['type'])) {
 845              if ($params['type'] === 'message') {
 846                  return $this->_decryptMessage($text, $params);
 847              } elseif (($params['type'] === 'signature') ||
 848                        ($params['type'] === 'detached-signature')) {
 849                  return $this->_decryptSignature($text, $params);
 850              }
 851          }
 852      }
 853  
 854      /**
 855       * Creates a temporary gpg keyring.
 856       *
 857       * @access private
 858       *
 859       * @param string $type  The type of key to analyze. Either 'public'
 860       *                      (Default) or 'private'
 861       *
 862       * @return string  Command line keystring option to use with gpg program.
 863       */
 864      function _createKeyring($type = 'public')
 865      {
 866          $type = String::lower($type);
 867  
 868          if ($type === 'public') {
 869              if (empty($this->_publicKeyring)) {
 870                  $this->_publicKeyring = $this->_createTempFile('horde-pgp');
 871              }
 872              return '--keyring ' . $this->_publicKeyring;
 873          } elseif ($type === 'private') {
 874              if (empty($this->_privateKeyring)) {
 875                  $this->_privateKeyring = $this->_createTempFile('horde-pgp');
 876              }
 877              return '--secret-keyring ' . $this->_privateKeyring;
 878          }
 879      }
 880  
 881      /**
 882       * Adds PGP keys to the keyring.
 883       *
 884       * @access private
 885       *
 886       * @param mixed $keys   A single key or an array of key(s) to add to the
 887       *                      keyring.
 888       * @param string $type  The type of key(s) to add. Either 'public'
 889       *                      (Default) or 'private'
 890       *
 891       * @return string  Command line keystring option to use with gpg program.
 892       */
 893      function _putInKeyring($keys = array(), $type = 'public')
 894      {
 895          $type = String::lower($type);
 896  
 897          if (!is_array($keys)) {
 898              $keys = array($keys);
 899          }
 900  
 901          /* Create the keyrings if they don't already exist. */
 902          $keyring = $this->_createKeyring($type);
 903  
 904          /* Store the key(s) in the keyring. */
 905          $cmdline = array(
 906              '--allow-secret-key-import',
 907              '--fast-import',
 908              $keyring
 909          );
 910          $this->_callGpg($cmdline, 'w', array_values($keys));
 911  
 912          return $keyring;
 913      }
 914  
 915      /**
 916       * Encrypts a message in PGP format using a public key.
 917       *
 918       * @access private
 919       *
 920       * @param string $text   The text to be encrypted.
 921       * @param array $params  The parameters needed for encryption.
 922       * <pre>
 923       * Parameters:
 924       * ===========
 925       * 'type'    =>  'message' (REQUIRED)
 926       * 'pubkey'  =>  PGP public key. (Optional) (DEPRECATED)
 927       * 'email'   =>  E-mail address of recipient. If not present, or not found
 928       *               in the public key, the first e-mail address found in the
 929       *               key will be used instead. (Optional) (DEPRECATED)
 930       * 'recips'  =>  An array with the e-mail address of the recipient as
 931       *               the key and that person's public key as the value.
 932       *               (REQUIRED)
 933       * </pre>
 934       *
 935       * @return string  The encrypted message, or PEAR_Error on error.
 936       */
 937      function _encryptMessage($text, $params)
 938      {
 939          $email = null;
 940  
 941          if (!isset($params['recips'])) {
 942              /* Check for required parameters. */
 943              if (!isset($params['pubkey'])) {
 944                  return PEAR::raiseError(_("A public PGP key is required to encrypt a message."), 'horde.error');
 945              }
 946  
 947              /* Get information on the key. */
 948              if (isset($params['email'])) {
 949                  $key_info = $this->pgpPacketSignature($params['pubkey'], $params['email']);
 950                  if (!empty($key_info)) {
 951                      $email = $key_info['email'];
 952                  }
 953              }
 954  
 955              /* If we have no email address at this point, use the first email
 956                 address found in the public key. */
 957              if (empty($email)) {
 958                  $key_info = $this->pgpPacketInformation($params['pubkey']);
 959                  if (isset($key_info['signature']['id1']['email'])) {
 960                      $email = $key_info['signature']['id1']['email'];
 961                  } else {
 962                      return PEAR::raiseError(_("Could not determine the recipient's e-mail address."), 'horde.error');
 963                  }
 964              }
 965  
 966              $params['recips'] = array($email => $params['pubkey']);
 967          }
 968  
 969          /* Store public key in temporary keyring. */
 970          $keyring = $this->_putInKeyring(array_values($params['recips']));
 971  
 972          /* Encrypt the document. */
 973          $cmdline = array(
 974              '--armor',
 975              '--batch',
 976              '--always-trust',
 977              $keyring,
 978              '--encrypt'
 979          );
 980          foreach (array_keys($params['recips']) as $val) {
 981              $cmdline[] = '--recipient ' . $val;
 982          }
 983          $result = $this->_callGpg($cmdline, 'w', $text, true, true);
 984          if (empty($result->output)) {
 985              $error = preg_replace('/\n.*/', '', $result->stderr);
 986              return PEAR::raiseError(_("Could not PGP encrypt message: ") . $error, 'horde.error');
 987          }
 988  
 989          return $result->output;
 990      }
 991  
 992      /**
 993       * Signs a message in PGP format using a private key.
 994       *
 995       * @access private
 996       *
 997       * @param string $text   The text to be signed.
 998       * @param array $params  The parameters needed for signing.
 999       * <pre>
1000       * Parameters:
1001       * ===========
1002       * 'type'        =>  'signature' (REQUIRED)
1003       * 'pubkey'      =>  PGP public key. (REQUIRED)
1004       * 'privkey'     =>  PGP private key. (REQUIRED)
1005       * 'passphrase'  =>  Passphrase for PGP Key. (REQUIRED)
1006       * 'sigtype'     =>  Determine the signature type to use. (Optional)
1007       *                   'cleartext'  --  Make a clear text signature
1008       *                   'detach'     --  Make a detached signature (DEFAULT)
1009       * </pre>
1010       *
1011       * @return string  The signed message, or PEAR_Error on error.
1012       */
1013      function _encryptSignature($text, $params)
1014      {
1015          /* Check for secure connection. */
1016          $secure_check = $this->requireSecureConnection();
1017          if (is_a($secure_check, 'PEAR_Error')) {
1018              return $secure_check;
1019          }
1020  
1021          /* Check for required parameters. */
1022          if (!isset($params['pubkey']) ||
1023              !isset($params['privkey']) ||
1024              !isset($params['passphrase'])) {
1025              return PEAR::raiseError(_("A public PGP key, private PGP key, and passphrase are required to sign a message."), 'horde.error');
1026          }
1027  
1028          /* Create temp files for input. */
1029          $input = $this->_createTempFile('horde-pgp');
1030  
1031          /* Encryption requires both keyrings. */
1032          $pub_keyring = $this->_putInKeyring(array($params['pubkey']));
1033          $sec_keyring = $this->_putInKeyring(array($params['privkey']), 'private');
1034  
1035          /* Store message in temporary file. */
1036          $fp = fopen($input, 'w+');
1037          fputs($fp, $text);
1038          fclose($fp);
1039  
1040          /* Determine the signature type to use. */
1041          $cmdline = array();
1042          if (isset($params['sigtype']) &&
1043              $params['sigtype'] == 'cleartext') {
1044              $sign_type = '--clearsign';
1045          } else {
1046              $sign_type = '--detach-sign';
1047          }
1048  
1049          /* Additional GPG options. */
1050          $cmdline += array(
1051              '--armor',
1052              '--batch',
1053              '--passphrase-fd 0',
1054              $sec_keyring,
1055              $pub_keyring,
1056              $sign_type,
1057              $input
1058          );
1059  
1060          /* Sign the document. */
1061          $result = $this->_callGpg($cmdline, 'w', $params['passphrase'], true, true);
1062          if (empty($result->output)) {
1063              $error = preg_replace('/\n.*/', '', $result->stderr);
1064              return PEAR::raiseError(_("Could not PGP sign message: ") . $error, 'horde.error');
1065          } else {
1066              return $result->output;
1067          }
1068      }
1069  
1070      /**
1071       * Decrypts an PGP encrypted message using a private/public keypair and a
1072       * passhprase.
1073       *
1074       * @access private
1075       *
1076       * @param string $text   The text to be decrypted.
1077       * @param array $params  The parameters needed for decryption.
1078       * <pre>
1079       * Parameters:
1080       * ===========
1081       * 'type'        =>  'message' (REQUIRED)
1082       * 'pubkey'      =>  PGP public key. (REQUIRED)
1083       * 'privkey'     =>  PGP private key. (REQUIRED)
1084       * 'passphrase'  =>  Passphrase for PGP Key. (REQUIRED)
1085       * </pre>
1086       *
1087       * @return stdClass  An object with the following properties, or PEAR_Error
1088       *                   on error:
1089       * <pre>
1090       * 'message'     -  The decrypted message.
1091       * 'sig_result'  -  The result of the signature test.
1092       * </pre>
1093       */
1094      function _decryptMessage($text, $params)
1095      {
1096          /* Check for secure connection. */
1097          $secure_check = $this->requireSecureConnection();
1098          if (is_a($secure_check, 'PEAR_Error')) {
1099              return $secure_check;
1100          }
1101  
1102          $good_sig_flag = false;
1103  
1104          /* Check for required parameters. */
1105          if (!isset($params['pubkey']) ||
1106              !isset($params['privkey']) ||
1107              !isset($params['passphrase'])) {
1108              return PEAR::raiseError(_("A public PGP key, private PGP key, and passphrase are required to decrypt a message."), 'horde.error');
1109          }
1110  
1111          /* Create temp files. */
1112          $input = $this->_createTempFile('horde-pgp');
1113  
1114          /* Decryption requires both keyrings. */
1115          $pub_keyring = $this->_putInKeyring(array($params['pubkey']));
1116          $sec_keyring = $this->_putInKeyring(array($params['privkey']), 'private');
1117  
1118          /* Store message in file. */
1119          $fp = fopen($input, 'w+');
1120          fputs($fp, $text);
1121          fclose($fp);
1122  
1123          /* Decrypt the document now. */
1124          $cmdline = array(
1125              '--always-trust',
1126              '--armor',
1127              '--batch',
1128              '--passphrase-fd 0',
1129              $sec_keyring,
1130              $pub_keyring,
1131              '--decrypt',
1132              $input
1133          );
1134          $result = $this->_callGpg($cmdline, 'w', $params['passphrase'], true, true);
1135          if (empty($result->output)) {
1136              $error = preg_replace('/\n.*/', '', $result->stderr);
1137              return PEAR::raiseError(_("Could not decrypt PGP data: ") . $error, 'horde.error');
1138          }
1139  
1140          /* Create the return object. */
1141          $ob = &new stdClass;
1142          $ob->message = $result->output;
1143  
1144          /* Check the PGP signature. */
1145          $sig_check = $this->_checkSignatureResult($result->stderr);
1146          if (is_a($sig_check, 'PEAR_Error')) {
1147              $ob->sig_result = $sig_check;
1148          } else {
1149              $ob->sig_result = ($sig_check) ? $result->stderr : '';
1150          }
1151  
1152          return $ob;
1153      }
1154  
1155      /**
1156       * Decrypts an PGP signed message using a public key.
1157       *
1158       * @access private
1159       *
1160       * @param string $text   The text to be verified.
1161       * @param array $params  The parameters needed for verification.
1162       * <pre>
1163       * Parameters:
1164       * ===========
1165       * 'type'       =>  'signature' or 'detached-signature' (REQUIRED)
1166       * 'pubkey'     =>  PGP public key. (REQUIRED)
1167       * 'signature'  =>  PGP signature block. (REQUIRED for detached signature)
1168       * </pre>
1169       *
1170       * @return string  The verification message from gpg. If no signature,
1171       *                 returns empty string, and PEAR_Error on error.
1172       */
1173      function _decryptSignature($text, $params)
1174      {
1175          /* Check for required parameters. */
1176          if (!isset($params['pubkey'])) {
1177              return PEAR::raiseError(_("A public PGP key is required to verify a signed message."), 'horde.error');
1178          }
1179          if (($params['type'] === 'detached-signature') &&
1180              !isset($params['signature'])) {
1181              return PEAR::raiseError(_("The detached PGP signature block is required to verify the signed message."), 'horde.error');
1182          }
1183  
1184          $good_sig_flag = 0;
1185  
1186          /* Create temp files for input. */
1187          $input = $this->_createTempFile('horde-pgp');
1188  
1189          /* Store public key in temporary keyring. */
1190          $keyring = $this->_putInKeyring($params['pubkey']);
1191  
1192          /* Store the message in a temporary file. */
1193          $fp = fopen($input, 'w+');
1194          fputs($fp, $text);
1195          fclose($fp);
1196  
1197          /* Options for the GPG binary. */
1198          $cmdline = array(
1199              '--armor',
1200              '--always-trust',
1201              '--batch',
1202              '--charset ' . NLS::getCharset(),
1203              $keyring,
1204              '--verify'
1205          );
1206  
1207          /* Extra stuff to do if we are using a detached signature. */
1208          if ($params['type'] === 'detached-signature') {
1209              $sigfile = $this->_createTempFile('horde-pgp');
1210              $cmdline[] = $sigfile . ' ' . $input;
1211  
1212              $fp = fopen($sigfile, 'w+');
1213              fputs($fp, $params['signature']);
1214              fclose($fp);
1215          } else {
1216              $cmdline[] = $input;
1217          }
1218          $cmdline[] = '2>&1';
1219  
1220          /* Verify the signature.
1221             We need to catch standard error output, since this is where
1222             the signature information is sent. */
1223          $result = $this->_callGpg($cmdline, 'r');
1224          $sig_result = $this->_checkSignatureResult($result->stdout);
1225          if (is_a($sig_result, 'PEAR_Error')) {
1226              return $sig_result;
1227          } else {
1228              return ($sig_result) ? $result->stdout : '';
1229          }
1230      }
1231  
1232      /**
1233       * Checks signature result from the GnuPG binary.
1234       *
1235       * @access private
1236       *
1237       * @param string $result  The signature result.
1238       *
1239       * @return boolean  True if signature is good.
1240       */
1241      function _checkSignatureResult($result)
1242      {
1243          /* Good signature:
1244           *   gpg: Good signature from "blah blah blah (Comment)"
1245           * Bad signature:
1246           *   gpg: BAD signature from "blah blah blah (Comment)" */
1247          if (strpos($result, 'gpg: BAD signature') !== false) {
1248              return PEAR::raiseError($result, 'horde.error');
1249          } elseif (strpos($result, 'gpg: Good signature') !== false) {
1250              return true;
1251          } else {
1252              return false;
1253          }
1254      }
1255  
1256      /**
1257       * Signs a MIME_Part using PGP.
1258       *
1259       * @param MIME_Part $mime_part  The MIME_Part object to sign.
1260       * @param array $params         The parameters required for signing.
1261       *                              @see _encryptSignature().
1262       *
1263       * @return MIME_Part  A MIME_Part object that is signed according to RFC
1264       *                    2015/3156, or PEAR_Error on error.
1265       */
1266      function signMIMEPart($mime_part, $params = array())
1267      {
1268          include_once 'Horde/MIME/Part.php';
1269  
1270          $params = array_merge($params, array('type' => 'signature', 'sigtype' => 'detach'));
1271  
1272          /* RFC 2015 Requirements for a PGP signed message:
1273           * + Content-Type params 'micalg' & 'protocol' are REQUIRED.
1274           * + The digitally signed message MUST be constrained to 7 bits.
1275           * + The MIME headers MUST be a part of the signed data. */
1276  
1277          $mime_part->strict7bit(true);
1278          $msg_sign = $this->encrypt($mime_part->toCanonicalString(), $params);
1279          if (is_a($msg_sign, 'PEAR_Error')) {
1280              return $msg_sign;
1281          }
1282  
1283          /* Add the PGP signature. */
1284          $pgp_sign = &new MIME_Part('application/pgp-signature', $msg_sign, null, 'inline');
1285          $pgp_sign->setDescription(_("PGP Digital Signature"));
1286  
1287          /* Get the algorithim information from the signature. Since we are
1288           * analyzing a signature packet, we need to use the special keyword
1289           * '_SIGNATURE' - see Horde_Crypt_pgp. */
1290          $sig_info = $this->pgpPacketSignature($msg_sign, '_SIGNATURE');
1291  
1292          /* Setup the multipart MIME Part. */
1293          $part = &new MIME_Part('multipart/signed');
1294          $part->setContents('This message is in MIME format and has been PGP signed.' . "\n");
1295          $part->addPart($mime_part);
1296          $part->addPart($pgp_sign);
1297          $part->setContentTypeParameter('protocol', 'application/pgp-signature');
1298          $part->setContentTypeParameter('micalg', $sig_info['micalg']);
1299  
1300          return $part;
1301      }
1302  
1303      /**
1304       * Encrypts a MIME_Part using PGP.
1305       *
1306       * @param MIME_Part $mime_part  The MIME_Part object to encrypt.
1307       * @param array $params         The parameters required for encryption.
1308       *                               @see _encryptMessage().
1309       *
1310       * @return MIME_Part  A MIME_Part object that is encrypted according to RFC
1311       *                    2015/3156, or PEAR_Error on error.
1312       */
1313      function encryptMIMEPart($mime_part, $params = array())
1314      {
1315          include_once 'Horde/MIME/Part.php';
1316  
1317          $params = array_merge($params, array('type' => 'message'));
1318  
1319          $signenc_body = $mime_part->toCanonicalString();
1320          $message_encrypt = $this->encrypt($signenc_body, $params);
1321          if (is_a($message_encrypt, 'PEAR_Error')) {
1322              return $message_encrypt;
1323          }
1324  
1325          /* Set up MIME Structure according to RFC 2015. */
1326          $part = &new MIME_Part('multipart/encrypted');
1327          $part->setContents('This message is in MIME format and has been PGP encrypted.' . "\n");
1328          $part->addPart(new MIME_Part('application/pgp-encrypted', "Version: 1\n", null));
1329          $part->addPart(new MIME_Part('application/octet-stream', $message_encrypt, null, 'inline'));
1330          $part->setContentTypeParameter('protocol', 'application/pgp-encrypted');
1331          $part->setDescription(_("PGP Encrypted Data"));
1332  
1333          return $part;
1334      }
1335  
1336      /**
1337       * Signs and encrypts a MIME_Part using PGP.
1338       *
1339       * @param MIME_Part $mime_part   The MIME_Part object to sign and encrypt.
1340       * @param array $sign_params     The parameters required for signing.
1341       *                               @see _encryptSignature().
1342       * @param array $encrypt_params  The parameters required for encryption.
1343       *                               @see _encryptMessage().
1344       *
1345       * @return MIME_Part  A MIME_Part object that is signed and encrypted
1346       *                    according to RFC 2015/3156, or PEAR_Error on error.
1347       */
1348      function signAndEncryptMIMEPart($mime_part, $sign_params = array(),
1349                                      $encrypt_params = array())
1350      {
1351          include_once 'Horde/MIME/Part.php';
1352  
1353          /* RFC 2015 requires that the entire signed message be encrypted.  We
1354           * need to explicitly call using Horde_Crypt_pgp:: because we don't
1355           * know whether a subclass has extended these methods. */
1356          $part = $this->signMIMEPart($mime_part, $sign_params);
1357          if (is_a($part, 'PEAR_Error')) {
1358              return $part;
1359          }
1360          $part = $this->encryptMIMEPart($part, $encrypt_params);
1361          if (is_a($part, 'PEAR_Error')) {
1362              return $part;
1363          }
1364          $part->setContents('This message is in MIME format and has been PGP signed and encrypted.' . "\n");
1365          $part->setDescription(_("PGP Signed/Encrypted Data"));
1366  
1367          return $part;
1368      }
1369  
1370      /**
1371       * Generates a MIME_Part object, in accordance with RFC 2015/3156, that
1372       * contains a public key.
1373       *
1374       * @param string $key  The public key.
1375       *
1376       * @return MIME_Part  A MIME_Part object that contains the public key.
1377       */
1378      function publicKeyMIMEPart($key)
1379      {
1380          include_once 'Horde/MIME/Part.php';
1381  
1382          $part = &new MIME_Part('application/pgp-keys', $key, NLS::getCharset());
1383          $part->setDescription(_("PGP Public Key"));
1384  
1385          return $part;
1386      }
1387  
1388      /**
1389       * Function that handles interfacing with the GnuPG binary.
1390       *
1391       * @access private
1392       *
1393       * @param array $options   TODO
1394       * @param string $mode     TODO
1395       * @param array $input     TODO
1396       * @param boolean $output  TODO
1397       * @param boolean $stderr  TODO
1398       *
1399       * @return stdClass  TODO
1400       */
1401      function _callGpg($options, $mode, $input = array(), $output = false,
1402                        $stderr = false)
1403      {
1404          $cmdline = array_merge($this->_gnupg, $options);
1405          $data = &new stdClass;
1406          $data->output = null;
1407          $data->stderr = null;
1408          $data->stdout = null;
1409  
1410          /* Create temp files for output. */
1411          if ($output) {
1412              $output_file = $this->_createTempFile('horde-pgp', false);
1413              array_unshift($options, '--output ' . $output_file);
1414  
1415              /* Do we need standard error output? */
1416              if ($stderr) {
1417                  $stderr_file = $this->_createTempFile('horde-pgp', false);
1418                  $options[] = '2> ' . $stderr_file;
1419              }
1420          }
1421  
1422          /* Build the command line string now. */
1423          $cmdline = implode(' ', array_merge($this->_gnupg, $options));
1424  
1425          if ($mode == 'w') {
1426              $fp = popen($cmdline, 'w');
1427              $win32 = substr(PHP_OS, 0, 3) == 'WIN';
1428  
1429              if (!is_array($input)) {
1430                  $input = array($input);
1431              }
1432              foreach ($input as $line) {
1433                  if ($win32 && (strpos($line, "\x0d\x0a") !== false)) {
1434                      $chunks = explode("\x0d\x0a", $line);
1435                      foreach ($chunks as $chunk) {
1436                          fputs($fp, $chunk . "\n");
1437                      }
1438                  } else {
1439                      fputs($fp, $line . "\n");
1440                  }
1441              }
1442          } elseif ($mode == 'r') {
1443              $fp = popen($cmdline, 'r');
1444              while (!feof($fp)) {
1445                  $data->stdout .= fgets($fp, 1024);
1446              }
1447          }
1448          pclose($fp);
1449  
1450          if ($output) {
1451              $data->output = file_get_contents($output_file);
1452              unlink($output_file);
1453              if ($stderr) {
1454                  $data->stderr = file_get_contents($stderr_file);
1455                  unlink($stderr_file);
1456              }
1457          }
1458  
1459          return $data;
1460      }
1461  
1462  }


Généré le : Sun Feb 25 18:01:28 2007 par Balluche grâce à PHPXref 0.7