summaryrefslogtreecommitdiff
path: root/program/lib/Crypt/GPG/KeyGenerator.php
diff options
context:
space:
mode:
Diffstat (limited to 'program/lib/Crypt/GPG/KeyGenerator.php')
-rw-r--r--program/lib/Crypt/GPG/KeyGenerator.php790
1 files changed, 0 insertions, 790 deletions
diff --git a/program/lib/Crypt/GPG/KeyGenerator.php b/program/lib/Crypt/GPG/KeyGenerator.php
deleted file mode 100644
index f59c0ee3a..000000000
--- a/program/lib/Crypt/GPG/KeyGenerator.php
+++ /dev/null
@@ -1,790 +0,0 @@
-<?php
-
-/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
-
-/**
- * Crypt_GPG is a package to use GPG from PHP
- *
- * This file contains an object that handles GnuPG key generation.
- *
- * PHP version 5
- *
- * LICENSE:
- *
- * This library is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as
- * published by the Free Software Foundation; either version 2.1 of the
- * License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- * @category Encryption
- * @package Crypt_GPG
- * @author Michael Gauthier <mike@silverorange.com>
- * @copyright 2011-2013 silverorange
- * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
- * @version CVS: $Id:$
- * @link http://pear.php.net/package/Crypt_GPG
- * @link http://www.gnupg.org/
- */
-
-/**
- * Base class for GPG methods
- */
-require_once 'Crypt/GPGAbstract.php';
-
-/**
- * Status output handler for key generation
- */
-require_once 'Crypt/GPG/KeyGeneratorStatusHandler.php';
-
-/**
- * Error output handler for key generation
- */
-require_once 'Crypt/GPG/KeyGeneratorErrorHandler.php';
-
-// {{{ class Crypt_GPG_KeyGenerator
-
-/**
- * GnuPG key generator
- *
- * This class provides an object oriented interface for generating keys with
- * the GNU Privacy Guard (GPG).
- *
- * Secure key generation requires true random numbers, and as such can be slow.
- * If the operating system runs out of entropy, key generation will block until
- * more entropy is available.
- *
- * If quick key generation is important, a hardware entropy generator, or an
- * entropy gathering daemon may be installed. For example, administrators of
- * Debian systems may want to install the 'randomsound' package.
- *
- * This class uses the experimental automated key generation support available
- * in GnuPG. See <b>doc/DETAILS</b> in the
- * {@link http://www.gnupg.org/download/ GPG distribution} for detailed
- * information on the key generation format.
- *
- * @category Encryption
- * @package Crypt_GPG
- * @author Nathan Fredrickson <nathan@silverorange.com>
- * @author Michael Gauthier <mike@silverorange.com>
- * @copyright 2005-2013 silverorange
- * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
- * @link http://pear.php.net/package/Crypt_GPG
- * @link http://www.gnupg.org/
- */
-class Crypt_GPG_KeyGenerator extends Crypt_GPGAbstract
-{
- // {{{ protected properties
-
- /**
- * The expiration date of generated keys
- *
- * @var integer
- *
- * @see Crypt_GPG_KeyGenerator::setExpirationDate()
- */
- protected $expirationDate = 0;
-
- /**
- * The passphrase of generated keys
- *
- * @var string
- *
- * @see Crypt_GPG_KeyGenerator::setPassphrase()
- */
- protected $passphrase = '';
-
- /**
- * The algorithm for generated primary keys
- *
- * @var integer
- *
- * @see Crypt_GPG_KeyGenerator::setKeyParams()
- */
- protected $keyAlgorithm = Crypt_GPG_SubKey::ALGORITHM_DSA;
-
- /**
- * The size of generated primary keys
- *
- * @var integer
- *
- * @see Crypt_GPG_KeyGenerator::setKeyParams()
- */
- protected $keySize = 1024;
-
- /**
- * The usages of generated primary keys
- *
- * This is a bitwise combination of the usage constants in
- * {@link Crypt_GPG_SubKey}.
- *
- * @var integer
- *
- * @see Crypt_GPG_KeyGenerator::setKeyParams()
- */
- protected $keyUsage = 6; // USAGE_SIGN | USAGE_CERTIFY
-
- /**
- * The algorithm for generated sub-keys
- *
- * @var integer
- *
- * @see Crypt_GPG_KeyGenerator::setSubKeyParams()
- */
- protected $subKeyAlgorithm = Crypt_GPG_SubKey::ALGORITHM_ELGAMAL_ENC;
-
- /**
- * The size of generated sub-keys
- *
- * @var integer
- *
- * @see Crypt_GPG_KeyGenerator::setSubKeyParams()
- */
- protected $subKeySize = 2048;
-
- /**
- * The usages of generated sub-keys
- *
- * This is a bitwise combination of the usage constants in
- * {@link Crypt_GPG_SubKey}.
- *
- * @var integer
- *
- * @see Crypt_GPG_KeyGenerator::setSubKeyParams()
- */
- protected $subKeyUsage = Crypt_GPG_SubKey::USAGE_ENCRYPT;
-
- /**
- * The GnuPG status handler to use for key generation
- *
- * @var Crypt_GPG_KeyGeneratorStatusHandler
- *
- * @see Crypt_GPG_KeyGenerator::setStatusHandler()
- */
- protected $statusHandler = null;
-
- /**
- * The GnuPG error handler to use for key generation
- *
- * @var Crypt_GPG_KeyGeneratorErrorHandler
- *
- * @see Crypt_GPG_KeyGenerator::setErrorHandler()
- */
- protected $errorHandler = null;
-
- // }}}
- // {{{ __construct()
-
- /**
- * Creates a new GnuPG key generator
- *
- * Available options are:
- *
- * - <kbd>string homedir</kbd> - the directory where the GPG
- * keyring files are stored. If not
- * specified, Crypt_GPG uses the
- * default of <kbd>~/.gnupg</kbd>.
- * - <kbd>string publicKeyring</kbd> - the file path of the public
- * keyring. Use this if the public
- * keyring is not in the homedir, or
- * if the keyring is in a directory
- * not writable by the process
- * invoking GPG (like Apache). Then
- * you can specify the path to the
- * keyring with this option
- * (/foo/bar/pubring.gpg), and specify
- * a writable directory (like /tmp)
- * using the <i>homedir</i> option.
- * - <kbd>string privateKeyring</kbd> - the file path of the private
- * keyring. Use this if the private
- * keyring is not in the homedir, or
- * if the keyring is in a directory
- * not writable by the process
- * invoking GPG (like Apache). Then
- * you can specify the path to the
- * keyring with this option
- * (/foo/bar/secring.gpg), and specify
- * a writable directory (like /tmp)
- * using the <i>homedir</i> option.
- * - <kbd>string trustDb</kbd> - the file path of the web-of-trust
- * database. Use this if the trust
- * database is not in the homedir, or
- * if the database is in a directory
- * not writable by the process
- * invoking GPG (like Apache). Then
- * you can specify the path to the
- * trust database with this option
- * (/foo/bar/trustdb.gpg), and specify
- * a writable directory (like /tmp)
- * using the <i>homedir</i> option.
- * - <kbd>string binary</kbd> - the location of the GPG binary. If
- * not specified, the driver attempts
- * to auto-detect the GPG binary
- * location using a list of known
- * default locations for the current
- * operating system. The option
- * <kbd>gpgBinary</kbd> is a
- * deprecated alias for this option.
- * - <kbd>string agent</kbd> - the location of the GnuPG agent
- * binary. The gpg-agent is only
- * used for GnuPG 2.x. If not
- * specified, the engine attempts
- * to auto-detect the gpg-agent
- * binary location using a list of
- * know default locations for the
- * current operating system.
- * - <kbd>boolean debug</kbd> - whether or not to use debug mode.
- * When debug mode is on, all
- * communication to and from the GPG
- * subprocess is logged. This can be
- *
- * @param array $options optional. An array of options used to create the
- * GPG object. All options are optional and are
- * represented as key-value pairs.
- *
- * @throws Crypt_GPG_FileException if the <kbd>homedir</kbd> does not exist
- * and cannot be created. This can happen if <kbd>homedir</kbd> is
- * not specified, Crypt_GPG is run as the web user, and the web
- * user has no home directory. This exception is also thrown if any
- * of the options <kbd>publicKeyring</kbd>,
- * <kbd>privateKeyring</kbd> or <kbd>trustDb</kbd> options are
- * specified but the files do not exist or are are not readable.
- * This can happen if the user running the Crypt_GPG process (for
- * example, the Apache user) does not have permission to read the
- * files.
- *
- * @throws PEAR_Exception if the provided <kbd>binary</kbd> is invalid, or
- * if no <kbd>binary</kbd> is provided and no suitable binary could
- * be found.
- *
- * @throws PEAR_Exception if the provided <kbd>agent</kbd> is invalid, or
- * if no <kbd>agent</kbd> is provided and no suitable gpg-agent
- * cound be found.
- */
- public function __construct(array $options = array())
- {
- parent::__construct($options);
-
- $this->statusHandler = new Crypt_GPG_KeyGeneratorStatusHandler();
- $this->errorHandler = new Crypt_GPG_KeyGeneratorErrorHandler();
- }
-
- // }}}
- // {{{ setExpirationDate()
-
- /**
- * Sets the expiration date of generated keys
- *
- * @param string|integer $date either a string that may be parsed by
- * PHP's strtotime() function, or an integer
- * timestamp representing the number of seconds
- * since the UNIX epoch. This date must be at
- * least one date in the future. Keys that
- * expire in the past may not be generated. Use
- * an expiration date of 0 for keys that do not
- * expire.
- *
- * @throws InvalidArgumentException if the date is not a valid format, or
- * if the date is not at least one day in
- * the future, or if the date is greater
- * than 2038-01-19T03:14:07.
- *
- * @return Crypt_GPG_KeyGenerator the current object, for fluent interface.
- */
- public function setExpirationDate($date)
- {
- if (is_int($date) || ctype_digit(strval($date))) {
- $expirationDate = intval($date);
- } else {
- $expirationDate = strtotime($date);
- }
-
- if ($expirationDate === false) {
- throw new InvalidArgumentException(
- sprintf(
- 'Invalid expiration date format: "%s". Please use a ' .
- 'format compatible with PHP\'s strtotime().',
- $date
- )
- );
- }
-
- if ($expirationDate !== 0 && $expirationDate < time() + 86400) {
- throw new InvalidArgumentException(
- 'Expiration date must be at least a day in the future.'
- );
- }
-
- // GnuPG suffers from the 2038 bug
- if ($expirationDate > 2147483647) {
- throw new InvalidArgumentException(
- 'Expiration date must not be greater than 2038-01-19T03:14:07.'
- );
- }
-
- $this->expirationDate = $expirationDate;
-
- return $this;
- }
-
- // }}}
- // {{{ setPassphrase()
-
- /**
- * Sets the passphrase of generated keys
- *
- * @param string $passphrase the passphrase to use for generated keys. Use
- * null or an empty string for no passphrase.
- *
- * @return Crypt_GPG_KeyGenerator the current object, for fluent interface.
- */
- public function setPassphrase($passphrase)
- {
- $this->passphrase = strval($passphrase);
- return $this;
- }
-
- // }}}
- // {{{ setKeyParams()
-
- /**
- * Sets the parameters for the primary key of generated key-pairs
- *
- * @param integer $algorithm the algorithm used by the key. This should be
- * one of the Crypt_GPG_SubKey::ALGORITHM_*
- * constants.
- * @param integer $size optional. The size of the key. Different
- * algorithms have different size requirements.
- * If not specified, the default size for the
- * specified algorithm will be used. If an
- * invalid key size is used, GnuPG will do its
- * best to round it to a valid size.
- * @param integer $usage optional. A bitwise combination of key usages.
- * If not specified, the primary key will be used
- * only to sign and certify. This is the default
- * behavior of GnuPG in interactive mode. Use
- * the Crypt_GPG_SubKey::USAGE_* constants here.
- * The primary key may be used to certify even
- * if the certify usage is not specified.
- *
- * @return Crypt_GPG_KeyGenerator the current object, for fluent interface.
- */
- public function setKeyParams($algorithm, $size = 0, $usage = 0)
- {
- $apgorithm = intval($algorithm);
-
- if ($algorithm === Crypt_GPG_SubKey::ALGORITHM_ELGAMAL_ENC) {
- throw new Crypt_GPG_InvalidKeyParamsException(
- 'Primary key algorithm must be capable of signing. The ' .
- 'Elgamal algorithm can only encrypt.',
- 0,
- $algorithm,
- $size,
- $usage
- );
- }
-
- if ($size != 0) {
- $size = intval($size);
- }
-
- if ($usage != 0) {
- $usage = intval($usage);
- }
-
- $usageEncrypt = Crypt_GPG_SubKey::USAGE_ENCRYPT;
-
- if ( $algorithm === Crypt_GPG_SubKey::ALGORITHM_DSA
- && ($usage & $usageEncrypt) === $usageEncrypt
- ) {
- throw new Crypt_GPG_InvalidKeyParamsException(
- 'The DSA algorithm is not capable of encrypting. Please ' .
- 'specify a different algorithm or do not include encryption ' .
- 'as a usage for the primary key.',
- 0,
- $algorithm,
- $size,
- $usage
- );
- }
-
- $this->keyAlgorithm = $algorithm;
-
- if ($size != 0) {
- $this->keySize = $size;
- }
-
- if ($usage != 0) {
- $this->keyUsage = $usage;
- }
-
- return $this;
- }
-
- // }}}
- // {{{ setSubKeyParams()
-
- /**
- * Sets the parameters for the sub-key of generated key-pairs
- *
- * @param integer $algorithm the algorithm used by the key. This should be
- * one of the Crypt_GPG_SubKey::ALGORITHM_*
- * constants.
- * @param integer $size optional. The size of the key. Different
- * algorithms have different size requirements.
- * If not specified, the default size for the
- * specified algorithm will be used. If an
- * invalid key size is used, GnuPG will do its
- * best to round it to a valid size.
- * @param integer $usage optional. A bitwise combination of key usages.
- * If not specified, the sub-key will be used
- * only to encrypt. This is the default behavior
- * of GnuPG in interactive mode. Use the
- * Crypt_GPG_SubKey::USAGE_* constants here.
- *
- * @return Crypt_GPG_KeyGenerator the current object, for fluent interface.
- */
- public function setSubKeyParams($algorithm, $size = '', $usage = 0)
- {
- $apgorithm = intval($algorithm);
-
- if ($size != 0) {
- $size = intval($size);
- }
-
- if ($usage != 0) {
- $usage = intval($usage);
- }
-
- $usageSign = Crypt_GPG_SubKey::USAGE_SIGN;
-
- if ( $algorithm === Crypt_GPG_SubKey::ALGORITHM_ELGAMAL_ENC
- && ($usage & $usageSign) === $usageSign
- ) {
- throw new Crypt_GPG_InvalidKeyParamsException(
- 'The Elgamal algorithm is not capable of signing. Please ' .
- 'specify a different algorithm or do not include signing ' .
- 'as a usage for the sub-key.',
- 0,
- $algorithm,
- $size,
- $usage
- );
- }
-
- $usageEncrypt = Crypt_GPG_SubKey::USAGE_ENCRYPT;
-
- if ( $algorithm === Crypt_GPG_SubKey::ALGORITHM_DSA
- && ($usage & $usageEncrypt) === $usageEncrypt
- ) {
- throw new Crypt_GPG_InvalidKeyParamsException(
- 'The DSA algorithm is not capable of encrypting. Please ' .
- 'specify a different algorithm or do not include encryption ' .
- 'as a usage for the sub-key.',
- 0,
- $algorithm,
- $size,
- $usage
- );
- }
-
- $this->subKeyAlgorithm = $algorithm;
-
- if ($size != 0) {
- $this->subKeySize = $size;
- }
-
- if ($usage != 0) {
- $this->subKeyUsage = $usage;
- }
-
- return $this;
- }
-
- // }}}
- // {{{ setStatusHandler()
-
- /**
- * Sets the status handler to use for key generation
- *
- * Normally this method does not need to be used. It provides a means for
- * dependency injection.
- *
- * @param Crypt_GPG_KeyStatusHandler $handler the key status handler to
- * use.
- *
- * @return Crypt_GPG_KeyGenerator the current object, for fluent interface.
- */
- public function setStatusHandler(
- Crypt_GPG_KeyGeneratorStatusHandler $handler
- ) {
- $this->statusHandler = $handler;
- return $this;
- }
-
- // }}}
- // {{{ setErrorHandler()
-
- /**
- * Sets the error handler to use for key generation
- *
- * Normally this method does not need to be used. It provides a means for
- * dependency injection.
- *
- * @param Crypt_GPG_KeyErrorHandler $handler the key error handler to
- * use.
- *
- * @return Crypt_GPG_KeyGenerator the current object, for fluent interface.
- */
- public function setErrorHandler(
- Crypt_GPG_KeyGeneratorErrorHandler $handler
- ) {
- $this->errorHandler = $handler;
- return $this;
- }
-
- // }}}
- // {{{ generateKey()
-
- /**
- * Generates a new key-pair in the current keyring
- *
- * Secure key generation requires true random numbers, and as such can be
- * solw. If the operating system runs out of entropy, key generation will
- * block until more entropy is available.
- *
- * If quick key generation is important, a hardware entropy generator, or
- * an entropy gathering daemon may be installed. For example,
- * administrators of Debian systems may want to install the 'randomsound'
- * package.
- *
- * @param string|Crypt_GPG_UserId $name either a {@link Crypt_GPG_UserId}
- * object, or a string containing
- * the name of the user id.
- * @param string $email optional. If <i>$name</i> is
- * specified as a string, this is
- * the email address of the user id.
- * @param string $comment optional. If <i>$name</i> is
- * specified as a string, this is
- * the comment of the user id.
- *
- * @return Crypt_GPG_Key the newly generated key.
- *
- * @throws Crypt_GPG_KeyNotCreatedException if the key parameters are
- * incorrect, if an unknown error occurs during key generation, or
- * if the newly generated key is not found in the keyring.
- *
- * @throws Crypt_GPG_Exception if an unknown or unexpected error occurs.
- * Use the <kbd>debug</kbd> option and file a bug report if these
- * exceptions occur.
- */
- public function generateKey($name, $email = '', $comment = '')
- {
- $handle = uniqid('key', true);
-
- $userId = $this->getUserId($name, $email, $comment);
-
- $keyParams = array(
- 'Key-Type' => $this->keyAlgorithm,
- 'Key-Length' => $this->keySize,
- 'Key-Usage' => $this->getUsage($this->keyUsage),
- 'Subkey-Type' => $this->subKeyAlgorithm,
- 'Subkey-Length' => $this->subKeySize,
- 'Subkey-Usage' => $this->getUsage($this->subKeyUsage),
- 'Name-Real' => $userId->getName(),
- 'Handle' => $handle,
- );
-
- if ($this->expirationDate != 0) {
- // GnuPG only accepts granularity of days
- $expirationDate = date('Y-m-d', $this->expirationDate);
- $keyParams['Expire-Date'] = $expirationDate;
- }
-
- if ($this->passphrase != '') {
- $keyParams['Passphrase'] = $this->passphrase;
- }
-
- if ($userId->getEmail() != '') {
- $keyParams['Name-Email'] = $userId->getEmail();
- }
-
- if ($userId->getComment() != '') {
- $keyParams['Name-Comment'] = $userId->getComment();
- }
-
-
- $keyParamsFormatted = array();
- foreach ($keyParams as $name => $value) {
- $keyParamsFormatted[] = $name . ': ' . $value;
- }
-
- $input = implode("\n", $keyParamsFormatted) . "\n%commit\n";
-
- $statusHandler = clone $this->statusHandler;
- $statusHandler->setHandle($handle);
-
- $errorHandler = clone $this->errorHandler;
-
- $this->engine->reset();
- $this->engine->addStatusHandler(array($statusHandler, 'handle'));
- $this->engine->addErrorHandler(array($errorHandler, 'handle'));
- $this->engine->setInput($input);
- $this->engine->setOutput($output);
- $this->engine->setOperation('--gen-key', array('--batch'));
- $this->engine->run();
-
- $code = $errorHandler->getErrorCode();
- switch ($code) {
- case self::ERROR_BAD_KEY_PARAMS:
- switch ($errorHandler->getLineNumber()) {
- case 1:
- throw new Crypt_GPG_InvalidKeyParamsException(
- 'Invalid primary key algorithm specified.',
- 0,
- $this->keyAlgorithm,
- $this->keySize,
- $this->keyUsage
- );
- case 4:
- throw new Crypt_GPG_InvalidKeyParamsException(
- 'Invalid sub-key algorithm specified.',
- 0,
- $this->subKeyAlgorithm,
- $this->subKeySize,
- $this->subKeyUsage
- );
- default:
- throw new Crypt_GPG_InvalidKeyParamsException(
- 'Invalid key algorithm specified.'
- );
- }
- }
-
- $code = $this->engine->getErrorCode();
-
- switch ($code) {
- case self::ERROR_NONE:
- break;
- default:
- throw new Crypt_GPG_Exception(
- 'Unknown error generating key-pair. Please use the \'debug\' ' .
- 'option when creating the Crypt_GPG object, and file a bug ' .
- 'report at ' . self::BUG_URI,
- $code
- );
- }
-
- $code = $statusHandler->getErrorCode();
-
- switch ($code) {
- case self::ERROR_NONE:
- break;
- case self::ERROR_KEY_NOT_CREATED:
- throw new Crypt_GPG_KeyNotCreatedException(
- 'Unable to create new key-pair. Invalid key parameters. ' .
- 'Make sure the specified key algorithms and sizes are ' .
- 'correct.',
- $code
- );
- }
-
- $fingerprint = $statusHandler->getKeyFingerprint();
- $keys = $this->_getKeys($fingerprint);
-
- if (count($keys) === 0) {
- throw new Crypt_GPG_KeyNotCreatedException(
- sprintf(
- 'Newly created key "%s" not found in keyring.',
- $fingerprint
- )
- );
- }
-
- return $keys[0];
- }
-
- // }}}
- // {{{ getUsage()
-
- /**
- * Builds a GnuPG key usage string suitable for key generation
- *
- * See <b>doc/DETAILS</b> in the
- * {@link http://www.gnupg.org/download/ GPG distribution} for detailed
- * information on the key usage format.
- *
- * @param integer $usage a bitwise combination of the key usages. This is
- * a combination of the Crypt_GPG_SubKey::USAGE_*
- * constants.
- *
- * @return string the key usage string.
- */
- protected function getUsage($usage)
- {
- $map = array(
- Crypt_GPG_SubKey::USAGE_ENCRYPT => 'encrypt',
- Crypt_GPG_SubKey::USAGE_SIGN => 'sign',
- Crypt_GPG_SubKey::USAGE_CERTIFY => 'cert',
- Crypt_GPG_SubKey::USAGE_AUTHENTICATION => 'auth',
- );
-
- // cert is always used for primary keys and does not need to be
- // specified
- $usage &= ~Crypt_GPG_SubKey::USAGE_CERTIFY;
-
- $usageArray = array();
-
- foreach ($map as $key => $value) {
- if (($usage & $key) === $key) {
- $usageArray[] = $value;
- }
- }
-
- return implode(',', $usageArray);
- }
-
- // }}}
- // {{{ getUserId()
-
- /**
- * Gets a user id object from parameters
- *
- * @param string|Crypt_GPG_UserId $name either a {@link Crypt_GPG_UserId}
- * object, or a string containing
- * the name of the user id.
- * @param string $email optional. If <i>$name</i> is
- * specified as a string, this is
- * the email address of the user id.
- * @param string $comment optional. If <i>$name</i> is
- * specified as a string, this is
- * the comment of the user id.
- *
- * @return Crypt_GPG_UserId a user id object for the specified parameters.
- */
- protected function getUserId($name, $email = '', $comment = '')
- {
- if ($name instanceof Crypt_GPG_UserId) {
- $userId = $name;
- } else {
- $userId = new Crypt_GPG_UserId();
- $userId->setName($name)->setEmail($email)->setComment($comment);
- }
-
- return $userId;
- }
-
- // }}}
-}
-
-// }}}
-
-?>