[ Index ] |
|
Code source de Horde 3.1.3 |
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 }
titre
Description
Corps
titre
Description
Corps
titre
Description
Corps
titre
Corps
Généré le : Sun Feb 25 18:01:28 2007 | par Balluche grâce à PHPXref 0.7 |