Show spam aliases #
This commit is contained in:
+125
@@ -0,0 +1,125 @@
|
||||
<?php
|
||||
// +-----------------------------------------------------------------------+
|
||||
// | Copyright (c) 2002-2003 Richard Heyes |
|
||||
// | All rights reserved. |
|
||||
// | |
|
||||
// | Redistribution and use in source and binary forms, with or without |
|
||||
// | modification, are permitted provided that the following conditions |
|
||||
// | are met: |
|
||||
// | |
|
||||
// | o Redistributions of source code must retain the above copyright |
|
||||
// | notice, this list of conditions and the following disclaimer. |
|
||||
// | o Redistributions in binary form must reproduce the above copyright |
|
||||
// | notice, this list of conditions and the following disclaimer in the |
|
||||
// | documentation and/or other materials provided with the distribution.|
|
||||
// | o The names of the authors may not be used to endorse or promote |
|
||||
// | products derived from this software without specific prior written |
|
||||
// | permission. |
|
||||
// | |
|
||||
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|
||||
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|
||||
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
|
||||
// | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
|
||||
// | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
||||
// | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
||||
// | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
|
||||
// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
||||
// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
||||
// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|
||||
// | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
||||
// | |
|
||||
// +-----------------------------------------------------------------------+
|
||||
// | Author: Richard Heyes <richard@php.net> |
|
||||
// +-----------------------------------------------------------------------+
|
||||
//
|
||||
// $Id$
|
||||
|
||||
/**
|
||||
* Client implementation of various SASL mechanisms
|
||||
*
|
||||
* @author Richard Heyes <richard@php.net>
|
||||
* @access public
|
||||
* @version 1.0
|
||||
* @package Auth_SASL
|
||||
*/
|
||||
|
||||
require_once('PEAR.php');
|
||||
|
||||
class Auth_SASL
|
||||
{
|
||||
/**
|
||||
* Factory class. Returns an object of the request
|
||||
* type.
|
||||
*
|
||||
* @param string $type One of: Anonymous
|
||||
* Plain
|
||||
* CramMD5
|
||||
* DigestMD5
|
||||
* SCRAM-* (any mechanism of the SCRAM family)
|
||||
* Types are not case sensitive
|
||||
*/
|
||||
function &factory($type)
|
||||
{
|
||||
switch (strtolower($type)) {
|
||||
case 'anonymous':
|
||||
$filename = 'Auth/SASL/Anonymous.php';
|
||||
$classname = 'Auth_SASL_Anonymous';
|
||||
break;
|
||||
|
||||
case 'login':
|
||||
$filename = 'Auth/SASL/Login.php';
|
||||
$classname = 'Auth_SASL_Login';
|
||||
break;
|
||||
|
||||
case 'plain':
|
||||
$filename = 'Auth/SASL/Plain.php';
|
||||
$classname = 'Auth_SASL_Plain';
|
||||
break;
|
||||
|
||||
case 'external':
|
||||
$filename = 'Auth/SASL/External.php';
|
||||
$classname = 'Auth_SASL_External';
|
||||
break;
|
||||
|
||||
case 'crammd5':
|
||||
// $msg = 'Deprecated mechanism name. Use IANA-registered name: CRAM-MD5.';
|
||||
// trigger_error($msg, E_USER_DEPRECATED);
|
||||
case 'cram-md5':
|
||||
$filename = 'Auth/SASL/CramMD5.php';
|
||||
$classname = 'Auth_SASL_CramMD5';
|
||||
break;
|
||||
|
||||
case 'digestmd5':
|
||||
// $msg = 'Deprecated mechanism name. Use IANA-registered name: DIGEST-MD5.';
|
||||
// trigger_error($msg, E_USER_DEPRECATED);
|
||||
case 'digest-md5':
|
||||
// $msg = 'DIGEST-MD5 is a deprecated SASL mechanism as per RFC-6331. Using it could be a security risk.';
|
||||
// trigger_error($msg, E_USER_NOTICE);
|
||||
$filename = 'Auth/SASL/DigestMD5.php';
|
||||
$classname = 'Auth_SASL_DigestMD5';
|
||||
break;
|
||||
|
||||
default:
|
||||
$scram = '/^SCRAM-(.{1,9})$/i';
|
||||
if (preg_match($scram, $type, $matches))
|
||||
{
|
||||
$hash = $matches[1];
|
||||
$filename = dirname(__FILE__) .'/SASL/SCRAM.php';
|
||||
$classname = 'Auth_SASL_SCRAM';
|
||||
$parameter = $hash;
|
||||
break;
|
||||
}
|
||||
return PEAR::raiseError('Invalid SASL mechanism type');
|
||||
break;
|
||||
}
|
||||
|
||||
require_once($filename);
|
||||
if (isset($parameter))
|
||||
$obj = new $classname($parameter);
|
||||
else
|
||||
$obj = new $classname();
|
||||
return $obj;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
// +-----------------------------------------------------------------------+
|
||||
// | Copyright (c) 2002-2003 Richard Heyes |
|
||||
// | All rights reserved. |
|
||||
// | |
|
||||
// | Redistribution and use in source and binary forms, with or without |
|
||||
// | modification, are permitted provided that the following conditions |
|
||||
// | are met: |
|
||||
// | |
|
||||
// | o Redistributions of source code must retain the above copyright |
|
||||
// | notice, this list of conditions and the following disclaimer. |
|
||||
// | o Redistributions in binary form must reproduce the above copyright |
|
||||
// | notice, this list of conditions and the following disclaimer in the |
|
||||
// | documentation and/or other materials provided with the distribution.|
|
||||
// | o The names of the authors may not be used to endorse or promote |
|
||||
// | products derived from this software without specific prior written |
|
||||
// | permission. |
|
||||
// | |
|
||||
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|
||||
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|
||||
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
|
||||
// | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
|
||||
// | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
||||
// | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
||||
// | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
|
||||
// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
||||
// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
||||
// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|
||||
// | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
||||
// | |
|
||||
// +-----------------------------------------------------------------------+
|
||||
// | Author: Richard Heyes <richard@php.net> |
|
||||
// +-----------------------------------------------------------------------+
|
||||
//
|
||||
// $Id$
|
||||
|
||||
/**
|
||||
* Implmentation of ANONYMOUS SASL mechanism
|
||||
*
|
||||
* @author Richard Heyes <richard@php.net>
|
||||
* @access public
|
||||
* @version 1.0
|
||||
* @package Auth_SASL
|
||||
*/
|
||||
|
||||
require_once('Auth/SASL/Common.php');
|
||||
|
||||
class Auth_SASL_Anonymous extends Auth_SASL_Common
|
||||
{
|
||||
/**
|
||||
* Not much to do here except return the token supplied.
|
||||
* No encoding, hashing or encryption takes place for this
|
||||
* mechanism, simply one of:
|
||||
* o An email address
|
||||
* o An opaque string not containing "@" that can be interpreted
|
||||
* by the sysadmin
|
||||
* o Nothing
|
||||
*
|
||||
* We could have some logic here for the second option, but this
|
||||
* would by no means create something interpretable.
|
||||
*
|
||||
* @param string $token Optional email address or string to provide
|
||||
* as trace information.
|
||||
* @return string The unaltered input token
|
||||
*/
|
||||
function getResponse($token = '')
|
||||
{
|
||||
return $token;
|
||||
}
|
||||
}
|
||||
?>
|
||||
+105
@@ -0,0 +1,105 @@
|
||||
<?php
|
||||
// +-----------------------------------------------------------------------+
|
||||
// | Copyright (c) 2002-2003 Richard Heyes |
|
||||
// | All rights reserved. |
|
||||
// | |
|
||||
// | Redistribution and use in source and binary forms, with or without |
|
||||
// | modification, are permitted provided that the following conditions |
|
||||
// | are met: |
|
||||
// | |
|
||||
// | o Redistributions of source code must retain the above copyright |
|
||||
// | notice, this list of conditions and the following disclaimer. |
|
||||
// | o Redistributions in binary form must reproduce the above copyright |
|
||||
// | notice, this list of conditions and the following disclaimer in the |
|
||||
// | documentation and/or other materials provided with the distribution.|
|
||||
// | o The names of the authors may not be used to endorse or promote |
|
||||
// | products derived from this software without specific prior written |
|
||||
// | permission. |
|
||||
// | |
|
||||
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|
||||
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|
||||
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
|
||||
// | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
|
||||
// | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
||||
// | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
||||
// | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
|
||||
// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
||||
// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
||||
// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|
||||
// | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
||||
// | |
|
||||
// +-----------------------------------------------------------------------+
|
||||
// | Author: Richard Heyes <richard@php.net> |
|
||||
// +-----------------------------------------------------------------------+
|
||||
//
|
||||
// $Id$
|
||||
|
||||
/**
|
||||
* Common functionality to SASL mechanisms
|
||||
*
|
||||
* @author Richard Heyes <richard@php.net>
|
||||
* @access public
|
||||
* @version 1.0
|
||||
* @package Auth_SASL
|
||||
*/
|
||||
|
||||
class Auth_SASL_Common
|
||||
{
|
||||
/**
|
||||
* Function which implements HMAC MD5 digest
|
||||
*
|
||||
* @param string $key The secret key
|
||||
* @param string $data The data to hash
|
||||
* @param bool $raw_output Whether the digest is returned in binary or hexadecimal format.
|
||||
*
|
||||
* @return string The HMAC-MD5 digest
|
||||
*/
|
||||
function _HMAC_MD5($key, $data, $raw_output = FALSE)
|
||||
{
|
||||
if (strlen($key) > 64) {
|
||||
$key = pack('H32', md5($key));
|
||||
}
|
||||
|
||||
if (strlen($key) < 64) {
|
||||
$key = str_pad($key, 64, chr(0));
|
||||
}
|
||||
|
||||
$k_ipad = substr($key, 0, 64) ^ str_repeat(chr(0x36), 64);
|
||||
$k_opad = substr($key, 0, 64) ^ str_repeat(chr(0x5C), 64);
|
||||
|
||||
$inner = pack('H32', md5($k_ipad . $data));
|
||||
$digest = md5($k_opad . $inner, $raw_output);
|
||||
|
||||
return $digest;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function which implements HMAC-SHA-1 digest
|
||||
*
|
||||
* @param string $key The secret key
|
||||
* @param string $data The data to hash
|
||||
* @param bool $raw_output Whether the digest is returned in binary or hexadecimal format.
|
||||
* @return string The HMAC-SHA-1 digest
|
||||
* @author Jehan <jehan.marmottard@gmail.com>
|
||||
* @access protected
|
||||
*/
|
||||
protected function _HMAC_SHA1($key, $data, $raw_output = FALSE)
|
||||
{
|
||||
if (strlen($key) > 64) {
|
||||
$key = sha1($key, TRUE);
|
||||
}
|
||||
|
||||
if (strlen($key) < 64) {
|
||||
$key = str_pad($key, 64, chr(0));
|
||||
}
|
||||
|
||||
$k_ipad = substr($key, 0, 64) ^ str_repeat(chr(0x36), 64);
|
||||
$k_opad = substr($key, 0, 64) ^ str_repeat(chr(0x5C), 64);
|
||||
|
||||
$inner = pack('H40', sha1($k_ipad . $data));
|
||||
$digest = sha1($k_opad . $inner, $raw_output);
|
||||
|
||||
return $digest;
|
||||
}
|
||||
}
|
||||
?>
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
// +-----------------------------------------------------------------------+
|
||||
// | Copyright (c) 2002-2003 Richard Heyes |
|
||||
// | All rights reserved. |
|
||||
// | |
|
||||
// | Redistribution and use in source and binary forms, with or without |
|
||||
// | modification, are permitted provided that the following conditions |
|
||||
// | are met: |
|
||||
// | |
|
||||
// | o Redistributions of source code must retain the above copyright |
|
||||
// | notice, this list of conditions and the following disclaimer. |
|
||||
// | o Redistributions in binary form must reproduce the above copyright |
|
||||
// | notice, this list of conditions and the following disclaimer in the |
|
||||
// | documentation and/or other materials provided with the distribution.|
|
||||
// | o The names of the authors may not be used to endorse or promote |
|
||||
// | products derived from this software without specific prior written |
|
||||
// | permission. |
|
||||
// | |
|
||||
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|
||||
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|
||||
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
|
||||
// | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
|
||||
// | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
||||
// | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
||||
// | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
|
||||
// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
||||
// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
||||
// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|
||||
// | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
||||
// | |
|
||||
// +-----------------------------------------------------------------------+
|
||||
// | Author: Richard Heyes <richard@php.net> |
|
||||
// +-----------------------------------------------------------------------+
|
||||
//
|
||||
// $Id$
|
||||
|
||||
/**
|
||||
* Implmentation of CRAM-MD5 SASL mechanism
|
||||
*
|
||||
* @author Richard Heyes <richard@php.net>
|
||||
* @access public
|
||||
* @version 1.0
|
||||
* @package Auth_SASL
|
||||
*/
|
||||
|
||||
require_once('Auth/SASL/Common.php');
|
||||
|
||||
class Auth_SASL_CramMD5 extends Auth_SASL_Common
|
||||
{
|
||||
/**
|
||||
* Implements the CRAM-MD5 SASL mechanism
|
||||
* This DOES NOT base64 encode the return value,
|
||||
* you will need to do that yourself.
|
||||
*
|
||||
* @param string $user Username
|
||||
* @param string $pass Password
|
||||
* @param string $challenge The challenge supplied by the server.
|
||||
* this should be already base64_decoded.
|
||||
*
|
||||
* @return string The string to pass back to the server, of the form
|
||||
* "<user> <digest>". This is NOT base64_encoded.
|
||||
*/
|
||||
function getResponse($user, $pass, $challenge)
|
||||
{
|
||||
return $user . ' ' . $this->_HMAC_MD5($pass, $challenge);
|
||||
}
|
||||
}
|
||||
?>
|
||||
+197
@@ -0,0 +1,197 @@
|
||||
<?php
|
||||
// +-----------------------------------------------------------------------+
|
||||
// | Copyright (c) 2002-2003 Richard Heyes |
|
||||
// | All rights reserved. |
|
||||
// | |
|
||||
// | Redistribution and use in source and binary forms, with or without |
|
||||
// | modification, are permitted provided that the following conditions |
|
||||
// | are met: |
|
||||
// | |
|
||||
// | o Redistributions of source code must retain the above copyright |
|
||||
// | notice, this list of conditions and the following disclaimer. |
|
||||
// | o Redistributions in binary form must reproduce the above copyright |
|
||||
// | notice, this list of conditions and the following disclaimer in the |
|
||||
// | documentation and/or other materials provided with the distribution.|
|
||||
// | o The names of the authors may not be used to endorse or promote |
|
||||
// | products derived from this software without specific prior written |
|
||||
// | permission. |
|
||||
// | |
|
||||
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|
||||
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|
||||
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
|
||||
// | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
|
||||
// | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
||||
// | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
||||
// | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
|
||||
// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
||||
// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
||||
// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|
||||
// | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
||||
// | |
|
||||
// +-----------------------------------------------------------------------+
|
||||
// | Author: Richard Heyes <richard@php.net> |
|
||||
// +-----------------------------------------------------------------------+
|
||||
//
|
||||
// $Id$
|
||||
|
||||
/**
|
||||
* Implmentation of DIGEST-MD5 SASL mechanism
|
||||
*
|
||||
* @author Richard Heyes <richard@php.net>
|
||||
* @access public
|
||||
* @version 1.0
|
||||
* @package Auth_SASL
|
||||
*/
|
||||
|
||||
require_once('Auth/SASL/Common.php');
|
||||
|
||||
class Auth_SASL_DigestMD5 extends Auth_SASL_Common
|
||||
{
|
||||
/**
|
||||
* Provides the (main) client response for DIGEST-MD5
|
||||
* requires a few extra parameters than the other
|
||||
* mechanisms, which are unavoidable.
|
||||
*
|
||||
* @param string $authcid Authentication id (username)
|
||||
* @param string $pass Password
|
||||
* @param string $challenge The digest challenge sent by the server
|
||||
* @param string $hostname The hostname of the machine you're connecting to
|
||||
* @param string $service The servicename (eg. imap, pop, acap etc)
|
||||
* @param string $authzid Authorization id (username to proxy as)
|
||||
* @return string The digest response (NOT base64 encoded)
|
||||
* @access public
|
||||
*/
|
||||
function getResponse($authcid, $pass, $challenge, $hostname, $service, $authzid = '')
|
||||
{
|
||||
$challenge = $this->_parseChallenge($challenge);
|
||||
$authzid_string = '';
|
||||
if ($authzid != '') {
|
||||
$authzid_string = ',authzid="' . $authzid . '"';
|
||||
}
|
||||
|
||||
if (!empty($challenge)) {
|
||||
$cnonce = $this->_getCnonce();
|
||||
$digest_uri = sprintf('%s/%s', $service, $hostname);
|
||||
$response_value = $this->_getResponseValue($authcid, $pass, $challenge['realm'], $challenge['nonce'], $cnonce, $digest_uri, $authzid);
|
||||
|
||||
if ($challenge['realm']) {
|
||||
return sprintf('username="%s",realm="%s"' . $authzid_string .
|
||||
',nonce="%s",cnonce="%s",nc=00000001,qop=auth,digest-uri="%s",response=%s,maxbuf=%d', $authcid, $challenge['realm'], $challenge['nonce'], $cnonce, $digest_uri, $response_value, $challenge['maxbuf']);
|
||||
} else {
|
||||
return sprintf('username="%s"' . $authzid_string . ',nonce="%s",cnonce="%s",nc=00000001,qop=auth,digest-uri="%s",response=%s,maxbuf=%d', $authcid, $challenge['nonce'], $cnonce, $digest_uri, $response_value, $challenge['maxbuf']);
|
||||
}
|
||||
} else {
|
||||
return PEAR::raiseError('Invalid digest challenge');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses and verifies the digest challenge*
|
||||
*
|
||||
* @param string $challenge The digest challenge
|
||||
* @return array The parsed challenge as an assoc
|
||||
* array in the form "directive => value".
|
||||
* @access private
|
||||
*/
|
||||
function _parseChallenge($challenge)
|
||||
{
|
||||
$tokens = array();
|
||||
while (preg_match('/^([a-z-]+)=("[^"]+(?<!\\\)"|[^,]+)/i', $challenge, $matches)) {
|
||||
|
||||
// Ignore these as per rfc2831
|
||||
if ($matches[1] == 'opaque' OR $matches[1] == 'domain') {
|
||||
$challenge = substr($challenge, strlen($matches[0]) + 1);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Allowed multiple "realm" and "auth-param"
|
||||
if (!empty($tokens[$matches[1]]) AND ($matches[1] == 'realm' OR $matches[1] == 'auth-param')) {
|
||||
if (is_array($tokens[$matches[1]])) {
|
||||
$tokens[$matches[1]][] = preg_replace('/^"(.*)"$/', '\\1', $matches[2]);
|
||||
} else {
|
||||
$tokens[$matches[1]] = array($tokens[$matches[1]], preg_replace('/^"(.*)"$/', '\\1', $matches[2]));
|
||||
}
|
||||
|
||||
// Any other multiple instance = failure
|
||||
} elseif (!empty($tokens[$matches[1]])) {
|
||||
$tokens = array();
|
||||
break;
|
||||
|
||||
} else {
|
||||
$tokens[$matches[1]] = preg_replace('/^"(.*)"$/', '\\1', $matches[2]);
|
||||
}
|
||||
|
||||
// Remove the just parsed directive from the challenge
|
||||
$challenge = substr($challenge, strlen($matches[0]) + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Defaults and required directives
|
||||
*/
|
||||
// Realm
|
||||
if (empty($tokens['realm'])) {
|
||||
$tokens['realm'] = "";
|
||||
}
|
||||
|
||||
// Maxbuf
|
||||
if (empty($tokens['maxbuf'])) {
|
||||
$tokens['maxbuf'] = 65536;
|
||||
}
|
||||
|
||||
// Required: nonce, algorithm
|
||||
if (empty($tokens['nonce']) OR empty($tokens['algorithm'])) {
|
||||
return array();
|
||||
}
|
||||
|
||||
return $tokens;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the response= part of the digest response
|
||||
*
|
||||
* @param string $authcid Authentication id (username)
|
||||
* @param string $pass Password
|
||||
* @param string $realm Realm as provided by the server
|
||||
* @param string $nonce Nonce as provided by the server
|
||||
* @param string $cnonce Client nonce
|
||||
* @param string $digest_uri The digest-uri= value part of the response
|
||||
* @param string $authzid Authorization id
|
||||
* @return string The response= part of the digest response
|
||||
* @access private
|
||||
*/
|
||||
function _getResponseValue($authcid, $pass, $realm, $nonce, $cnonce, $digest_uri, $authzid = '')
|
||||
{
|
||||
if ($authzid == '') {
|
||||
$A1 = sprintf('%s:%s:%s', pack('H32', md5(sprintf('%s:%s:%s', $authcid, $realm, $pass))), $nonce, $cnonce);
|
||||
} else {
|
||||
$A1 = sprintf('%s:%s:%s:%s', pack('H32', md5(sprintf('%s:%s:%s', $authcid, $realm, $pass))), $nonce, $cnonce, $authzid);
|
||||
}
|
||||
$A2 = 'AUTHENTICATE:' . $digest_uri;
|
||||
return md5(sprintf('%s:%s:00000001:%s:auth:%s', md5($A1), $nonce, $cnonce, md5($A2)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the client nonce for the response
|
||||
*
|
||||
* @return string The cnonce value
|
||||
* @access private
|
||||
*/
|
||||
function _getCnonce()
|
||||
{
|
||||
if (@file_exists('/dev/urandom') && $fd = @fopen('/dev/urandom', 'r')) {
|
||||
return base64_encode(fread($fd, 32));
|
||||
|
||||
} elseif (@file_exists('/dev/random') && $fd = @fopen('/dev/random', 'r')) {
|
||||
return base64_encode(fread($fd, 32));
|
||||
|
||||
} else {
|
||||
$str = '';
|
||||
for ($i=0; $i<32; $i++) {
|
||||
$str .= chr(mt_rand(0, 255));
|
||||
}
|
||||
|
||||
return base64_encode($str);
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
// +-----------------------------------------------------------------------+
|
||||
// | Copyright (c) 2008 Christoph Schulz |
|
||||
// | All rights reserved. |
|
||||
// | |
|
||||
// | Redistribution and use in source and binary forms, with or without |
|
||||
// | modification, are permitted provided that the following conditions |
|
||||
// | are met: |
|
||||
// | |
|
||||
// | o Redistributions of source code must retain the above copyright |
|
||||
// | notice, this list of conditions and the following disclaimer. |
|
||||
// | o Redistributions in binary form must reproduce the above copyright |
|
||||
// | notice, this list of conditions and the following disclaimer in the |
|
||||
// | documentation and/or other materials provided with the distribution.|
|
||||
// | o The names of the authors may not be used to endorse or promote |
|
||||
// | products derived from this software without specific prior written |
|
||||
// | permission. |
|
||||
// | |
|
||||
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|
||||
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|
||||
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
|
||||
// | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
|
||||
// | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
||||
// | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
||||
// | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
|
||||
// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
||||
// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
||||
// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|
||||
// | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
||||
// | |
|
||||
// +-----------------------------------------------------------------------+
|
||||
// | Author: Christoph Schulz <develop@kristov.de> |
|
||||
// +-----------------------------------------------------------------------+
|
||||
//
|
||||
// $Id$
|
||||
|
||||
/**
|
||||
* Implmentation of EXTERNAL SASL mechanism
|
||||
*
|
||||
* @author Christoph Schulz <develop@kristov.de>
|
||||
* @access public
|
||||
* @version 1.0.3
|
||||
* @package Auth_SASL
|
||||
*/
|
||||
|
||||
require_once('Auth/SASL/Common.php');
|
||||
|
||||
class Auth_SASL_External extends Auth_SASL_Common
|
||||
{
|
||||
/**
|
||||
* Returns EXTERNAL response
|
||||
*
|
||||
* @param string $authcid Authentication id (username)
|
||||
* @param string $pass Password
|
||||
* @param string $authzid Autorization id
|
||||
* @return string EXTERNAL Response
|
||||
*/
|
||||
function getResponse($authcid, $pass, $authzid = '')
|
||||
{
|
||||
return $authzid;
|
||||
}
|
||||
}
|
||||
?>
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
// +-----------------------------------------------------------------------+
|
||||
// | Copyright (c) 2002-2003 Richard Heyes |
|
||||
// | All rights reserved. |
|
||||
// | |
|
||||
// | Redistribution and use in source and binary forms, with or without |
|
||||
// | modification, are permitted provided that the following conditions |
|
||||
// | are met: |
|
||||
// | |
|
||||
// | o Redistributions of source code must retain the above copyright |
|
||||
// | notice, this list of conditions and the following disclaimer. |
|
||||
// | o Redistributions in binary form must reproduce the above copyright |
|
||||
// | notice, this list of conditions and the following disclaimer in the |
|
||||
// | documentation and/or other materials provided with the distribution.|
|
||||
// | o The names of the authors may not be used to endorse or promote |
|
||||
// | products derived from this software without specific prior written |
|
||||
// | permission. |
|
||||
// | |
|
||||
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|
||||
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|
||||
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
|
||||
// | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
|
||||
// | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
||||
// | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
||||
// | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
|
||||
// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
||||
// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
||||
// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|
||||
// | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
||||
// | |
|
||||
// +-----------------------------------------------------------------------+
|
||||
// | Author: Richard Heyes <richard@php.net> |
|
||||
// +-----------------------------------------------------------------------+
|
||||
//
|
||||
// $Id$
|
||||
|
||||
/**
|
||||
* This is technically not a SASL mechanism, however
|
||||
* it's used by Net_Sieve, Net_Cyrus and potentially
|
||||
* other protocols , so here is a good place to abstract
|
||||
* it.
|
||||
*
|
||||
* @author Richard Heyes <richard@php.net>
|
||||
* @access public
|
||||
* @version 1.0
|
||||
* @package Auth_SASL
|
||||
*/
|
||||
|
||||
require_once('Auth/SASL/Common.php');
|
||||
|
||||
class Auth_SASL_Login extends Auth_SASL_Common
|
||||
{
|
||||
/**
|
||||
* Pseudo SASL LOGIN mechanism
|
||||
*
|
||||
* @param string $user Username
|
||||
* @param string $pass Password
|
||||
* @return string LOGIN string
|
||||
*/
|
||||
function getResponse($user, $pass)
|
||||
{
|
||||
return sprintf('LOGIN %s %s', $user, $pass);
|
||||
}
|
||||
}
|
||||
?>
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
// +-----------------------------------------------------------------------+
|
||||
// | Copyright (c) 2002-2003 Richard Heyes |
|
||||
// | All rights reserved. |
|
||||
// | |
|
||||
// | Redistribution and use in source and binary forms, with or without |
|
||||
// | modification, are permitted provided that the following conditions |
|
||||
// | are met: |
|
||||
// | |
|
||||
// | o Redistributions of source code must retain the above copyright |
|
||||
// | notice, this list of conditions and the following disclaimer. |
|
||||
// | o Redistributions in binary form must reproduce the above copyright |
|
||||
// | notice, this list of conditions and the following disclaimer in the |
|
||||
// | documentation and/or other materials provided with the distribution.|
|
||||
// | o The names of the authors may not be used to endorse or promote |
|
||||
// | products derived from this software without specific prior written |
|
||||
// | permission. |
|
||||
// | |
|
||||
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|
||||
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|
||||
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
|
||||
// | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
|
||||
// | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
||||
// | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
||||
// | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
|
||||
// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
||||
// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
||||
// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|
||||
// | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
||||
// | |
|
||||
// +-----------------------------------------------------------------------+
|
||||
// | Author: Richard Heyes <richard@php.net> |
|
||||
// +-----------------------------------------------------------------------+
|
||||
//
|
||||
// $Id$
|
||||
|
||||
/**
|
||||
* Implmentation of PLAIN SASL mechanism
|
||||
*
|
||||
* @author Richard Heyes <richard@php.net>
|
||||
* @access public
|
||||
* @version 1.0
|
||||
* @package Auth_SASL
|
||||
*/
|
||||
|
||||
require_once('Auth/SASL/Common.php');
|
||||
|
||||
class Auth_SASL_Plain extends Auth_SASL_Common
|
||||
{
|
||||
/**
|
||||
* Returns PLAIN response
|
||||
*
|
||||
* @param string $authcid Authentication id (username)
|
||||
* @param string $pass Password
|
||||
* @param string $authzid Autorization id
|
||||
* @return string PLAIN Response
|
||||
*/
|
||||
function getResponse($authcid, $pass, $authzid = '')
|
||||
{
|
||||
return $authzid . chr(0) . $authcid . chr(0) . $pass;
|
||||
}
|
||||
}
|
||||
?>
|
||||
+306
@@ -0,0 +1,306 @@
|
||||
<?php
|
||||
// +-----------------------------------------------------------------------+
|
||||
// | Copyright (c) 2011 Jehan |
|
||||
// | All rights reserved. |
|
||||
// | |
|
||||
// | Redistribution and use in source and binary forms, with or without |
|
||||
// | modification, are permitted provided that the following conditions |
|
||||
// | are met: |
|
||||
// | |
|
||||
// | o Redistributions of source code must retain the above copyright |
|
||||
// | notice, this list of conditions and the following disclaimer. |
|
||||
// | o Redistributions in binary form must reproduce the above copyright |
|
||||
// | notice, this list of conditions and the following disclaimer in the |
|
||||
// | documentation and/or other materials provided with the distribution.|
|
||||
// | o The names of the authors may not be used to endorse or promote |
|
||||
// | products derived from this software without specific prior written |
|
||||
// | permission. |
|
||||
// | |
|
||||
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|
||||
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|
||||
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
|
||||
// | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
|
||||
// | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
||||
// | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
||||
// | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
|
||||
// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
||||
// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
||||
// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|
||||
// | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
||||
// | |
|
||||
// +-----------------------------------------------------------------------+
|
||||
// | Author: Jehan <jehan.marmottard@gmail.com |
|
||||
// +-----------------------------------------------------------------------+
|
||||
//
|
||||
// $Id$
|
||||
|
||||
/**
|
||||
* Implementation of SCRAM-* SASL mechanisms.
|
||||
* SCRAM mechanisms have 3 main steps (initial response, response to the server challenge, then server signature
|
||||
* verification) which keep state-awareness. Therefore a single class instanciation must be done and reused for the whole
|
||||
* authentication process.
|
||||
*
|
||||
* @author Jehan <jehan.marmottard@gmail.com>
|
||||
* @access public
|
||||
* @version 1.0
|
||||
* @package Auth_SASL
|
||||
*/
|
||||
|
||||
require_once('Auth/SASL/Common.php');
|
||||
|
||||
class Auth_SASL_SCRAM extends Auth_SASL_Common
|
||||
{
|
||||
/**
|
||||
* Construct a SCRAM-H client where 'H' is a cryptographic hash function.
|
||||
*
|
||||
* @param string $hash The name cryptographic hash function 'H' as registered by IANA in the "Hash Function Textual
|
||||
* Names" registry.
|
||||
* @link http://www.iana.org/assignments/hash-function-text-names/hash-function-text-names.xml "Hash Function Textual
|
||||
* Names"
|
||||
* format of core PHP hash function.
|
||||
* @access public
|
||||
*/
|
||||
function __construct($hash)
|
||||
{
|
||||
// Though I could be strict, I will actually also accept the naming used in the PHP core hash framework.
|
||||
// For instance "sha1" is accepted, while the registered hash name should be "SHA-1".
|
||||
$hash = strtolower($hash);
|
||||
$hashes = array('md2' => 'md2',
|
||||
'md5' => 'md5',
|
||||
'sha-1' => 'sha1',
|
||||
'sha1' => 'sha1',
|
||||
'sha-224' > 'sha224',
|
||||
'sha224' > 'sha224',
|
||||
'sha-256' => 'sha256',
|
||||
'sha256' => 'sha256',
|
||||
'sha-384' => 'sha384',
|
||||
'sha384' => 'sha384',
|
||||
'sha-512' => 'sha512',
|
||||
'sha512' => 'sha512');
|
||||
if (function_exists('hash_hmac') && isset($hashes[$hash]))
|
||||
{
|
||||
$this->hash = create_function('$data', 'return hash("' . $hashes[$hash] . '", $data, TRUE);');
|
||||
$this->hmac = create_function('$key,$str,$raw', 'return hash_hmac("' . $hashes[$hash] . '", $str, $key, $raw);');
|
||||
}
|
||||
elseif ($hash == 'md5')
|
||||
{
|
||||
$this->hash = create_function('$data', 'return md5($data, true);');
|
||||
$this->hmac = array($this, '_HMAC_MD5');
|
||||
}
|
||||
elseif (in_array($hash, array('sha1', 'sha-1')))
|
||||
{
|
||||
$this->hash = create_function('$data', 'return sha1($data, true);');
|
||||
$this->hmac = array($this, '_HMAC_SHA1');
|
||||
}
|
||||
else
|
||||
return PEAR::raiseError('Invalid SASL mechanism type');
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the (main) client response for SCRAM-H.
|
||||
*
|
||||
* @param string $authcid Authentication id (username)
|
||||
* @param string $pass Password
|
||||
* @param string $challenge The challenge sent by the server.
|
||||
* If the challenge is NULL or an empty string, the result will be the "initial response".
|
||||
* @param string $authzid Authorization id (username to proxy as)
|
||||
* @return string|false The response (binary, NOT base64 encoded)
|
||||
* @access public
|
||||
*/
|
||||
public function getResponse($authcid, $pass, $challenge = NULL, $authzid = NULL)
|
||||
{
|
||||
$authcid = $this->_formatName($authcid);
|
||||
if (empty($authcid))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!empty($authzid))
|
||||
{
|
||||
$authzid = $this->_formatName($authzid);
|
||||
if (empty($authzid))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($challenge))
|
||||
{
|
||||
return $this->_generateInitialResponse($authcid, $authzid);
|
||||
}
|
||||
else
|
||||
{
|
||||
return $this->_generateResponse($challenge, $pass);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare a name for inclusion in a SCRAM response.
|
||||
*
|
||||
* @param string $username a name to be prepared.
|
||||
* @return string the reformated name.
|
||||
* @access private
|
||||
*/
|
||||
private function _formatName($username)
|
||||
{
|
||||
// TODO: prepare through the SASLprep profile of the stringprep algorithm.
|
||||
// See RFC-4013.
|
||||
|
||||
$username = str_replace('=', '=3D', $username);
|
||||
$username = str_replace(',', '=2C', $username);
|
||||
return $username;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the initial response which can be either sent directly in the first message or as a response to an empty
|
||||
* server challenge.
|
||||
*
|
||||
* @param string $authcid Prepared authentication identity.
|
||||
* @param string $authzid Prepared authorization identity.
|
||||
* @return string The SCRAM response to send.
|
||||
* @access private
|
||||
*/
|
||||
private function _generateInitialResponse($authcid, $authzid)
|
||||
{
|
||||
$init_rep = '';
|
||||
$gs2_cbind_flag = 'n,'; // TODO: support channel binding.
|
||||
$this->gs2_header = $gs2_cbind_flag . (!empty($authzid)? 'a=' . $authzid : '') . ',';
|
||||
|
||||
// I must generate a client nonce and "save" it for later comparison on second response.
|
||||
$this->cnonce = $this->_getCnonce();
|
||||
// XXX: in the future, when mandatory and/or optional extensions are defined in any updated RFC,
|
||||
// this message can be updated.
|
||||
$this->first_message_bare = 'n=' . $authcid . ',r=' . $this->cnonce;
|
||||
return $this->gs2_header . $this->first_message_bare;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses and verifies a non-empty SCRAM challenge.
|
||||
*
|
||||
* @param string $challenge The SCRAM challenge
|
||||
* @return string|false The response to send; false in case of wrong challenge or if an initial response has not
|
||||
* been generated first.
|
||||
* @access private
|
||||
*/
|
||||
private function _generateResponse($challenge, $password)
|
||||
{
|
||||
// XXX: as I don't support mandatory extension, I would fail on them.
|
||||
// And I simply ignore any optional extension.
|
||||
$server_message_regexp = "#^r=([\x21-\x2B\x2D-\x7E]+),s=((?:[A-Za-z0-9/+]{4})*(?:[A-Za-z0-9]{3}=|[A-Xa-z0-9]{2}==)?),i=([0-9]*)(,[A-Za-z]=[^,])*$#";
|
||||
if (!isset($this->cnonce, $this->gs2_header)
|
||||
|| !preg_match($server_message_regexp, $challenge, $matches))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
$nonce = $matches[1];
|
||||
$salt = base64_decode($matches[2]);
|
||||
if (!$salt)
|
||||
{
|
||||
// Invalid Base64.
|
||||
return false;
|
||||
}
|
||||
$i = intval($matches[3]);
|
||||
|
||||
$cnonce = substr($nonce, 0, strlen($this->cnonce));
|
||||
if ($cnonce <> $this->cnonce)
|
||||
{
|
||||
// Invalid challenge! Are we under attack?
|
||||
return false;
|
||||
}
|
||||
|
||||
$channel_binding = 'c=' . base64_encode($this->gs2_header); // TODO: support channel binding.
|
||||
$final_message = $channel_binding . ',r=' . $nonce; // XXX: no extension.
|
||||
|
||||
// TODO: $password = $this->normalize($password); // SASLprep profile of stringprep.
|
||||
$saltedPassword = $this->hi($password, $salt, $i);
|
||||
$this->saltedPassword = $saltedPassword;
|
||||
$clientKey = call_user_func($this->hmac, $saltedPassword, "Client Key", TRUE);
|
||||
$storedKey = call_user_func($this->hash, $clientKey, TRUE);
|
||||
$authMessage = $this->first_message_bare . ',' . $challenge . ',' . $final_message;
|
||||
$this->authMessage = $authMessage;
|
||||
$clientSignature = call_user_func($this->hmac, $storedKey, $authMessage, TRUE);
|
||||
$clientProof = $clientKey ^ $clientSignature;
|
||||
$proof = ',p=' . base64_encode($clientProof);
|
||||
|
||||
return $final_message . $proof;
|
||||
}
|
||||
|
||||
/**
|
||||
* SCRAM has also a server verification step. On a successful outcome, it will send additional data which must
|
||||
* absolutely be checked against this function. If this fails, the entity which we are communicating with is probably
|
||||
* not the server as it has not access to your ServerKey.
|
||||
*
|
||||
* @param string $data The additional data sent along a successful outcome.
|
||||
* @return bool Whether the server has been authenticated.
|
||||
* If false, the client must close the connection and consider to be under a MITM attack.
|
||||
* @access public
|
||||
*/
|
||||
public function processOutcome($data)
|
||||
{
|
||||
$verifier_regexp = '#^v=((?:[A-Za-z0-9/+]{4})*(?:[A-Za-z0-9]{3}=|[A-Xa-z0-9]{2}==)?)$#';
|
||||
if (!isset($this->saltedPassword, $this->authMessage)
|
||||
|| !preg_match($verifier_regexp, $data, $matches))
|
||||
{
|
||||
// This cannot be an outcome, you never sent the challenge's response.
|
||||
return false;
|
||||
}
|
||||
|
||||
$verifier = $matches[1];
|
||||
$proposed_serverSignature = base64_decode($verifier);
|
||||
$serverKey = call_user_func($this->hmac, $this->saltedPassword, "Server Key", true);
|
||||
$serverSignature = call_user_func($this->hmac, $serverKey, $this->authMessage, TRUE);
|
||||
return ($proposed_serverSignature === $serverSignature);
|
||||
}
|
||||
|
||||
/**
|
||||
* Hi() call, which is essentially PBKDF2 (RFC-2898) with HMAC-H() as the pseudorandom function.
|
||||
*
|
||||
* @param string $str The string to hash.
|
||||
* @param string $hash The hash value.
|
||||
* @param int $i The iteration count.
|
||||
* @access private
|
||||
*/
|
||||
private function hi($str, $salt, $i)
|
||||
{
|
||||
$int1 = "\0\0\0\1";
|
||||
$ui = call_user_func($this->hmac, $str, $salt . $int1, true);
|
||||
$result = $ui;
|
||||
for ($k = 1; $k < $i; $k++)
|
||||
{
|
||||
$ui = call_user_func($this->hmac, $str, $ui, true);
|
||||
$result = $result ^ $ui;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates the client nonce for the response
|
||||
*
|
||||
* @return string The cnonce value
|
||||
* @access private
|
||||
* @author Richard Heyes <richard@php.net>
|
||||
*/
|
||||
private function _getCnonce()
|
||||
{
|
||||
// TODO: I reused the nonce function from the DigestMD5 class.
|
||||
// I should probably make this a protected function in Common.
|
||||
if (@file_exists('/dev/urandom') && $fd = @fopen('/dev/urandom', 'r')) {
|
||||
return base64_encode(fread($fd, 32));
|
||||
|
||||
} elseif (@file_exists('/dev/random') && $fd = @fopen('/dev/random', 'r')) {
|
||||
return base64_encode(fread($fd, 32));
|
||||
|
||||
} else {
|
||||
$str = '';
|
||||
for ($i=0; $i<32; $i++) {
|
||||
$str .= chr(mt_rand(0, 255));
|
||||
}
|
||||
|
||||
return base64_encode($str);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
||||
+1305
File diff suppressed because it is too large
Load Diff
Generated
Vendored
+142
@@ -0,0 +1,142 @@
|
||||
<?php
|
||||
|
||||
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
|
||||
|
||||
/**
|
||||
* This file is part of the PEAR Console_CommandLine package.
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* LICENSE: This source file is subject to the MIT license that is available
|
||||
* through the world-wide-web at the following URI:
|
||||
* http://opensource.org/licenses/mit-license.php
|
||||
*
|
||||
* @category Console
|
||||
* @package Console_CommandLine
|
||||
* @author David JEAN LOUIS <izimobil@gmail.com>
|
||||
* @copyright 2007 David JEAN LOUIS
|
||||
* @license http://opensource.org/licenses/mit-license.php MIT License
|
||||
* @version CVS: $Id$
|
||||
* @link http://pear.php.net/package/Console_CommandLine
|
||||
* @since File available since release 0.1.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
/**
|
||||
* Class that represent an option action.
|
||||
*
|
||||
* @category Console
|
||||
* @package Console_CommandLine
|
||||
* @author David JEAN LOUIS <izimobil@gmail.com>
|
||||
* @copyright 2007 David JEAN LOUIS
|
||||
* @license http://opensource.org/licenses/mit-license.php MIT License
|
||||
* @version Release: 1.2.2
|
||||
* @link http://pear.php.net/package/Console_CommandLine
|
||||
* @since Class available since release 0.1.0
|
||||
*/
|
||||
abstract class Console_CommandLine_Action
|
||||
{
|
||||
// Properties {{{
|
||||
|
||||
/**
|
||||
* A reference to the result instance.
|
||||
*
|
||||
* @var Console_CommandLine_Result $result The result instance
|
||||
*/
|
||||
protected $result;
|
||||
|
||||
/**
|
||||
* A reference to the option instance.
|
||||
*
|
||||
* @var Console_CommandLine_Option $option The action option
|
||||
*/
|
||||
protected $option;
|
||||
|
||||
/**
|
||||
* A reference to the parser instance.
|
||||
*
|
||||
* @var Console_CommandLine $parser The parser
|
||||
*/
|
||||
protected $parser;
|
||||
|
||||
// }}}
|
||||
// __construct() {{{
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param Console_CommandLine_Result $result The result instance
|
||||
* @param Console_CommandLine_Option $option The action option
|
||||
* @param Console_CommandLine $parser The current parser
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($result, $option, $parser)
|
||||
{
|
||||
$this->result = $result;
|
||||
$this->option = $option;
|
||||
$this->parser = $parser;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// getResult() {{{
|
||||
|
||||
/**
|
||||
* Convenience method to retrieve the value of result->options[name].
|
||||
*
|
||||
* @return mixed The result value or null
|
||||
*/
|
||||
public function getResult()
|
||||
{
|
||||
if (isset($this->result->options[$this->option->name])) {
|
||||
return $this->result->options[$this->option->name];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// format() {{{
|
||||
|
||||
/**
|
||||
* Allow a value to be pre-formatted prior to being used in a choices test.
|
||||
* Setting $value to the new format will keep the formatting.
|
||||
*
|
||||
* @param mixed &$value The value to format
|
||||
*
|
||||
* @return mixed The formatted value
|
||||
*/
|
||||
public function format(&$value)
|
||||
{
|
||||
return $value;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// setResult() {{{
|
||||
|
||||
/**
|
||||
* Convenience method to assign the result->options[name] value.
|
||||
*
|
||||
* @param mixed $result The result value
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setResult($result)
|
||||
{
|
||||
$this->result->options[$this->option->name] = $result;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// execute() {{{
|
||||
|
||||
/**
|
||||
* Executes the action with the value entered by the user.
|
||||
* All children actions must implement this method.
|
||||
*
|
||||
* @param mixed $value The option value
|
||||
* @param array $params An optional array of parameters
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
abstract public function execute($value = false, $params = array());
|
||||
// }}}
|
||||
}
|
||||
Generated
Vendored
+80
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
|
||||
|
||||
/**
|
||||
* This file is part of the PEAR Console_CommandLine package.
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* LICENSE: This source file is subject to the MIT license that is available
|
||||
* through the world-wide-web at the following URI:
|
||||
* http://opensource.org/licenses/mit-license.php
|
||||
*
|
||||
* @category Console
|
||||
* @package Console_CommandLine
|
||||
* @author David JEAN LOUIS <izimobil@gmail.com>
|
||||
* @copyright 2007 David JEAN LOUIS
|
||||
* @license http://opensource.org/licenses/mit-license.php MIT License
|
||||
* @version CVS: $Id$
|
||||
* @link http://pear.php.net/package/Console_CommandLine
|
||||
* @since File available since release 0.1.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
/**
|
||||
* Required by this class.
|
||||
*/
|
||||
require_once 'Console/CommandLine/Action.php';
|
||||
|
||||
/**
|
||||
* Class that represent the Callback action.
|
||||
*
|
||||
* The result option array entry value is set to the return value of the
|
||||
* callback defined in the option.
|
||||
*
|
||||
* There are two steps to defining a callback option:
|
||||
* - define the option itself using the callback action
|
||||
* - write the callback; this is a function (or method) that takes five
|
||||
* arguments, as described below.
|
||||
*
|
||||
* All callbacks are called as follows:
|
||||
* <code>
|
||||
* callable_func(
|
||||
* $value, // the value of the option
|
||||
* $option_instance, // the option instance
|
||||
* $result_instance, // the result instance
|
||||
* $parser_instance, // the parser instance
|
||||
* $params // an array of params as specified in the option
|
||||
* );
|
||||
* </code>
|
||||
* and *must* return the option value.
|
||||
*
|
||||
* @category Console
|
||||
* @package Console_CommandLine
|
||||
* @author David JEAN LOUIS <izimobil@gmail.com>
|
||||
* @copyright 2007 David JEAN LOUIS
|
||||
* @license http://opensource.org/licenses/mit-license.php MIT License
|
||||
* @version Release: 1.2.2
|
||||
* @link http://pear.php.net/package/Console_CommandLine
|
||||
* @since Class available since release 0.1.0
|
||||
*/
|
||||
class Console_CommandLine_Action_Callback extends Console_CommandLine_Action
|
||||
{
|
||||
// execute() {{{
|
||||
|
||||
/**
|
||||
* Executes the action with the value entered by the user.
|
||||
*
|
||||
* @param mixed $value The value of the option
|
||||
* @param array $params An optional array of parameters
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function execute($value = false, $params = array())
|
||||
{
|
||||
$this->setResult(call_user_func($this->option->callback, $value,
|
||||
$this->option, $this->result, $this->parser, $params));
|
||||
}
|
||||
// }}}
|
||||
}
|
||||
Generated
Vendored
+86
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
|
||||
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
|
||||
|
||||
/**
|
||||
* This file is part of the PEAR Console_CommandLine package.
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* LICENSE: This source file is subject to the MIT license that is available
|
||||
* through the world-wide-web at the following URI:
|
||||
* http://opensource.org/licenses/mit-license.php
|
||||
*
|
||||
* @category Console
|
||||
* @package Console_CommandLine
|
||||
* @author David JEAN LOUIS <izimobil@gmail.com>
|
||||
* @copyright 2007 David JEAN LOUIS
|
||||
* @license http://opensource.org/licenses/mit-license.php MIT License
|
||||
* @version CVS: $Id$
|
||||
* @link http://pear.php.net/package/Console_CommandLine
|
||||
* @since File available since release 0.1.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
/**
|
||||
* Required by this class.
|
||||
*/
|
||||
require_once 'Console/CommandLine/Action.php';
|
||||
|
||||
/**
|
||||
* Class that represent the Version action.
|
||||
*
|
||||
* The execute methode add 1 to the value of the result option array entry.
|
||||
* The value is incremented each time the option is found, for example
|
||||
* with an option defined like that:
|
||||
*
|
||||
* <code>
|
||||
* $parser->addOption(
|
||||
* 'verbose',
|
||||
* array(
|
||||
* 'short_name' => '-v',
|
||||
* 'action' => 'Counter'
|
||||
* )
|
||||
* );
|
||||
* </code>
|
||||
* If the user type:
|
||||
* <code>
|
||||
* $ script.php -v -v -v
|
||||
* </code>
|
||||
* or:
|
||||
* <code>
|
||||
* $ script.php -vvv
|
||||
* </code>
|
||||
* the verbose variable will be set to to 3.
|
||||
*
|
||||
* @category Console
|
||||
* @package Console_CommandLine
|
||||
* @author David JEAN LOUIS <izimobil@gmail.com>
|
||||
* @copyright 2007 David JEAN LOUIS
|
||||
* @license http://opensource.org/licenses/mit-license.php MIT License
|
||||
* @version Release: 1.2.2
|
||||
* @link http://pear.php.net/package/Console_CommandLine
|
||||
* @since Class available since release 0.1.0
|
||||
*/
|
||||
class Console_CommandLine_Action_Counter extends Console_CommandLine_Action
|
||||
{
|
||||
// execute() {{{
|
||||
|
||||
/**
|
||||
* Executes the action with the value entered by the user.
|
||||
*
|
||||
* @param mixed $value The option value
|
||||
* @param array $params An optional array of parameters
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function execute($value = false, $params = array())
|
||||
{
|
||||
$result = $this->getResult();
|
||||
if ($result === null) {
|
||||
$result = 0;
|
||||
}
|
||||
$this->setResult(++$result);
|
||||
}
|
||||
// }}}
|
||||
}
|
||||
Generated
Vendored
+60
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
|
||||
|
||||
/**
|
||||
* This file is part of the PEAR Console_CommandLine package.
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* LICENSE: This source file is subject to the MIT license that is available
|
||||
* through the world-wide-web at the following URI:
|
||||
* http://opensource.org/licenses/mit-license.php
|
||||
*
|
||||
* @category Console
|
||||
* @package Console_CommandLine
|
||||
* @author David JEAN LOUIS <izimobil@gmail.com>
|
||||
* @copyright 2007 David JEAN LOUIS
|
||||
* @license http://opensource.org/licenses/mit-license.php MIT License
|
||||
* @version CVS: $Id$
|
||||
* @link http://pear.php.net/package/Console_CommandLine
|
||||
* @since File available since release 0.1.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
/**
|
||||
* Required by this class.
|
||||
*/
|
||||
require_once 'Console/CommandLine/Action.php';
|
||||
|
||||
/**
|
||||
* Class that represent the Help action, a special action that displays the
|
||||
* help message, telling the user how to use the program.
|
||||
*
|
||||
* @category Console
|
||||
* @package Console_CommandLine
|
||||
* @author David JEAN LOUIS <izimobil@gmail.com>
|
||||
* @copyright 2007 David JEAN LOUIS
|
||||
* @license http://opensource.org/licenses/mit-license.php MIT License
|
||||
* @version Release: 1.2.2
|
||||
* @link http://pear.php.net/package/Console_CommandLine
|
||||
* @since Class available since release 0.1.0
|
||||
*/
|
||||
class Console_CommandLine_Action_Help extends Console_CommandLine_Action
|
||||
{
|
||||
// execute() {{{
|
||||
|
||||
/**
|
||||
* Executes the action with the value entered by the user.
|
||||
*
|
||||
* @param mixed $value The option value
|
||||
* @param array $params An optional array of parameters
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function execute($value = false, $params = array())
|
||||
{
|
||||
return $this->parser->displayUsage();
|
||||
}
|
||||
// }}}
|
||||
}
|
||||
Generated
Vendored
+73
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
|
||||
|
||||
/**
|
||||
* This file is part of the PEAR Console_CommandLine package.
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* LICENSE: This source file is subject to the MIT license that is available
|
||||
* through the world-wide-web at the following URI:
|
||||
* http://opensource.org/licenses/mit-license.php
|
||||
*
|
||||
* @category Console
|
||||
* @package Console_CommandLine
|
||||
* @author David JEAN LOUIS <izimobil@gmail.com>
|
||||
* @copyright 2007 David JEAN LOUIS
|
||||
* @license http://opensource.org/licenses/mit-license.php MIT License
|
||||
* @version CVS: $Id$
|
||||
* @link http://pear.php.net/package/Console_CommandLine
|
||||
* @since File available since release 0.1.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
/**
|
||||
* Required by this class.
|
||||
*/
|
||||
require_once 'Console/CommandLine/Action.php';
|
||||
|
||||
/**
|
||||
* Class that represent the List action, a special action that simply output an
|
||||
* array as a list.
|
||||
*
|
||||
* @category Console
|
||||
* @package Console_CommandLine
|
||||
* @author David JEAN LOUIS <izimobil@gmail.com>
|
||||
* @copyright 2007 David JEAN LOUIS
|
||||
* @license http://opensource.org/licenses/mit-license.php MIT License
|
||||
* @version Release: 1.2.2
|
||||
* @link http://pear.php.net/package/Console_CommandLine
|
||||
* @since Class available since release 0.1.0
|
||||
*/
|
||||
class Console_CommandLine_Action_List extends Console_CommandLine_Action
|
||||
{
|
||||
// execute() {{{
|
||||
|
||||
/**
|
||||
* Executes the action with the value entered by the user.
|
||||
* Possible parameters are:
|
||||
* - message: an alternative message to display instead of the default
|
||||
* message,
|
||||
* - delimiter: an alternative delimiter instead of the comma,
|
||||
* - post: a string to append after the message (default is the new line
|
||||
* char).
|
||||
*
|
||||
* @param mixed $value The option value
|
||||
* @param array $params An optional array of parameters
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function execute($value = false, $params = array())
|
||||
{
|
||||
$list = isset($params['list']) ? $params['list'] : array();
|
||||
$msg = isset($params['message'])
|
||||
? $params['message']
|
||||
: $this->parser->message_provider->get('LIST_DISPLAYED_MESSAGE');
|
||||
$del = isset($params['delimiter']) ? $params['delimiter'] : ', ';
|
||||
$post = isset($params['post']) ? $params['post'] : "\n";
|
||||
$this->parser->outputter->stdout($msg . implode($del, $list) . $post);
|
||||
exit(0);
|
||||
}
|
||||
// }}}
|
||||
}
|
||||
Generated
Vendored
+90
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
|
||||
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
|
||||
|
||||
/**
|
||||
* This file is part of the PEAR Console_CommandLine package.
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* LICENSE: This source file is subject to the MIT license that is available
|
||||
* through the world-wide-web at the following URI:
|
||||
* http://opensource.org/licenses/mit-license.php
|
||||
*
|
||||
* @category Console
|
||||
* @package Console_CommandLine
|
||||
* @author David JEAN LOUIS <izimobil@gmail.com>
|
||||
* @copyright 2007 David JEAN LOUIS
|
||||
* @license http://opensource.org/licenses/mit-license.php MIT License
|
||||
* @version CVS: $Id$
|
||||
* @link http://pear.php.net/package/Console_CommandLine
|
||||
* @since File available since release 0.1.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
/**
|
||||
* Required by this class.
|
||||
*/
|
||||
require_once 'Console/CommandLine/Action.php';
|
||||
|
||||
/**
|
||||
* Class that represent the Password action, a special action that allow the
|
||||
* user to specify the password on the commandline or to be prompted for
|
||||
* entering it.
|
||||
*
|
||||
* @category Console
|
||||
* @package Console_CommandLine
|
||||
* @author David JEAN LOUIS <izimobil@gmail.com>
|
||||
* @copyright 2007 David JEAN LOUIS
|
||||
* @license http://opensource.org/licenses/mit-license.php MIT License
|
||||
* @version Release: 1.2.2
|
||||
* @link http://pear.php.net/package/Console_CommandLine
|
||||
* @since Class available since release 0.1.0
|
||||
*/
|
||||
class Console_CommandLine_Action_Password extends Console_CommandLine_Action
|
||||
{
|
||||
// execute() {{{
|
||||
|
||||
/**
|
||||
* Executes the action with the value entered by the user.
|
||||
*
|
||||
* @param mixed $value The option value
|
||||
* @param array $params An array of optional parameters
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function execute($value = false, $params = array())
|
||||
{
|
||||
$this->setResult(empty($value) ? $this->_promptPassword() : $value);
|
||||
}
|
||||
// }}}
|
||||
// _promptPassword() {{{
|
||||
|
||||
/**
|
||||
* Prompts the password to the user without echoing it.
|
||||
*
|
||||
* @return string
|
||||
* @todo not echo-ing the password does not work on windows is there a way
|
||||
* to make this work ?
|
||||
*/
|
||||
private function _promptPassword()
|
||||
{
|
||||
if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
|
||||
fwrite(STDOUT,
|
||||
$this->parser->message_provider->get('PASSWORD_PROMPT_ECHO'));
|
||||
@flock(STDIN, LOCK_EX);
|
||||
$passwd = fgets(STDIN);
|
||||
@flock(STDIN, LOCK_UN);
|
||||
} else {
|
||||
fwrite(STDOUT, $this->parser->message_provider->get('PASSWORD_PROMPT'));
|
||||
// disable echoing
|
||||
system('stty -echo');
|
||||
@flock(STDIN, LOCK_EX);
|
||||
$passwd = fgets(STDIN);
|
||||
@flock(STDIN, LOCK_UN);
|
||||
system('stty echo');
|
||||
}
|
||||
return trim($passwd);
|
||||
}
|
||||
// }}}
|
||||
}
|
||||
Generated
Vendored
+78
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
|
||||
|
||||
/**
|
||||
* This file is part of the PEAR Console_CommandLine package.
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* LICENSE: This source file is subject to the MIT license that is available
|
||||
* through the world-wide-web at the following URI:
|
||||
* http://opensource.org/licenses/mit-license.php
|
||||
*
|
||||
* @category Console
|
||||
* @package Console_CommandLine
|
||||
* @author David JEAN LOUIS <izimobil@gmail.com>
|
||||
* @copyright 2007 David JEAN LOUIS
|
||||
* @license http://opensource.org/licenses/mit-license.php MIT License
|
||||
* @version CVS: $Id$
|
||||
* @link http://pear.php.net/package/Console_CommandLine
|
||||
* @since File available since release 0.1.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
/**
|
||||
* Required by this class.
|
||||
*/
|
||||
require_once 'Console/CommandLine/Action.php';
|
||||
|
||||
/**
|
||||
* Class that represent the StoreArray action.
|
||||
*
|
||||
* The execute method appends the value of the option entered by the user to
|
||||
* the result option array entry.
|
||||
*
|
||||
* @category Console
|
||||
* @package Console_CommandLine
|
||||
* @author David JEAN LOUIS <izimobil@gmail.com>
|
||||
* @copyright 2007 David JEAN LOUIS
|
||||
* @license http://opensource.org/licenses/mit-license.php MIT License
|
||||
* @version Release: 1.2.2
|
||||
* @link http://pear.php.net/package/Console_CommandLine
|
||||
* @since Class available since release 0.1.0
|
||||
*/
|
||||
class Console_CommandLine_Action_StoreArray extends Console_CommandLine_Action
|
||||
{
|
||||
// Protected properties {{{
|
||||
|
||||
/**
|
||||
* Force a clean result when first called, overriding any defaults assigned.
|
||||
*
|
||||
* @var object $firstPass First time this action has been called.
|
||||
*/
|
||||
protected $firstPass = true;
|
||||
|
||||
// }}}
|
||||
// execute() {{{
|
||||
|
||||
/**
|
||||
* Executes the action with the value entered by the user.
|
||||
*
|
||||
* @param mixed $value The option value
|
||||
* @param array $params An optional array of parameters
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function execute($value = false, $params = array())
|
||||
{
|
||||
$result = $this->getResult();
|
||||
if (null === $result || $this->firstPass) {
|
||||
$result = array();
|
||||
$this->firstPass = false;
|
||||
}
|
||||
$result[] = $value;
|
||||
$this->setResult($result);
|
||||
}
|
||||
// }}}
|
||||
}
|
||||
Generated
Vendored
+64
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
|
||||
|
||||
/**
|
||||
* This file is part of the PEAR Console_CommandLine package.
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* LICENSE: This source file is subject to the MIT license that is available
|
||||
* through the world-wide-web at the following URI:
|
||||
* http://opensource.org/licenses/mit-license.php
|
||||
*
|
||||
* @category Console
|
||||
* @package Console_CommandLine
|
||||
* @author David JEAN LOUIS <izimobil@gmail.com>
|
||||
* @copyright 2007 David JEAN LOUIS
|
||||
* @license http://opensource.org/licenses/mit-license.php MIT License
|
||||
* @version CVS: $Id$
|
||||
* @link http://pear.php.net/package/Console_CommandLine
|
||||
* @since File available since release 0.1.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
/**
|
||||
* Required by this class.
|
||||
*/
|
||||
require_once 'Console/CommandLine/Action.php';
|
||||
|
||||
/**
|
||||
* Class that represent the StoreFalse action.
|
||||
*
|
||||
* The execute method store the boolean 'false' in the corrsponding result
|
||||
* option array entry (the value is true if the option is not present in the
|
||||
* command line entered by the user).
|
||||
*
|
||||
* @category Console
|
||||
* @package Console_CommandLine
|
||||
* @author David JEAN LOUIS <izimobil@gmail.com>
|
||||
* @copyright 2007 David JEAN LOUIS
|
||||
* @license http://opensource.org/licenses/mit-license.php MIT License
|
||||
* @version Release: 1.2.2
|
||||
* @link http://pear.php.net/package/Console_CommandLine
|
||||
* @since Class available since release 0.1.0
|
||||
*/
|
||||
class Console_CommandLine_Action_StoreFalse extends Console_CommandLine_Action
|
||||
{
|
||||
// execute() {{{
|
||||
|
||||
/**
|
||||
* Executes the action with the value entered by the user.
|
||||
*
|
||||
* @param mixed $value The option value
|
||||
* @param array $params An array of optional parameters
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function execute($value = false, $params = array())
|
||||
{
|
||||
$this->setResult(false);
|
||||
}
|
||||
|
||||
// }}}
|
||||
}
|
||||
Generated
Vendored
+76
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
|
||||
|
||||
/**
|
||||
* This file is part of the PEAR Console_CommandLine package.
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* LICENSE: This source file is subject to the MIT license that is available
|
||||
* through the world-wide-web at the following URI:
|
||||
* http://opensource.org/licenses/mit-license.php
|
||||
*
|
||||
* @category Console
|
||||
* @package Console_CommandLine
|
||||
* @author David JEAN LOUIS <izimobil@gmail.com>
|
||||
* @copyright 2007 David JEAN LOUIS
|
||||
* @license http://opensource.org/licenses/mit-license.php MIT License
|
||||
* @version CVS: $Id$
|
||||
* @link http://pear.php.net/package/Console_CommandLine
|
||||
* @since File available since release 0.1.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
/**
|
||||
* Required by this class.
|
||||
*/
|
||||
require_once 'Console/CommandLine/Action.php';
|
||||
|
||||
/**
|
||||
* Class that represent the StoreFloat action.
|
||||
*
|
||||
* The execute method store the value of the option entered by the user as a
|
||||
* float in the result option array entry, if the value passed is not a float
|
||||
* an Exception is raised.
|
||||
*
|
||||
* @category Console
|
||||
* @package Console_CommandLine
|
||||
* @author David JEAN LOUIS <izimobil@gmail.com>
|
||||
* @copyright 2007 David JEAN LOUIS
|
||||
* @license http://opensource.org/licenses/mit-license.php MIT License
|
||||
* @version Release: 1.2.2
|
||||
* @link http://pear.php.net/package/Console_CommandLine
|
||||
* @since Class available since release 0.1.0
|
||||
*/
|
||||
class Console_CommandLine_Action_StoreFloat extends Console_CommandLine_Action
|
||||
{
|
||||
// execute() {{{
|
||||
|
||||
/**
|
||||
* Executes the action with the value entered by the user.
|
||||
*
|
||||
* @param mixed $value The option value
|
||||
* @param array $params An array of optional parameters
|
||||
*
|
||||
* @return string
|
||||
* @throws Console_CommandLine_Exception
|
||||
*/
|
||||
public function execute($value = false, $params = array())
|
||||
{
|
||||
if (!is_numeric($value)) {
|
||||
include_once 'Console/CommandLine/Exception.php';
|
||||
throw Console_CommandLine_Exception::factory(
|
||||
'OPTION_VALUE_TYPE_ERROR',
|
||||
array(
|
||||
'name' => $this->option->name,
|
||||
'type' => 'float',
|
||||
'value' => $value
|
||||
),
|
||||
$this->parser
|
||||
);
|
||||
}
|
||||
$this->setResult((float)$value);
|
||||
}
|
||||
// }}}
|
||||
}
|
||||
Generated
Vendored
+76
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
|
||||
|
||||
/**
|
||||
* This file is part of the PEAR Console_CommandLine package.
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* LICENSE: This source file is subject to the MIT license that is available
|
||||
* through the world-wide-web at the following URI:
|
||||
* http://opensource.org/licenses/mit-license.php
|
||||
*
|
||||
* @category Console
|
||||
* @package Console_CommandLine
|
||||
* @author David JEAN LOUIS <izimobil@gmail.com>
|
||||
* @copyright 2007 David JEAN LOUIS
|
||||
* @license http://opensource.org/licenses/mit-license.php MIT License
|
||||
* @version CVS: $Id$
|
||||
* @link http://pear.php.net/package/Console_CommandLine
|
||||
* @since File available since release 0.1.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
/**
|
||||
* Required by this class.
|
||||
*/
|
||||
require_once 'Console/CommandLine/Action.php';
|
||||
|
||||
/**
|
||||
* Class that represent the StoreInt action.
|
||||
*
|
||||
* The execute method store the value of the option entered by the user as an
|
||||
* integer in the result option array entry, if the value passed is not an
|
||||
* integer an Exception is raised.
|
||||
*
|
||||
* @category Console
|
||||
* @package Console_CommandLine
|
||||
* @author David JEAN LOUIS <izimobil@gmail.com>
|
||||
* @copyright 2007 David JEAN LOUIS
|
||||
* @license http://opensource.org/licenses/mit-license.php MIT License
|
||||
* @version Release: 1.2.2
|
||||
* @link http://pear.php.net/package/Console_CommandLine
|
||||
* @since Class available since release 0.1.0
|
||||
*/
|
||||
class Console_CommandLine_Action_StoreInt extends Console_CommandLine_Action
|
||||
{
|
||||
// execute() {{{
|
||||
|
||||
/**
|
||||
* Executes the action with the value entered by the user.
|
||||
*
|
||||
* @param mixed $value The option value
|
||||
* @param array $params An array of optional parameters
|
||||
*
|
||||
* @return string
|
||||
* @throws Console_CommandLine_Exception
|
||||
*/
|
||||
public function execute($value = false, $params = array())
|
||||
{
|
||||
if (!is_numeric($value)) {
|
||||
include_once 'Console/CommandLine/Exception.php';
|
||||
throw Console_CommandLine_Exception::factory(
|
||||
'OPTION_VALUE_TYPE_ERROR',
|
||||
array(
|
||||
'name' => $this->option->name,
|
||||
'type' => 'int',
|
||||
'value' => $value
|
||||
),
|
||||
$this->parser
|
||||
);
|
||||
}
|
||||
$this->setResult((int)$value);
|
||||
}
|
||||
// }}}
|
||||
}
|
||||
Generated
Vendored
+62
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
|
||||
|
||||
/**
|
||||
* This file is part of the PEAR Console_CommandLine package.
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* LICENSE: This source file is subject to the MIT license that is available
|
||||
* through the world-wide-web at the following URI:
|
||||
* http://opensource.org/licenses/mit-license.php
|
||||
*
|
||||
* @category Console
|
||||
* @package Console_CommandLine
|
||||
* @author David JEAN LOUIS <izimobil@gmail.com>
|
||||
* @copyright 2007 David JEAN LOUIS
|
||||
* @license http://opensource.org/licenses/mit-license.php MIT License
|
||||
* @version CVS: $Id$
|
||||
* @link http://pear.php.net/package/Console_CommandLine
|
||||
* @since File available since release 0.1.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
/**
|
||||
* Required by this class.
|
||||
*/
|
||||
require_once 'Console/CommandLine/Action.php';
|
||||
|
||||
/**
|
||||
* Class that represent the StoreString action.
|
||||
*
|
||||
* The execute method store the value of the option entered by the user as a
|
||||
* string in the result option array entry.
|
||||
*
|
||||
* @category Console
|
||||
* @package Console_CommandLine
|
||||
* @author David JEAN LOUIS <izimobil@gmail.com>
|
||||
* @copyright 2007 David JEAN LOUIS
|
||||
* @license http://opensource.org/licenses/mit-license.php MIT License
|
||||
* @version Release: 1.2.2
|
||||
* @link http://pear.php.net/package/Console_CommandLine
|
||||
* @since Class available since release 0.1.0
|
||||
*/
|
||||
class Console_CommandLine_Action_StoreString extends Console_CommandLine_Action
|
||||
{
|
||||
// execute() {{{
|
||||
|
||||
/**
|
||||
* Executes the action with the value entered by the user.
|
||||
*
|
||||
* @param mixed $value The option value
|
||||
* @param array $params An array of optional parameters
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function execute($value = false, $params = array())
|
||||
{
|
||||
$this->setResult((string)$value);
|
||||
}
|
||||
// }}}
|
||||
}
|
||||
Generated
Vendored
+63
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
|
||||
|
||||
/**
|
||||
* This file is part of the PEAR Console_CommandLine package.
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* LICENSE: This source file is subject to the MIT license that is available
|
||||
* through the world-wide-web at the following URI:
|
||||
* http://opensource.org/licenses/mit-license.php
|
||||
*
|
||||
* @category Console
|
||||
* @package Console_CommandLine
|
||||
* @author David JEAN LOUIS <izimobil@gmail.com>
|
||||
* @copyright 2007 David JEAN LOUIS
|
||||
* @license http://opensource.org/licenses/mit-license.php MIT License
|
||||
* @version CVS: $Id$
|
||||
* @link http://pear.php.net/package/Console_CommandLine
|
||||
* @since File available since release 0.1.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
/**
|
||||
* Required by this class.
|
||||
*/
|
||||
require_once 'Console/CommandLine/Action.php';
|
||||
|
||||
/**
|
||||
* Class that represent the StoreTrue action.
|
||||
*
|
||||
* The execute method store the boolean 'true' in the corrsponding result
|
||||
* option array entry (the value is false if the option is not present in the
|
||||
* command line entered by the user).
|
||||
*
|
||||
* @category Console
|
||||
* @package Console_CommandLine
|
||||
* @author David JEAN LOUIS <izimobil@gmail.com>
|
||||
* @copyright 2007 David JEAN LOUIS
|
||||
* @license http://opensource.org/licenses/mit-license.php MIT License
|
||||
* @version Release: 1.2.2
|
||||
* @link http://pear.php.net/package/Console_CommandLine
|
||||
* @since Class available since release 0.1.0
|
||||
*/
|
||||
class Console_CommandLine_Action_StoreTrue extends Console_CommandLine_Action
|
||||
{
|
||||
// execute() {{{
|
||||
|
||||
/**
|
||||
* Executes the action with the value entered by the user.
|
||||
*
|
||||
* @param mixed $value The option value
|
||||
* @param array $params An array of optional parameters
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function execute($value = false, $params = array())
|
||||
{
|
||||
$this->setResult(true);
|
||||
}
|
||||
// }}}
|
||||
}
|
||||
Generated
Vendored
+60
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
|
||||
|
||||
/**
|
||||
* This file is part of the PEAR Console_CommandLine package.
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* LICENSE: This source file is subject to the MIT license that is available
|
||||
* through the world-wide-web at the following URI:
|
||||
* http://opensource.org/licenses/mit-license.php
|
||||
*
|
||||
* @category Console
|
||||
* @package Console_CommandLine
|
||||
* @author David JEAN LOUIS <izimobil@gmail.com>
|
||||
* @copyright 2007 David JEAN LOUIS
|
||||
* @license http://opensource.org/licenses/mit-license.php MIT License
|
||||
* @version CVS: $Id$
|
||||
* @link http://pear.php.net/package/Console_CommandLine
|
||||
* @since File available since release 0.1.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
/**
|
||||
* Required by this class.
|
||||
*/
|
||||
require_once 'Console/CommandLine/Action.php';
|
||||
|
||||
/**
|
||||
* Class that represent the Version action, a special action that displays the
|
||||
* version string of the program.
|
||||
*
|
||||
* @category Console
|
||||
* @package Console_CommandLine
|
||||
* @author David JEAN LOUIS <izimobil@gmail.com>
|
||||
* @copyright 2007 David JEAN LOUIS
|
||||
* @license http://opensource.org/licenses/mit-license.php MIT License
|
||||
* @version Release: 1.2.2
|
||||
* @link http://pear.php.net/package/Console_CommandLine
|
||||
* @since Class available since release 0.1.0
|
||||
*/
|
||||
class Console_CommandLine_Action_Version extends Console_CommandLine_Action
|
||||
{
|
||||
// execute() {{{
|
||||
|
||||
/**
|
||||
* Executes the action with the value entered by the user.
|
||||
*
|
||||
* @param mixed $value The option value
|
||||
* @param array $params An array of optional parameters
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function execute($value = false, $params = array())
|
||||
{
|
||||
return $this->parser->displayVersion();
|
||||
}
|
||||
// }}}
|
||||
}
|
||||
Generated
Vendored
+102
@@ -0,0 +1,102 @@
|
||||
<?php
|
||||
|
||||
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
|
||||
|
||||
/**
|
||||
* This file is part of the PEAR Console_CommandLine package.
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* LICENSE: This source file is subject to the MIT license that is available
|
||||
* through the world-wide-web at the following URI:
|
||||
* http://opensource.org/licenses/mit-license.php
|
||||
*
|
||||
* @category Console
|
||||
* @package Console_CommandLine
|
||||
* @author David JEAN LOUIS <izimobil@gmail.com>
|
||||
* @copyright 2007 David JEAN LOUIS
|
||||
* @license http://opensource.org/licenses/mit-license.php MIT License
|
||||
* @version CVS: $Id$
|
||||
* @link http://pear.php.net/package/Console_CommandLine
|
||||
* @since File available since release 0.1.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
/**
|
||||
* Include base element class.
|
||||
*/
|
||||
require_once 'Console/CommandLine/Element.php';
|
||||
|
||||
/**
|
||||
* Class that represent a command line argument.
|
||||
*
|
||||
* @category Console
|
||||
* @package Console_CommandLine
|
||||
* @author David JEAN LOUIS <izimobil@gmail.com>
|
||||
* @copyright 2007 David JEAN LOUIS
|
||||
* @license http://opensource.org/licenses/mit-license.php MIT License
|
||||
* @version Release: 1.2.2
|
||||
* @link http://pear.php.net/package/Console_CommandLine
|
||||
* @since Class available since release 0.1.0
|
||||
*/
|
||||
class Console_CommandLine_Argument extends Console_CommandLine_Element
|
||||
{
|
||||
// Public properties {{{
|
||||
|
||||
/**
|
||||
* Setting this to true will tell the parser that the argument expects more
|
||||
* than one argument and that argument values should be stored in an array.
|
||||
*
|
||||
* @var boolean $multiple Whether the argument expects multiple values
|
||||
*/
|
||||
public $multiple = false;
|
||||
|
||||
/**
|
||||
* Setting this to true will tell the parser that the argument is optional
|
||||
* and can be ommited.
|
||||
* Note that it is not a good practice to make arguments optional, it is
|
||||
* the role of the options to be optional, by essence.
|
||||
*
|
||||
* @var boolean $optional Whether the argument is optional or not.
|
||||
*/
|
||||
public $optional = false;
|
||||
|
||||
/**
|
||||
* An array of possible values for the argument.
|
||||
*
|
||||
* @var array $choices Valid choices for the argument
|
||||
*/
|
||||
public $choices = array();
|
||||
|
||||
// }}}
|
||||
// validate() {{{
|
||||
|
||||
/**
|
||||
* Validates the argument instance.
|
||||
*
|
||||
* @return void
|
||||
* @throws Console_CommandLine_Exception
|
||||
* @todo use exceptions
|
||||
*/
|
||||
public function validate()
|
||||
{
|
||||
// check if the argument name is valid
|
||||
if (!preg_match('/^[a-zA-Z_\x7f-\xff]+[a-zA-Z0-9_\x7f-\xff]*$/',
|
||||
$this->name)) {
|
||||
Console_CommandLine::triggerError(
|
||||
'argument_bad_name',
|
||||
E_USER_ERROR,
|
||||
array('{$name}' => $this->name)
|
||||
);
|
||||
}
|
||||
if (!$this->optional && $this->default !== null) {
|
||||
Console_CommandLine::triggerError(
|
||||
'argument_no_default',
|
||||
E_USER_ERROR
|
||||
);
|
||||
}
|
||||
parent::validate();
|
||||
}
|
||||
|
||||
// }}}
|
||||
}
|
||||
Generated
Vendored
+76
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
|
||||
|
||||
/**
|
||||
* This file is part of the PEAR Console_CommandLine package.
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* LICENSE: This source file is subject to the MIT license that is available
|
||||
* through the world-wide-web at the following URI:
|
||||
* http://opensource.org/licenses/mit-license.php
|
||||
*
|
||||
* @category Console
|
||||
* @package Console_CommandLine
|
||||
* @author David JEAN LOUIS <izimobil@gmail.com>
|
||||
* @copyright 2007 David JEAN LOUIS
|
||||
* @license http://opensource.org/licenses/mit-license.php MIT License
|
||||
* @version CVS: $Id$
|
||||
* @link http://pear.php.net/package/Console_CommandLine
|
||||
* @since File available since release 0.1.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
/**
|
||||
* File containing the parent class.
|
||||
*/
|
||||
require_once 'Console/CommandLine.php';
|
||||
|
||||
/**
|
||||
* Class that represent a command with option and arguments.
|
||||
*
|
||||
* This class exist just to clarify the interface but at the moment it is
|
||||
* strictly identical to Console_CommandLine class, it could change in the
|
||||
* future though.
|
||||
*
|
||||
* @category Console
|
||||
* @package Console_CommandLine
|
||||
* @author David JEAN LOUIS <izimobil@gmail.com>
|
||||
* @copyright 2007 David JEAN LOUIS
|
||||
* @license http://opensource.org/licenses/mit-license.php MIT License
|
||||
* @version Release: 1.2.2
|
||||
* @link http://pear.php.net/package/Console_CommandLine
|
||||
* @since Class available since release 0.1.0
|
||||
*/
|
||||
class Console_CommandLine_Command extends Console_CommandLine
|
||||
{
|
||||
// Public properties {{{
|
||||
|
||||
/**
|
||||
* An array of aliases for the subcommand.
|
||||
*
|
||||
* @var array $aliases Aliases for the subcommand.
|
||||
*/
|
||||
public $aliases = array();
|
||||
|
||||
// }}}
|
||||
// __construct() {{{
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param array $params An optional array of parameters
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($params = array())
|
||||
{
|
||||
if (isset($params['aliases'])) {
|
||||
$this->aliases = $params['aliases'];
|
||||
}
|
||||
parent::__construct($params);
|
||||
}
|
||||
|
||||
// }}}
|
||||
}
|
||||
Generated
Vendored
+66
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
|
||||
|
||||
/**
|
||||
* This file is part of the PEAR Console_CommandLine package.
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* LICENSE: This source file is subject to the MIT license that is available
|
||||
* through the world-wide-web at the following URI:
|
||||
* http://opensource.org/licenses/mit-license.php
|
||||
*
|
||||
* @category Console
|
||||
* @package Console_CommandLine
|
||||
* @author David JEAN LOUIS <izimobil@gmail.com>
|
||||
* @author Michael Gauthier <mike@silverorange.com>
|
||||
* @copyright 2007 David JEAN LOUIS, 2009 silverorange
|
||||
* @license http://opensource.org/licenses/mit-license.php MIT License
|
||||
* @version CVS: $Id$
|
||||
* @link http://pear.php.net/package/Console_CommandLine
|
||||
* @since File available since release 1.1.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
/**
|
||||
* Common interfacefor message providers that allow overriding with custom
|
||||
* messages
|
||||
*
|
||||
* Message providers may optionally implement this interface.
|
||||
*
|
||||
* @category Console
|
||||
* @package Console_CommandLine
|
||||
* @author David JEAN LOUIS <izimobil@gmail.com>
|
||||
* @author Michael Gauthier <mike@silverorange.com>
|
||||
* @copyright 2007 David JEAN LOUIS, 2009 silverorange
|
||||
* @license http://opensource.org/licenses/mit-license.php MIT License
|
||||
* @version Release: 1.2.2
|
||||
* @link http://pear.php.net/package/Console_CommandLine
|
||||
* @since Interface available since release 1.1.0
|
||||
*/
|
||||
interface Console_CommandLine_CustomMessageProvider
|
||||
{
|
||||
// getWithCustomMesssages() {{{
|
||||
|
||||
/**
|
||||
* Retrieves the given string identifier corresponding message.
|
||||
*
|
||||
* For a list of identifiers please see the provided default message
|
||||
* provider.
|
||||
*
|
||||
* @param string $code The string identifier of the message
|
||||
* @param array $vars An array of template variables
|
||||
* @param array $messages An optional array of messages to use. Array
|
||||
* indexes are message codes.
|
||||
*
|
||||
* @return string
|
||||
* @see Console_CommandLine_MessageProvider
|
||||
* @see Console_CommandLine_MessageProvider_Default
|
||||
*/
|
||||
public function getWithCustomMessages(
|
||||
$code, $vars = array(), $messages = array()
|
||||
);
|
||||
|
||||
// }}}
|
||||
}
|
||||
Generated
Vendored
+151
@@ -0,0 +1,151 @@
|
||||
<?php
|
||||
|
||||
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
|
||||
|
||||
/**
|
||||
* This file is part of the PEAR Console_CommandLine package.
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* LICENSE: This source file is subject to the MIT license that is available
|
||||
* through the world-wide-web at the following URI:
|
||||
* http://opensource.org/licenses/mit-license.php
|
||||
*
|
||||
* @category Console
|
||||
* @package Console_CommandLine
|
||||
* @author David JEAN LOUIS <izimobil@gmail.com>
|
||||
* @copyright 2007 David JEAN LOUIS
|
||||
* @license http://opensource.org/licenses/mit-license.php MIT License
|
||||
* @version CVS: $Id$
|
||||
* @link http://pear.php.net/package/Console_CommandLine
|
||||
* @since File available since release 0.1.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
/**
|
||||
* Class that represent a command line element (an option, or an argument).
|
||||
*
|
||||
* @category Console
|
||||
* @package Console_CommandLine
|
||||
* @author David JEAN LOUIS <izimobil@gmail.com>
|
||||
* @copyright 2007 David JEAN LOUIS
|
||||
* @license http://opensource.org/licenses/mit-license.php MIT License
|
||||
* @version Release: 1.2.2
|
||||
* @link http://pear.php.net/package/Console_CommandLine
|
||||
* @since Class available since release 0.1.0
|
||||
*/
|
||||
abstract class Console_CommandLine_Element
|
||||
{
|
||||
// Public properties {{{
|
||||
|
||||
/**
|
||||
* The element name.
|
||||
*
|
||||
* @var string $name Element name
|
||||
*/
|
||||
public $name;
|
||||
|
||||
/**
|
||||
* The name of variable displayed in the usage message, if no set it
|
||||
* defaults to the "name" property.
|
||||
*
|
||||
* @var string $help_name Element "help" variable name
|
||||
*/
|
||||
public $help_name;
|
||||
|
||||
/**
|
||||
* The element description.
|
||||
*
|
||||
* @var string $description Element description
|
||||
*/
|
||||
public $description;
|
||||
|
||||
/**
|
||||
* The default value of the element if not provided on the command line.
|
||||
*
|
||||
* @var mixed $default Default value of the option.
|
||||
*/
|
||||
public $default;
|
||||
|
||||
/**
|
||||
* Custom errors messages for this element
|
||||
*
|
||||
* This array is of the form:
|
||||
* <code>
|
||||
* <?php
|
||||
* array(
|
||||
* $messageName => $messageText,
|
||||
* $messageName => $messageText,
|
||||
* ...
|
||||
* );
|
||||
* ?>
|
||||
* </code>
|
||||
*
|
||||
* If specified, these messages override the messages provided by the
|
||||
* default message provider. For example:
|
||||
* <code>
|
||||
* <?php
|
||||
* $messages = array(
|
||||
* 'ARGUMENT_REQUIRED' => 'The argument foo is required.',
|
||||
* );
|
||||
* ?>
|
||||
* </code>
|
||||
*
|
||||
* @var array
|
||||
* @see Console_CommandLine_MessageProvider_Default
|
||||
*/
|
||||
public $messages = array();
|
||||
|
||||
// }}}
|
||||
// __construct() {{{
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string $name The name of the element
|
||||
* @param array $params An optional array of parameters
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($name = null, $params = array())
|
||||
{
|
||||
$this->name = $name;
|
||||
foreach ($params as $attr => $value) {
|
||||
if (property_exists($this, $attr)) {
|
||||
$this->$attr = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// }}}
|
||||
// toString() {{{
|
||||
|
||||
/**
|
||||
* Returns the string representation of the element.
|
||||
*
|
||||
* @return string The string representation of the element
|
||||
* @todo use __toString() instead
|
||||
*/
|
||||
public function toString()
|
||||
{
|
||||
return $this->help_name;
|
||||
}
|
||||
// }}}
|
||||
// validate() {{{
|
||||
|
||||
/**
|
||||
* Validates the element instance and set it's default values.
|
||||
*
|
||||
* @return void
|
||||
* @throws Console_CommandLine_Exception
|
||||
*/
|
||||
public function validate()
|
||||
{
|
||||
// if no help_name passed, default to name
|
||||
if ($this->help_name == null) {
|
||||
$this->help_name = $this->name;
|
||||
}
|
||||
}
|
||||
|
||||
// }}}
|
||||
}
|
||||
Generated
Vendored
+97
@@ -0,0 +1,97 @@
|
||||
<?php
|
||||
|
||||
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
|
||||
|
||||
/**
|
||||
* This file is part of the PEAR Console_CommandLine package.
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* LICENSE: This source file is subject to the MIT license that is available
|
||||
* through the world-wide-web at the following URI:
|
||||
* http://opensource.org/licenses/mit-license.php
|
||||
*
|
||||
* @category Console
|
||||
* @package Console_CommandLine
|
||||
* @author David JEAN LOUIS <izimobil@gmail.com>
|
||||
* @copyright 2007 David JEAN LOUIS
|
||||
* @license http://opensource.org/licenses/mit-license.php MIT License
|
||||
* @version CVS: $Id$
|
||||
* @link http://pear.php.net/package/Console_CommandLine
|
||||
* @since File available since release 0.1.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
/**
|
||||
* Include the PEAR_Exception class
|
||||
*/
|
||||
require_once 'PEAR/Exception.php';
|
||||
|
||||
/**
|
||||
* Interface for custom message provider.
|
||||
*/
|
||||
require_once 'Console/CommandLine/CustomMessageProvider.php';
|
||||
|
||||
/**
|
||||
* Class for exceptions raised by the Console_CommandLine package.
|
||||
*
|
||||
* @category Console
|
||||
* @package Console_CommandLine
|
||||
* @author David JEAN LOUIS <izimobil@gmail.com>
|
||||
* @copyright 2007 David JEAN LOUIS
|
||||
* @license http://opensource.org/licenses/mit-license.php MIT License
|
||||
* @version Release: 1.2.2
|
||||
* @link http://pear.php.net/package/Console_CommandLine
|
||||
* @since Class available since release 0.1.0
|
||||
*/
|
||||
class Console_CommandLine_Exception extends PEAR_Exception
|
||||
{
|
||||
// Codes constants {{{
|
||||
|
||||
/**#@+
|
||||
* Exception code constants.
|
||||
*/
|
||||
const OPTION_VALUE_REQUIRED = 1;
|
||||
const OPTION_VALUE_UNEXPECTED = 2;
|
||||
const OPTION_VALUE_TYPE_ERROR = 3;
|
||||
const OPTION_UNKNOWN = 4;
|
||||
const ARGUMENT_REQUIRED = 5;
|
||||
const INVALID_SUBCOMMAND = 6;
|
||||
/**#@-*/
|
||||
|
||||
// }}}
|
||||
// factory() {{{
|
||||
|
||||
/**
|
||||
* Convenience method that builds the exception with the array of params by
|
||||
* calling the message provider class.
|
||||
*
|
||||
* @param string $code The string identifier of the
|
||||
* exception.
|
||||
* @param array $params Array of template vars/values
|
||||
* @param Console_CommandLine $parser An instance of the parser
|
||||
* @param array $messages An optional array of messages
|
||||
* passed to the message provider.
|
||||
*
|
||||
* @return object an instance of Console_CommandLine_Exception
|
||||
*/
|
||||
public static function factory(
|
||||
$code, $params, $parser, array $messages = array()
|
||||
) {
|
||||
$provider = $parser->message_provider;
|
||||
if ($provider instanceof Console_CommandLine_CustomMessageProvider) {
|
||||
$msg = $provider->getWithCustomMessages(
|
||||
$code,
|
||||
$params,
|
||||
$messages
|
||||
);
|
||||
} else {
|
||||
$msg = $provider->get($code, $params);
|
||||
}
|
||||
$const = 'Console_CommandLine_Exception::' . $code;
|
||||
$code = defined($const) ? constant($const) : 0;
|
||||
return new Console_CommandLine_Exception($msg, $code);
|
||||
}
|
||||
|
||||
// }}}
|
||||
}
|
||||
Generated
Vendored
+56
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
|
||||
|
||||
/**
|
||||
* This file is part of the PEAR Console_CommandLine package.
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* LICENSE: This source file is subject to the MIT license that is available
|
||||
* through the world-wide-web at the following URI:
|
||||
* http://opensource.org/licenses/mit-license.php
|
||||
*
|
||||
* @category Console
|
||||
* @package Console_CommandLine
|
||||
* @author David JEAN LOUIS <izimobil@gmail.com>
|
||||
* @copyright 2007 David JEAN LOUIS
|
||||
* @license http://opensource.org/licenses/mit-license.php MIT License
|
||||
* @version CVS: $Id$
|
||||
* @link http://pear.php.net/package/Console_CommandLine
|
||||
* @since File available since release 0.1.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
/**
|
||||
* Message providers common interface, all message providers must implement
|
||||
* this interface.
|
||||
*
|
||||
* @category Console
|
||||
* @package Console_CommandLine
|
||||
* @author David JEAN LOUIS <izimobil@gmail.com>
|
||||
* @copyright 2007 David JEAN LOUIS
|
||||
* @license http://opensource.org/licenses/mit-license.php MIT License
|
||||
* @version Release: 1.2.2
|
||||
* @link http://pear.php.net/package/Console_CommandLine
|
||||
* @since Class available since release 0.1.0
|
||||
*/
|
||||
interface Console_CommandLine_MessageProvider
|
||||
{
|
||||
// get() {{{
|
||||
|
||||
/**
|
||||
* Retrieves the given string identifier corresponding message.
|
||||
* For a list of identifiers please see the provided default message
|
||||
* provider.
|
||||
*
|
||||
* @param string $code The string identifier of the message
|
||||
* @param array $vars An array of template variables
|
||||
*
|
||||
* @return string
|
||||
* @see Console_CommandLine_MessageProvider_Default
|
||||
*/
|
||||
public function get($code, $vars=array());
|
||||
|
||||
// }}}
|
||||
}
|
||||
Generated
Vendored
+153
@@ -0,0 +1,153 @@
|
||||
<?php
|
||||
|
||||
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
|
||||
|
||||
/**
|
||||
* This file is part of the PEAR Console_CommandLine package.
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* LICENSE: This source file is subject to the MIT license that is available
|
||||
* through the world-wide-web at the following URI:
|
||||
* http://opensource.org/licenses/mit-license.php
|
||||
*
|
||||
* @category Console
|
||||
* @package Console_CommandLine
|
||||
* @author David JEAN LOUIS <izimobil@gmail.com>
|
||||
* @copyright 2007 David JEAN LOUIS
|
||||
* @license http://opensource.org/licenses/mit-license.php MIT License
|
||||
* @version CVS: $Id$
|
||||
* @link http://pear.php.net/package/Console_CommandLine
|
||||
* @since File available since release 0.1.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
/**
|
||||
* The message provider interface.
|
||||
*/
|
||||
require_once 'Console/CommandLine/MessageProvider.php';
|
||||
|
||||
/**
|
||||
* The custom message provider interface.
|
||||
*/
|
||||
require_once 'Console/CommandLine/CustomMessageProvider.php';
|
||||
|
||||
/**
|
||||
* Lightweight class that manages messages used by Console_CommandLine package,
|
||||
* allowing the developper to customize these messages, for example to
|
||||
* internationalize a command line frontend.
|
||||
*
|
||||
* @category Console
|
||||
* @package Console_CommandLine
|
||||
* @author David JEAN LOUIS <izimobil@gmail.com>
|
||||
* @copyright 2007 David JEAN LOUIS
|
||||
* @license http://opensource.org/licenses/mit-license.php MIT License
|
||||
* @version Release: 1.2.2
|
||||
* @link http://pear.php.net/package/Console_CommandLine
|
||||
* @since Class available since release 0.1.0
|
||||
*/
|
||||
class Console_CommandLine_MessageProvider_Default
|
||||
implements Console_CommandLine_MessageProvider,
|
||||
Console_CommandLine_CustomMessageProvider
|
||||
{
|
||||
// Properties {{{
|
||||
|
||||
/**
|
||||
* Associative array of messages
|
||||
*
|
||||
* @var array $messages
|
||||
*/
|
||||
protected $messages = array(
|
||||
'OPTION_VALUE_REQUIRED' => 'Option "{$name}" requires a value.',
|
||||
'OPTION_VALUE_UNEXPECTED' => 'Option "{$name}" does not expect a value (got "{$value}").',
|
||||
'OPTION_VALUE_NOT_VALID' => 'Option "{$name}" must be one of the following: "{$choices}" (got "{$value}").',
|
||||
'ARGUMENT_VALUE_NOT_VALID'=> 'Argument "{$name}" must be one of the following: "{$choices}" (got "{$value}").',
|
||||
'OPTION_VALUE_TYPE_ERROR' => 'Option "{$name}" requires a value of type {$type} (got "{$value}").',
|
||||
'OPTION_AMBIGUOUS' => 'Ambiguous option "{$name}", can be one of the following: {$matches}.',
|
||||
'OPTION_UNKNOWN' => 'Unknown option "{$name}".',
|
||||
'ARGUMENT_REQUIRED' => 'You must provide at least {$argnum} argument{$plural}.',
|
||||
'PROG_HELP_LINE' => 'Type "{$progname} --help" to get help.',
|
||||
'PROG_VERSION_LINE' => '{$progname} version {$version}.',
|
||||
'COMMAND_HELP_LINE' => 'Type "{$progname} <command> --help" to get help on specific command.',
|
||||
'USAGE_WORD' => 'Usage',
|
||||
'OPTION_WORD' => 'Options',
|
||||
'ARGUMENT_WORD' => 'Arguments',
|
||||
'COMMAND_WORD' => 'Commands',
|
||||
'PASSWORD_PROMPT' => 'Password: ',
|
||||
'PASSWORD_PROMPT_ECHO' => 'Password (warning: will echo): ',
|
||||
'INVALID_CUSTOM_INSTANCE' => 'Instance does not implement the required interface',
|
||||
'LIST_OPTION_MESSAGE' => 'lists valid choices for option {$name}',
|
||||
'LIST_DISPLAYED_MESSAGE' => 'Valid choices are: ',
|
||||
'INVALID_SUBCOMMAND' => 'Command "{$command}" is not valid.',
|
||||
'SUBCOMMAND_REQUIRED' => 'Please enter one of the following command: {$commands}.',
|
||||
);
|
||||
|
||||
// }}}
|
||||
// get() {{{
|
||||
|
||||
/**
|
||||
* Retrieve the given string identifier corresponding message.
|
||||
*
|
||||
* @param string $code The string identifier of the message
|
||||
* @param array $vars An array of template variables
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get($code, $vars = array())
|
||||
{
|
||||
if (!isset($this->messages[$code])) {
|
||||
return 'UNKNOWN';
|
||||
}
|
||||
return $this->replaceTemplateVars($this->messages[$code], $vars);
|
||||
}
|
||||
|
||||
// }}}
|
||||
// getWithCustomMessages() {{{
|
||||
|
||||
/**
|
||||
* Retrieve the given string identifier corresponding message.
|
||||
*
|
||||
* @param string $code The string identifier of the message
|
||||
* @param array $vars An array of template variables
|
||||
* @param array $messages An optional array of messages to use. Array
|
||||
* indexes are message codes.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getWithCustomMessages(
|
||||
$code, $vars = array(), $messages = array()
|
||||
) {
|
||||
// get message
|
||||
if (isset($messages[$code])) {
|
||||
$message = $messages[$code];
|
||||
} elseif (isset($this->messages[$code])) {
|
||||
$message = $this->messages[$code];
|
||||
} else {
|
||||
$message = 'UNKNOWN';
|
||||
}
|
||||
return $this->replaceTemplateVars($message, $vars);
|
||||
}
|
||||
|
||||
// }}}
|
||||
// replaceTemplateVars() {{{
|
||||
|
||||
/**
|
||||
* Replaces template vars in a message
|
||||
*
|
||||
* @param string $message The message
|
||||
* @param array $vars An array of template variables
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function replaceTemplateVars($message, $vars = array())
|
||||
{
|
||||
$tmpkeys = array_keys($vars);
|
||||
$keys = array();
|
||||
foreach ($tmpkeys as $key) {
|
||||
$keys[] = '{$' . $key . '}';
|
||||
}
|
||||
return str_replace($keys, array_values($vars), $message);
|
||||
}
|
||||
|
||||
// }}}
|
||||
}
|
||||
Generated
Vendored
+366
@@ -0,0 +1,366 @@
|
||||
<?php
|
||||
|
||||
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
|
||||
|
||||
/**
|
||||
* This file is part of the PEAR Console_CommandLine package.
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* LICENSE: This source file is subject to the MIT license that is available
|
||||
* through the world-wide-web at the following URI:
|
||||
* http://opensource.org/licenses/mit-license.php
|
||||
*
|
||||
* @category Console
|
||||
* @package Console_CommandLine
|
||||
* @author David JEAN LOUIS <izimobil@gmail.com>
|
||||
* @copyright 2007 David JEAN LOUIS
|
||||
* @license http://opensource.org/licenses/mit-license.php MIT License
|
||||
* @version CVS: $Id$
|
||||
* @link http://pear.php.net/package/Console_CommandLine
|
||||
* @since File available since release 0.1.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
/**
|
||||
* Required by this class.
|
||||
*/
|
||||
require_once 'Console/CommandLine.php';
|
||||
require_once 'Console/CommandLine/Element.php';
|
||||
|
||||
/**
|
||||
* Class that represent a commandline option.
|
||||
*
|
||||
* @category Console
|
||||
* @package Console_CommandLine
|
||||
* @author David JEAN LOUIS <izimobil@gmail.com>
|
||||
* @copyright 2007 David JEAN LOUIS
|
||||
* @license http://opensource.org/licenses/mit-license.php MIT License
|
||||
* @version Release: 1.2.2
|
||||
* @link http://pear.php.net/package/Console_CommandLine
|
||||
* @since Class available since release 0.1.0
|
||||
*/
|
||||
class Console_CommandLine_Option extends Console_CommandLine_Element
|
||||
{
|
||||
// Public properties {{{
|
||||
|
||||
/**
|
||||
* The option short name (ex: -v).
|
||||
*
|
||||
* @var string $short_name Short name of the option
|
||||
*/
|
||||
public $short_name;
|
||||
|
||||
/**
|
||||
* The option long name (ex: --verbose).
|
||||
*
|
||||
* @var string $long_name Long name of the option
|
||||
*/
|
||||
public $long_name;
|
||||
|
||||
/**
|
||||
* The option action, defaults to "StoreString".
|
||||
*
|
||||
* @var string $action Option action
|
||||
*/
|
||||
public $action = 'StoreString';
|
||||
|
||||
/**
|
||||
* An array of possible values for the option. If this array is not empty
|
||||
* and the value passed is not in the array an exception is raised.
|
||||
* This only make sense for actions that accept values of course.
|
||||
*
|
||||
* @var array $choices Valid choices for the option
|
||||
*/
|
||||
public $choices = array();
|
||||
|
||||
/**
|
||||
* The callback function (or method) to call for an action of type
|
||||
* Callback, this can be any callable supported by the php function
|
||||
* call_user_func.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* <code>
|
||||
* $parser->addOption('myoption', array(
|
||||
* 'short_name' => '-m',
|
||||
* 'long_name' => '--myoption',
|
||||
* 'action' => 'Callback',
|
||||
* 'callback' => 'myCallbackFunction'
|
||||
* ));
|
||||
* </code>
|
||||
*
|
||||
* @var callable $callback The option callback
|
||||
*/
|
||||
public $callback;
|
||||
|
||||
/**
|
||||
* An associative array of additional params to pass to the class
|
||||
* corresponding to the action, this array will also be passed to the
|
||||
* callback defined for an action of type Callback, Example:
|
||||
*
|
||||
* <code>
|
||||
* // for a custom action
|
||||
* $parser->addOption('myoption', array(
|
||||
* 'short_name' => '-m',
|
||||
* 'long_name' => '--myoption',
|
||||
* 'action' => 'MyCustomAction',
|
||||
* 'action_params' => array('foo'=>true, 'bar'=>false)
|
||||
* ));
|
||||
*
|
||||
* // if the user type:
|
||||
* // $ <yourprogram> -m spam
|
||||
* // in your MyCustomAction class the execute() method will be called
|
||||
* // with the value 'spam' as first parameter and
|
||||
* // array('foo'=>true, 'bar'=>false) as second parameter
|
||||
* </code>
|
||||
*
|
||||
* @var array $action_params Additional parameters to pass to the action
|
||||
*/
|
||||
public $action_params = array();
|
||||
|
||||
/**
|
||||
* For options that expect an argument, this property tells the parser if
|
||||
* the option argument is optional and can be ommited.
|
||||
*
|
||||
* @var bool $argumentOptional Whether the option arg is optional or not
|
||||
*/
|
||||
public $argument_optional = false;
|
||||
|
||||
/**
|
||||
* For options that uses the "choice" property only.
|
||||
* Adds a --list-<choice> option to the parser that displays the list of
|
||||
* choices for the option.
|
||||
*
|
||||
* @var bool $add_list_option Whether to add a list option or not
|
||||
*/
|
||||
public $add_list_option = false;
|
||||
|
||||
// }}}
|
||||
// Private properties {{{
|
||||
|
||||
/**
|
||||
* When an action is called remember it to allow for multiple calls.
|
||||
*
|
||||
* @var object $action_instance Placeholder for action
|
||||
*/
|
||||
private $_action_instance = null;
|
||||
|
||||
// }}}
|
||||
// __construct() {{{
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string $name The name of the option
|
||||
* @param array $params An optional array of parameters
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($name = null, $params = array())
|
||||
{
|
||||
parent::__construct($name, $params);
|
||||
if ($this->action == 'Password') {
|
||||
// special case for Password action, password can be passed to the
|
||||
// commandline or prompted by the parser
|
||||
$this->argument_optional = true;
|
||||
}
|
||||
}
|
||||
|
||||
// }}}
|
||||
// toString() {{{
|
||||
|
||||
/**
|
||||
* Returns the string representation of the option.
|
||||
*
|
||||
* @param string $delim Delimiter to use between short and long option
|
||||
*
|
||||
* @return string The string representation of the option
|
||||
* @todo use __toString() instead
|
||||
*/
|
||||
public function toString($delim = ", ")
|
||||
{
|
||||
$ret = '';
|
||||
$padding = '';
|
||||
if ($this->short_name != null) {
|
||||
$ret .= $this->short_name;
|
||||
if ($this->expectsArgument()) {
|
||||
$ret .= ' ' . $this->help_name;
|
||||
}
|
||||
$padding = $delim;
|
||||
}
|
||||
if ($this->long_name != null) {
|
||||
$ret .= $padding . $this->long_name;
|
||||
if ($this->expectsArgument()) {
|
||||
$ret .= '=' . $this->help_name;
|
||||
}
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// expectsArgument() {{{
|
||||
|
||||
/**
|
||||
* Returns true if the option requires one or more argument and false
|
||||
* otherwise.
|
||||
*
|
||||
* @return bool Whether the option expects an argument or not
|
||||
*/
|
||||
public function expectsArgument()
|
||||
{
|
||||
if ($this->action == 'StoreTrue' || $this->action == 'StoreFalse' ||
|
||||
$this->action == 'Help' || $this->action == 'Version' ||
|
||||
$this->action == 'Counter' || $this->action == 'List') {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// dispatchAction() {{{
|
||||
|
||||
/**
|
||||
* Formats the value $value according to the action of the option and
|
||||
* updates the passed Console_CommandLine_Result object.
|
||||
*
|
||||
* @param mixed $value The value to format
|
||||
* @param Console_CommandLine_Result $result The result instance
|
||||
* @param Console_CommandLine $parser The parser instance
|
||||
*
|
||||
* @return void
|
||||
* @throws Console_CommandLine_Exception
|
||||
*/
|
||||
public function dispatchAction($value, $result, $parser)
|
||||
{
|
||||
$actionInfo = Console_CommandLine::$actions[$this->action];
|
||||
if (true === $actionInfo[1]) {
|
||||
// we have a "builtin" action
|
||||
$tokens = explode('_', $actionInfo[0]);
|
||||
include_once implode('/', $tokens) . '.php';
|
||||
}
|
||||
$clsname = $actionInfo[0];
|
||||
if ($this->_action_instance === null) {
|
||||
$this->_action_instance = new $clsname($result, $this, $parser);
|
||||
}
|
||||
|
||||
// check value is in option choices
|
||||
if (!empty($this->choices) && !in_array($this->_action_instance->format($value), $this->choices)) {
|
||||
throw Console_CommandLine_Exception::factory(
|
||||
'OPTION_VALUE_NOT_VALID',
|
||||
array(
|
||||
'name' => $this->name,
|
||||
'choices' => implode('", "', $this->choices),
|
||||
'value' => $value,
|
||||
),
|
||||
$parser,
|
||||
$this->messages
|
||||
);
|
||||
}
|
||||
$this->_action_instance->execute($value, $this->action_params);
|
||||
}
|
||||
|
||||
// }}}
|
||||
// validate() {{{
|
||||
|
||||
/**
|
||||
* Validates the option instance.
|
||||
*
|
||||
* @return void
|
||||
* @throws Console_CommandLine_Exception
|
||||
* @todo use exceptions instead
|
||||
*/
|
||||
public function validate()
|
||||
{
|
||||
// check if the option name is valid
|
||||
if (!preg_match('/^[a-zA-Z_\x7f-\xff]+[a-zA-Z0-9_\x7f-\xff]*$/',
|
||||
$this->name)) {
|
||||
Console_CommandLine::triggerError('option_bad_name',
|
||||
E_USER_ERROR, array('{$name}' => $this->name));
|
||||
}
|
||||
// call the parent validate method
|
||||
parent::validate();
|
||||
// a short_name or a long_name must be provided
|
||||
if ($this->short_name == null && $this->long_name == null) {
|
||||
Console_CommandLine::triggerError('option_long_and_short_name_missing',
|
||||
E_USER_ERROR, array('{$name}' => $this->name));
|
||||
}
|
||||
// check if the option short_name is valid
|
||||
if ($this->short_name != null &&
|
||||
!(preg_match('/^\-[a-zA-Z]{1}$/', $this->short_name))) {
|
||||
Console_CommandLine::triggerError('option_bad_short_name',
|
||||
E_USER_ERROR, array(
|
||||
'{$name}' => $this->name,
|
||||
'{$short_name}' => $this->short_name
|
||||
));
|
||||
}
|
||||
// check if the option long_name is valid
|
||||
if ($this->long_name != null &&
|
||||
!preg_match('/^\-\-[a-zA-Z]+[a-zA-Z0-9_\-]*$/', $this->long_name)) {
|
||||
Console_CommandLine::triggerError('option_bad_long_name',
|
||||
E_USER_ERROR, array(
|
||||
'{$name}' => $this->name,
|
||||
'{$long_name}' => $this->long_name
|
||||
));
|
||||
}
|
||||
// check if we have a valid action
|
||||
if (!is_string($this->action)) {
|
||||
Console_CommandLine::triggerError('option_bad_action',
|
||||
E_USER_ERROR, array('{$name}' => $this->name));
|
||||
}
|
||||
if (!isset(Console_CommandLine::$actions[$this->action])) {
|
||||
Console_CommandLine::triggerError('option_unregistered_action',
|
||||
E_USER_ERROR, array(
|
||||
'{$action}' => $this->action,
|
||||
'{$name}' => $this->name
|
||||
));
|
||||
}
|
||||
// if the action is a callback, check that we have a valid callback
|
||||
if ($this->action == 'Callback' && !is_callable($this->callback)) {
|
||||
Console_CommandLine::triggerError('option_invalid_callback',
|
||||
E_USER_ERROR, array('{$name}' => $this->name));
|
||||
}
|
||||
}
|
||||
|
||||
// }}}
|
||||
// setDefaults() {{{
|
||||
|
||||
/**
|
||||
* Set the default value according to the configured action.
|
||||
*
|
||||
* Note that for backward compatibility issues this method is only called
|
||||
* when the 'force_options_defaults' is set to true, it will become the
|
||||
* default behaviour in the next major release of Console_CommandLine.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setDefaults()
|
||||
{
|
||||
if ($this->default !== null) {
|
||||
// already set
|
||||
return;
|
||||
}
|
||||
switch ($this->action) {
|
||||
case 'Counter':
|
||||
case 'StoreInt':
|
||||
$this->default = 0;
|
||||
break;
|
||||
case 'StoreFloat':
|
||||
$this->default = 0.0;
|
||||
break;
|
||||
case 'StoreArray':
|
||||
$this->default = array();
|
||||
break;
|
||||
case 'StoreTrue':
|
||||
$this->default = false;
|
||||
break;
|
||||
case 'StoreFalse':
|
||||
$this->default = true;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// }}}
|
||||
}
|
||||
Generated
Vendored
+63
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
|
||||
|
||||
/**
|
||||
* This file is part of the PEAR Console_CommandLine package.
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* LICENSE: This source file is subject to the MIT license that is available
|
||||
* through the world-wide-web at the following URI:
|
||||
* http://opensource.org/licenses/mit-license.php
|
||||
*
|
||||
* @category Console
|
||||
* @package Console_CommandLine
|
||||
* @author David JEAN LOUIS <izimobil@gmail.com>
|
||||
* @copyright 2007 David JEAN LOUIS
|
||||
* @license http://opensource.org/licenses/mit-license.php MIT License
|
||||
* @version CVS: $Id$
|
||||
* @link http://pear.php.net/package/Console_CommandLine
|
||||
* @since File available since release 0.1.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
/**
|
||||
* Outputters common interface, all outputters must implement this interface.
|
||||
*
|
||||
* @category Console
|
||||
* @package Console_CommandLine
|
||||
* @author David JEAN LOUIS <izimobil@gmail.com>
|
||||
* @copyright 2007 David JEAN LOUIS
|
||||
* @license http://opensource.org/licenses/mit-license.php MIT License
|
||||
* @version Release: 1.2.2
|
||||
* @link http://pear.php.net/package/Console_CommandLine
|
||||
* @since Class available since release 0.1.0
|
||||
*/
|
||||
interface Console_CommandLine_Outputter
|
||||
{
|
||||
// stdout() {{{
|
||||
|
||||
/**
|
||||
* Processes the output for a message that should be displayed on STDOUT.
|
||||
*
|
||||
* @param string $msg The message to output
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function stdout($msg);
|
||||
|
||||
// }}}
|
||||
// stderr() {{{
|
||||
|
||||
/**
|
||||
* Processes the output for a message that should be displayed on STDERR.
|
||||
*
|
||||
* @param string $msg The message to output
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function stderr($msg);
|
||||
|
||||
// }}}
|
||||
}
|
||||
Generated
Vendored
+82
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
|
||||
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
|
||||
|
||||
/**
|
||||
* This file is part of the PEAR Console_CommandLine package.
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* LICENSE: This source file is subject to the MIT license that is available
|
||||
* through the world-wide-web at the following URI:
|
||||
* http://opensource.org/licenses/mit-license.php
|
||||
*
|
||||
* @category Console
|
||||
* @package Console_CommandLine
|
||||
* @author David JEAN LOUIS <izimobil@gmail.com>
|
||||
* @copyright 2007 David JEAN LOUIS
|
||||
* @license http://opensource.org/licenses/mit-license.php MIT License
|
||||
* @version CVS: $Id$
|
||||
* @link http://pear.php.net/package/Console_CommandLine
|
||||
* @since File available since release 0.1.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
/**
|
||||
* The Outputter interface.
|
||||
*/
|
||||
require_once 'Console/CommandLine/Outputter.php';
|
||||
|
||||
/**
|
||||
* Console_CommandLine default Outputter.
|
||||
*
|
||||
* @category Console
|
||||
* @package Console_CommandLine
|
||||
* @author David JEAN LOUIS <izimobil@gmail.com>
|
||||
* @copyright 2007 David JEAN LOUIS
|
||||
* @license http://opensource.org/licenses/mit-license.php MIT License
|
||||
* @version Release: 1.2.2
|
||||
* @link http://pear.php.net/package/Console_CommandLine
|
||||
* @since Class available since release 0.1.0
|
||||
*/
|
||||
class Console_CommandLine_Outputter_Default implements Console_CommandLine_Outputter
|
||||
{
|
||||
// stdout() {{{
|
||||
|
||||
/**
|
||||
* Writes the message $msg to STDOUT.
|
||||
*
|
||||
* @param string $msg The message to output
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function stdout($msg)
|
||||
{
|
||||
if (defined('STDOUT')) {
|
||||
fwrite(STDOUT, $msg);
|
||||
} else {
|
||||
echo $msg;
|
||||
}
|
||||
}
|
||||
|
||||
// }}}
|
||||
// stderr() {{{
|
||||
|
||||
/**
|
||||
* Writes the message $msg to STDERR.
|
||||
*
|
||||
* @param string $msg The message to output
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function stderr($msg)
|
||||
{
|
||||
if (defined('STDERR')) {
|
||||
fwrite(STDERR, $msg);
|
||||
} else {
|
||||
echo $msg;
|
||||
}
|
||||
}
|
||||
|
||||
// }}}
|
||||
}
|
||||
Generated
Vendored
+71
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
|
||||
|
||||
/**
|
||||
* This file is part of the PEAR Console_CommandLine package.
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* LICENSE: This source file is subject to the MIT license that is available
|
||||
* through the world-wide-web at the following URI:
|
||||
* http://opensource.org/licenses/mit-license.php
|
||||
*
|
||||
* @category Console
|
||||
* @package Console_CommandLine
|
||||
* @author David JEAN LOUIS <izimobil@gmail.com>
|
||||
* @copyright 2007 David JEAN LOUIS
|
||||
* @license http://opensource.org/licenses/mit-license.php MIT License
|
||||
* @version CVS: $Id$
|
||||
* @link http://pear.php.net/package/Console_CommandLine
|
||||
* @since File available since release 0.1.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
/**
|
||||
* Renderers common interface, all renderers must implement this interface.
|
||||
*
|
||||
* @category Console
|
||||
* @package Console_CommandLine
|
||||
* @author David JEAN LOUIS <izimobil@gmail.com>
|
||||
* @copyright 2007 David JEAN LOUIS
|
||||
* @license http://opensource.org/licenses/mit-license.php MIT License
|
||||
* @version Release: 1.2.2
|
||||
* @link http://pear.php.net/package/Console_CommandLine
|
||||
* @since Class available since release 0.1.0
|
||||
*/
|
||||
interface Console_CommandLine_Renderer
|
||||
{
|
||||
// usage() {{{
|
||||
|
||||
/**
|
||||
* Returns the full usage message.
|
||||
*
|
||||
* @return string The usage message
|
||||
*/
|
||||
public function usage();
|
||||
|
||||
// }}}
|
||||
// error() {{{
|
||||
|
||||
/**
|
||||
* Returns a formatted error message.
|
||||
*
|
||||
* @param string $error The error message to format
|
||||
*
|
||||
* @return string The error string
|
||||
*/
|
||||
public function error($error);
|
||||
|
||||
// }}}
|
||||
// version() {{{
|
||||
|
||||
/**
|
||||
* Returns the program version string.
|
||||
*
|
||||
* @return string The version string
|
||||
*/
|
||||
public function version();
|
||||
|
||||
// }}}
|
||||
}
|
||||
Generated
Vendored
+430
@@ -0,0 +1,430 @@
|
||||
<?php
|
||||
|
||||
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
|
||||
|
||||
/**
|
||||
* This file is part of the PEAR Console_CommandLine package.
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* LICENSE: This source file is subject to the MIT license that is available
|
||||
* through the world-wide-web at the following URI:
|
||||
* http://opensource.org/licenses/mit-license.php
|
||||
*
|
||||
* @category Console
|
||||
* @package Console_CommandLine
|
||||
* @author David JEAN LOUIS <izimobil@gmail.com>
|
||||
* @copyright 2007 David JEAN LOUIS
|
||||
* @license http://opensource.org/licenses/mit-license.php MIT License
|
||||
* @version CVS: $Id$
|
||||
* @link http://pear.php.net/package/Console_CommandLine
|
||||
* @since File available since release 0.1.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
/**
|
||||
* The renderer interface.
|
||||
*/
|
||||
require_once 'Console/CommandLine/Renderer.php';
|
||||
|
||||
/**
|
||||
* Console_CommandLine default renderer.
|
||||
*
|
||||
* @category Console
|
||||
* @package Console_CommandLine
|
||||
* @author David JEAN LOUIS <izimobil@gmail.com>
|
||||
* @copyright 2007 David JEAN LOUIS
|
||||
* @license http://opensource.org/licenses/mit-license.php MIT License
|
||||
* @version Release: 1.2.2
|
||||
* @link http://pear.php.net/package/Console_CommandLine
|
||||
* @since Class available since release 0.1.0
|
||||
*/
|
||||
class Console_CommandLine_Renderer_Default implements Console_CommandLine_Renderer
|
||||
{
|
||||
// Properties {{{
|
||||
|
||||
/**
|
||||
* Integer that define the max width of the help text.
|
||||
*
|
||||
* @var integer $line_width Line width
|
||||
*/
|
||||
public $line_width = 75;
|
||||
|
||||
/**
|
||||
* Integer that define the max width of the help text.
|
||||
*
|
||||
* @var integer $line_width Line width
|
||||
*/
|
||||
public $options_on_different_lines = false;
|
||||
|
||||
/**
|
||||
* An instance of Console_CommandLine.
|
||||
*
|
||||
* @var Console_CommandLine $parser The parser
|
||||
*/
|
||||
public $parser = false;
|
||||
|
||||
// }}}
|
||||
// __construct() {{{
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param object $parser A Console_CommandLine instance
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($parser = false)
|
||||
{
|
||||
$this->parser = $parser;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// usage() {{{
|
||||
|
||||
/**
|
||||
* Returns the full usage message.
|
||||
*
|
||||
* @return string The usage message
|
||||
*/
|
||||
public function usage()
|
||||
{
|
||||
$ret = '';
|
||||
if (!empty($this->parser->description)) {
|
||||
$ret .= $this->description() . "\n\n";
|
||||
}
|
||||
$ret .= $this->usageLine() . "\n";
|
||||
if (count($this->parser->commands) > 0) {
|
||||
$ret .= $this->commandUsageLine() . "\n";
|
||||
}
|
||||
if (count($this->parser->options) > 0) {
|
||||
$ret .= "\n" . $this->optionList() . "\n";
|
||||
}
|
||||
if (count($this->parser->args) > 0) {
|
||||
$ret .= "\n" . $this->argumentList() . "\n";
|
||||
}
|
||||
if (count($this->parser->commands) > 0) {
|
||||
$ret .= "\n" . $this->commandList() . "\n";
|
||||
}
|
||||
$ret .= "\n";
|
||||
return $ret;
|
||||
}
|
||||
// }}}
|
||||
// error() {{{
|
||||
|
||||
/**
|
||||
* Returns a formatted error message.
|
||||
*
|
||||
* @param string $error The error message to format
|
||||
*
|
||||
* @return string The error string
|
||||
*/
|
||||
public function error($error)
|
||||
{
|
||||
$ret = 'Error: ' . $error . "\n";
|
||||
if ($this->parser->add_help_option) {
|
||||
$name = $this->name();
|
||||
$ret .= $this->wrap($this->parser->message_provider->get('PROG_HELP_LINE',
|
||||
array('progname' => $name))) . "\n";
|
||||
if (count($this->parser->commands) > 0) {
|
||||
$ret .= $this->wrap($this->parser->message_provider->get('COMMAND_HELP_LINE',
|
||||
array('progname' => $name))) . "\n";
|
||||
}
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// version() {{{
|
||||
|
||||
/**
|
||||
* Returns the program version string.
|
||||
*
|
||||
* @return string The version string
|
||||
*/
|
||||
public function version()
|
||||
{
|
||||
return $this->parser->message_provider->get('PROG_VERSION_LINE', array(
|
||||
'progname' => $this->name(),
|
||||
'version' => $this->parser->version
|
||||
)) . "\n";
|
||||
}
|
||||
|
||||
// }}}
|
||||
// name() {{{
|
||||
|
||||
/**
|
||||
* Returns the full name of the program or the sub command
|
||||
*
|
||||
* @return string The name of the program
|
||||
*/
|
||||
protected function name()
|
||||
{
|
||||
$name = $this->parser->name;
|
||||
$parent = $this->parser->parent;
|
||||
while ($parent) {
|
||||
if (count($parent->options) > 0) {
|
||||
$name = '['
|
||||
. strtolower($this->parser->message_provider->get('OPTION_WORD',
|
||||
array('plural' => 's')))
|
||||
. '] ' . $name;
|
||||
}
|
||||
$name = $parent->name . ' ' . $name;
|
||||
$parent = $parent->parent;
|
||||
}
|
||||
return $this->wrap($name);
|
||||
}
|
||||
|
||||
// }}}
|
||||
// description() {{{
|
||||
|
||||
/**
|
||||
* Returns the command line description message.
|
||||
*
|
||||
* @return string The description message
|
||||
*/
|
||||
protected function description()
|
||||
{
|
||||
return $this->wrap($this->parser->description);
|
||||
}
|
||||
|
||||
// }}}
|
||||
// usageLine() {{{
|
||||
|
||||
/**
|
||||
* Returns the command line usage message
|
||||
*
|
||||
* @return string the usage message
|
||||
*/
|
||||
protected function usageLine()
|
||||
{
|
||||
$usage = $this->parser->message_provider->get('USAGE_WORD') . ":\n";
|
||||
$ret = $usage . ' ' . $this->name();
|
||||
if (count($this->parser->options) > 0) {
|
||||
$ret .= ' ['
|
||||
. strtolower($this->parser->message_provider->get('OPTION_WORD'))
|
||||
. ']';
|
||||
}
|
||||
if (count($this->parser->args) > 0) {
|
||||
foreach ($this->parser->args as $name=>$arg) {
|
||||
$arg_str = $arg->help_name;
|
||||
if ($arg->multiple) {
|
||||
$arg_str .= '1 ' . $arg->help_name . '2 ...';
|
||||
}
|
||||
if ($arg->optional) {
|
||||
$arg_str = '[' . $arg_str . ']';
|
||||
}
|
||||
$ret .= ' ' . $arg_str;
|
||||
}
|
||||
}
|
||||
return $this->columnWrap($ret, 2);
|
||||
}
|
||||
|
||||
// }}}
|
||||
// commandUsageLine() {{{
|
||||
|
||||
/**
|
||||
* Returns the command line usage message for subcommands.
|
||||
*
|
||||
* @return string The usage line
|
||||
*/
|
||||
protected function commandUsageLine()
|
||||
{
|
||||
if (count($this->parser->commands) == 0) {
|
||||
return '';
|
||||
}
|
||||
$ret = ' ' . $this->name();
|
||||
if (count($this->parser->options) > 0) {
|
||||
$ret .= ' ['
|
||||
. strtolower($this->parser->message_provider->get('OPTION_WORD'))
|
||||
. ']';
|
||||
}
|
||||
$ret .= " <command>";
|
||||
$hasArgs = false;
|
||||
$hasOptions = false;
|
||||
foreach ($this->parser->commands as $command) {
|
||||
if (!$hasArgs && count($command->args) > 0) {
|
||||
$hasArgs = true;
|
||||
}
|
||||
if (!$hasOptions && ($command->add_help_option ||
|
||||
$command->add_version_option || count($command->options) > 0)) {
|
||||
$hasOptions = true;
|
||||
}
|
||||
}
|
||||
if ($hasOptions) {
|
||||
$ret .= ' [options]';
|
||||
}
|
||||
if ($hasArgs) {
|
||||
$ret .= ' [args]';
|
||||
}
|
||||
return $this->columnWrap($ret, 2);
|
||||
}
|
||||
|
||||
// }}}
|
||||
// argumentList() {{{
|
||||
|
||||
/**
|
||||
* Render the arguments list that will be displayed to the user, you can
|
||||
* override this method if you want to change the look of the list.
|
||||
*
|
||||
* @return string The formatted argument list
|
||||
*/
|
||||
protected function argumentList()
|
||||
{
|
||||
$col = 0;
|
||||
$args = array();
|
||||
foreach ($this->parser->args as $arg) {
|
||||
$argstr = ' ' . $arg->toString();
|
||||
$args[] = array($argstr, $arg->description);
|
||||
$ln = strlen($argstr);
|
||||
if ($col < $ln) {
|
||||
$col = $ln;
|
||||
}
|
||||
}
|
||||
$ret = $this->parser->message_provider->get('ARGUMENT_WORD') . ":";
|
||||
foreach ($args as $arg) {
|
||||
$text = str_pad($arg[0], $col) . ' ' . $arg[1];
|
||||
$ret .= "\n" . $this->columnWrap($text, $col+2);
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// optionList() {{{
|
||||
|
||||
/**
|
||||
* Render the options list that will be displayed to the user, you can
|
||||
* override this method if you want to change the look of the list.
|
||||
*
|
||||
* @return string The formatted option list
|
||||
*/
|
||||
protected function optionList()
|
||||
{
|
||||
$col = 0;
|
||||
$options = array();
|
||||
foreach ($this->parser->options as $option) {
|
||||
$delim = $this->options_on_different_lines ? "\n" : ', ';
|
||||
$optstr = $option->toString($delim);
|
||||
$lines = explode("\n", $optstr);
|
||||
$lines[0] = ' ' . $lines[0];
|
||||
if (count($lines) > 1) {
|
||||
$lines[1] = ' ' . $lines[1];
|
||||
$ln = strlen($lines[1]);
|
||||
} else {
|
||||
$ln = strlen($lines[0]);
|
||||
}
|
||||
$options[] = array($lines, $option->description);
|
||||
if ($col < $ln) {
|
||||
$col = $ln;
|
||||
}
|
||||
}
|
||||
$ret = $this->parser->message_provider->get('OPTION_WORD') . ":";
|
||||
foreach ($options as $option) {
|
||||
if (count($option[0]) > 1) {
|
||||
$text = str_pad($option[0][1], $col) . ' ' . $option[1];
|
||||
$pre = $option[0][0] . "\n";
|
||||
} else {
|
||||
$text = str_pad($option[0][0], $col) . ' ' . $option[1];
|
||||
$pre = '';
|
||||
}
|
||||
$ret .= "\n" . $pre . $this->columnWrap($text, $col+2);
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// commandList() {{{
|
||||
|
||||
/**
|
||||
* Render the command list that will be displayed to the user, you can
|
||||
* override this method if you want to change the look of the list.
|
||||
*
|
||||
* @return string The formatted subcommand list
|
||||
*/
|
||||
protected function commandList()
|
||||
{
|
||||
|
||||
$commands = array();
|
||||
$col = 0;
|
||||
foreach ($this->parser->commands as $cmdname=>$command) {
|
||||
$cmdname = ' ' . $cmdname;
|
||||
$commands[] = array($cmdname, $command->description, $command->aliases);
|
||||
$ln = strlen($cmdname);
|
||||
if ($col < $ln) {
|
||||
$col = $ln;
|
||||
}
|
||||
}
|
||||
$ret = $this->parser->message_provider->get('COMMAND_WORD') . ":";
|
||||
foreach ($commands as $command) {
|
||||
$text = str_pad($command[0], $col) . ' ' . $command[1];
|
||||
if ($aliasesCount = count($command[2])) {
|
||||
$pad = '';
|
||||
$text .= ' (';
|
||||
$text .= $aliasesCount > 1 ? 'aliases: ' : 'alias: ';
|
||||
foreach ($command[2] as $alias) {
|
||||
$text .= $pad . $alias;
|
||||
$pad = ', ';
|
||||
}
|
||||
$text .= ')';
|
||||
}
|
||||
$ret .= "\n" . $this->columnWrap($text, $col+2);
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// wrap() {{{
|
||||
|
||||
/**
|
||||
* Wraps the text passed to the method.
|
||||
*
|
||||
* @param string $text The text to wrap
|
||||
* @param int $lw The column width (defaults to line_width property)
|
||||
*
|
||||
* @return string The wrapped text
|
||||
*/
|
||||
protected function wrap($text, $lw=null)
|
||||
{
|
||||
if ($this->line_width > 0) {
|
||||
if ($lw === null) {
|
||||
$lw = $this->line_width;
|
||||
}
|
||||
return wordwrap($text, $lw, "\n", false);
|
||||
}
|
||||
return $text;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// columnWrap() {{{
|
||||
|
||||
/**
|
||||
* Wraps the text passed to the method at the specified width.
|
||||
*
|
||||
* @param string $text The text to wrap
|
||||
* @param int $cw The wrap width
|
||||
*
|
||||
* @return string The wrapped text
|
||||
*/
|
||||
protected function columnWrap($text, $cw)
|
||||
{
|
||||
$tokens = explode("\n", $this->wrap($text));
|
||||
$ret = $tokens[0];
|
||||
$text = trim(substr($text, strlen($ret)));
|
||||
if (empty($text)) {
|
||||
return $ret;
|
||||
}
|
||||
|
||||
$chunks = $this->wrap($text, $this->line_width - $cw);
|
||||
$tokens = explode("\n", $chunks);
|
||||
foreach ($tokens as $token) {
|
||||
if (!empty($token)) {
|
||||
$ret .= "\n" . str_repeat(' ', $cw) . $token;
|
||||
} else {
|
||||
$ret .= "\n";
|
||||
}
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
// }}}
|
||||
}
|
||||
Generated
Vendored
+71
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
|
||||
|
||||
/**
|
||||
* This file is part of the PEAR Console_CommandLine package.
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* LICENSE: This source file is subject to the MIT license that is available
|
||||
* through the world-wide-web at the following URI:
|
||||
* http://opensource.org/licenses/mit-license.php
|
||||
*
|
||||
* @category Console
|
||||
* @package Console_CommandLine
|
||||
* @author David JEAN LOUIS <izimobil@gmail.com>
|
||||
* @copyright 2007 David JEAN LOUIS
|
||||
* @license http://opensource.org/licenses/mit-license.php MIT License
|
||||
* @version CVS: $Id$
|
||||
* @link http://pear.php.net/package/Console_CommandLine
|
||||
* @since File available since release 0.1.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
/**
|
||||
* A lightweight class to store the result of the command line parsing.
|
||||
*
|
||||
* @category Console
|
||||
* @package Console_CommandLine
|
||||
* @author David JEAN LOUIS <izimobil@gmail.com>
|
||||
* @copyright 2007 David JEAN LOUIS
|
||||
* @license http://opensource.org/licenses/mit-license.php MIT License
|
||||
* @version Release: 1.2.2
|
||||
* @link http://pear.php.net/package/Console_CommandLine
|
||||
* @since Class available since release 0.1.0
|
||||
*/
|
||||
class Console_CommandLine_Result
|
||||
{
|
||||
// Public properties {{{
|
||||
|
||||
/**
|
||||
* The result options associative array.
|
||||
* Key is the name of the option and value its value.
|
||||
*
|
||||
* @var array $options Result options array
|
||||
*/
|
||||
public $options = array();
|
||||
|
||||
/**
|
||||
* The result arguments array.
|
||||
*
|
||||
* @var array $args Result arguments array
|
||||
*/
|
||||
public $args = array();
|
||||
|
||||
/**
|
||||
* Name of the command invoked by the user, false if no command invoked.
|
||||
*
|
||||
* @var string $command_name Result command name
|
||||
*/
|
||||
public $command_name = false;
|
||||
|
||||
/**
|
||||
* A result instance for the subcommand.
|
||||
*
|
||||
* @var Console_CommandLine_Result Result instance for the subcommand
|
||||
*/
|
||||
public $command = false;
|
||||
|
||||
// }}}
|
||||
}
|
||||
Generated
Vendored
+318
@@ -0,0 +1,318 @@
|
||||
<?php
|
||||
|
||||
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
|
||||
|
||||
/**
|
||||
* This file is part of the PEAR Console_CommandLine package.
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* LICENSE: This source file is subject to the MIT license that is available
|
||||
* through the world-wide-web at the following URI:
|
||||
* http://opensource.org/licenses/mit-license.php
|
||||
*
|
||||
* @category Console
|
||||
* @package Console_CommandLine
|
||||
* @author David JEAN LOUIS <izimobil@gmail.com>
|
||||
* @copyright 2007 David JEAN LOUIS
|
||||
* @license http://opensource.org/licenses/mit-license.php MIT License
|
||||
* @version CVS: $Id$
|
||||
* @link http://pear.php.net/package/Console_CommandLine
|
||||
* @since File available since release 0.1.0
|
||||
* @filesource
|
||||
*/
|
||||
|
||||
/**
|
||||
* Required file
|
||||
*/
|
||||
require_once 'Console/CommandLine.php';
|
||||
|
||||
/**
|
||||
* Parser for command line xml definitions.
|
||||
*
|
||||
* @category Console
|
||||
* @package Console_CommandLine
|
||||
* @author David JEAN LOUIS <izimobil@gmail.com>
|
||||
* @copyright 2007 David JEAN LOUIS
|
||||
* @license http://opensource.org/licenses/mit-license.php MIT License
|
||||
* @version Release: 1.2.2
|
||||
* @link http://pear.php.net/package/Console_CommandLine
|
||||
* @since Class available since release 0.1.0
|
||||
*/
|
||||
class Console_CommandLine_XmlParser
|
||||
{
|
||||
// parse() {{{
|
||||
|
||||
/**
|
||||
* Parses the given xml definition file and returns a
|
||||
* Console_CommandLine instance constructed with the xml data.
|
||||
*
|
||||
* @param string $xmlfile The xml file to parse
|
||||
*
|
||||
* @return Console_CommandLine A parser instance
|
||||
*/
|
||||
public static function parse($xmlfile)
|
||||
{
|
||||
if (!is_readable($xmlfile)) {
|
||||
Console_CommandLine::triggerError('invalid_xml_file',
|
||||
E_USER_ERROR, array('{$file}' => $xmlfile));
|
||||
}
|
||||
$doc = new DomDocument();
|
||||
$doc->load($xmlfile);
|
||||
self::validate($doc);
|
||||
$nodes = $doc->getElementsByTagName('command');
|
||||
$root = $nodes->item(0);
|
||||
return self::_parseCommandNode($root, true);
|
||||
}
|
||||
|
||||
// }}}
|
||||
// parseString() {{{
|
||||
|
||||
/**
|
||||
* Parses the given xml definition string and returns a
|
||||
* Console_CommandLine instance constructed with the xml data.
|
||||
*
|
||||
* @param string $xmlstr The xml string to parse
|
||||
*
|
||||
* @return Console_CommandLine A parser instance
|
||||
*/
|
||||
public static function parseString($xmlstr)
|
||||
{
|
||||
$doc = new DomDocument();
|
||||
$doc->loadXml($xmlstr);
|
||||
self::validate($doc);
|
||||
$nodes = $doc->getElementsByTagName('command');
|
||||
$root = $nodes->item(0);
|
||||
return self::_parseCommandNode($root, true);
|
||||
}
|
||||
|
||||
// }}}
|
||||
// validate() {{{
|
||||
|
||||
/**
|
||||
* Validates the xml definition using Relax NG.
|
||||
*
|
||||
* @param DomDocument $doc The document to validate
|
||||
*
|
||||
* @return boolean Whether the xml data is valid or not.
|
||||
* @throws Console_CommandLine_Exception
|
||||
* @todo use exceptions
|
||||
*/
|
||||
public static function validate($doc)
|
||||
{
|
||||
$pkgRoot = __DIR__ . '/../../';
|
||||
$paths = array(
|
||||
// PEAR/Composer
|
||||
'/www/roundcube/releases/roundcubemail-1.3-beta/vendor/pear-pear.php.net/Console_CommandLine/data/Console_CommandLine/data/xmlschema.rng',
|
||||
// Composer
|
||||
$pkgRoot . 'data/Console_CommandLine/data/xmlschema.rng',
|
||||
$pkgRoot . 'data/console_commandline/data/xmlschema.rng',
|
||||
// Git
|
||||
$pkgRoot . 'data/xmlschema.rng',
|
||||
'xmlschema.rng',
|
||||
);
|
||||
|
||||
foreach ($paths as $path) {
|
||||
if (is_readable($path)) {
|
||||
return $doc->relaxNGValidate($path);
|
||||
}
|
||||
}
|
||||
Console_CommandLine::triggerError(
|
||||
'invalid_xml_file',
|
||||
E_USER_ERROR, array('{$file}' => $rngfile));
|
||||
}
|
||||
|
||||
// }}}
|
||||
// _parseCommandNode() {{{
|
||||
|
||||
/**
|
||||
* Parses the root command node or a command node and returns the
|
||||
* constructed Console_CommandLine or Console_CommandLine_Command instance.
|
||||
*
|
||||
* @param DomDocumentNode $node The node to parse
|
||||
* @param bool $isRootNode Whether it is a root node or not
|
||||
*
|
||||
* @return mixed Console_CommandLine or Console_CommandLine_Command
|
||||
*/
|
||||
private static function _parseCommandNode($node, $isRootNode = false)
|
||||
{
|
||||
if ($isRootNode) {
|
||||
$obj = new Console_CommandLine();
|
||||
} else {
|
||||
include_once 'Console/CommandLine/Command.php';
|
||||
$obj = new Console_CommandLine_Command();
|
||||
}
|
||||
foreach ($node->childNodes as $cNode) {
|
||||
$cNodeName = $cNode->nodeName;
|
||||
switch ($cNodeName) {
|
||||
case 'name':
|
||||
case 'description':
|
||||
case 'version':
|
||||
$obj->$cNodeName = trim($cNode->nodeValue);
|
||||
break;
|
||||
case 'add_help_option':
|
||||
case 'add_version_option':
|
||||
case 'force_posix':
|
||||
$obj->$cNodeName = self::_bool(trim($cNode->nodeValue));
|
||||
break;
|
||||
case 'option':
|
||||
$obj->addOption(self::_parseOptionNode($cNode));
|
||||
break;
|
||||
case 'argument':
|
||||
$obj->addArgument(self::_parseArgumentNode($cNode));
|
||||
break;
|
||||
case 'command':
|
||||
$obj->addCommand(self::_parseCommandNode($cNode));
|
||||
break;
|
||||
case 'aliases':
|
||||
if (!$isRootNode) {
|
||||
foreach ($cNode->childNodes as $subChildNode) {
|
||||
if ($subChildNode->nodeName == 'alias') {
|
||||
$obj->aliases[] = trim($subChildNode->nodeValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'messages':
|
||||
$obj->messages = self::_messages($cNode);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return $obj;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// _parseOptionNode() {{{
|
||||
|
||||
/**
|
||||
* Parses an option node and returns the constructed
|
||||
* Console_CommandLine_Option instance.
|
||||
*
|
||||
* @param DomDocumentNode $node The node to parse
|
||||
*
|
||||
* @return Console_CommandLine_Option The built option
|
||||
*/
|
||||
private static function _parseOptionNode($node)
|
||||
{
|
||||
include_once 'Console/CommandLine/Option.php';
|
||||
$obj = new Console_CommandLine_Option($node->getAttribute('name'));
|
||||
foreach ($node->childNodes as $cNode) {
|
||||
$cNodeName = $cNode->nodeName;
|
||||
switch ($cNodeName) {
|
||||
case 'choices':
|
||||
foreach ($cNode->childNodes as $subChildNode) {
|
||||
if ($subChildNode->nodeName == 'choice') {
|
||||
$obj->choices[] = trim($subChildNode->nodeValue);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'messages':
|
||||
$obj->messages = self::_messages($cNode);
|
||||
break;
|
||||
default:
|
||||
if (property_exists($obj, $cNodeName)) {
|
||||
$obj->$cNodeName = trim($cNode->nodeValue);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($obj->action == 'Password') {
|
||||
$obj->argument_optional = true;
|
||||
}
|
||||
return $obj;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// _parseArgumentNode() {{{
|
||||
|
||||
/**
|
||||
* Parses an argument node and returns the constructed
|
||||
* Console_CommandLine_Argument instance.
|
||||
*
|
||||
* @param DomDocumentNode $node The node to parse
|
||||
*
|
||||
* @return Console_CommandLine_Argument The built argument
|
||||
*/
|
||||
private static function _parseArgumentNode($node)
|
||||
{
|
||||
include_once 'Console/CommandLine/Argument.php';
|
||||
$obj = new Console_CommandLine_Argument($node->getAttribute('name'));
|
||||
foreach ($node->childNodes as $cNode) {
|
||||
$cNodeName = $cNode->nodeName;
|
||||
switch ($cNodeName) {
|
||||
case 'description':
|
||||
case 'help_name':
|
||||
case 'default':
|
||||
$obj->$cNodeName = trim($cNode->nodeValue);
|
||||
break;
|
||||
case 'multiple':
|
||||
$obj->multiple = self::_bool(trim($cNode->nodeValue));
|
||||
break;
|
||||
case 'optional':
|
||||
$obj->optional = self::_bool(trim($cNode->nodeValue));
|
||||
break;
|
||||
case 'choices':
|
||||
foreach ($cNode->childNodes as $subChildNode) {
|
||||
if ($subChildNode->nodeName == 'choice') {
|
||||
$obj->choices[] = trim($subChildNode->nodeValue);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'messages':
|
||||
$obj->messages = self::_messages($cNode);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return $obj;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// _bool() {{{
|
||||
|
||||
/**
|
||||
* Returns a boolean according to true/false possible strings.
|
||||
*
|
||||
* @param string $str The string to process
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
private static function _bool($str)
|
||||
{
|
||||
return in_array(strtolower((string)$str), array('true', '1', 'on', 'yes'));
|
||||
}
|
||||
|
||||
// }}}
|
||||
// _messages() {{{
|
||||
|
||||
/**
|
||||
* Returns an array of custom messages for the element
|
||||
*
|
||||
* @param DOMNode $node The messages node to process
|
||||
*
|
||||
* @return array an array of messages
|
||||
*
|
||||
* @see Console_CommandLine::$messages
|
||||
* @see Console_CommandLine_Element::$messages
|
||||
*/
|
||||
private static function _messages(DOMNode $node)
|
||||
{
|
||||
$messages = array();
|
||||
|
||||
foreach ($node->childNodes as $cNode) {
|
||||
if ($cNode->nodeType == XML_ELEMENT_NODE) {
|
||||
$name = $cNode->getAttribute('name');
|
||||
$value = trim($cNode->nodeValue);
|
||||
|
||||
$messages[$name] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
return $messages;
|
||||
}
|
||||
|
||||
// }}}
|
||||
}
|
||||
data/web/rc/vendor/pear-pear.php.net/Console_CommandLine/data/Console_CommandLine/data/xmlschema.rng
Generated
Vendored
+234
@@ -0,0 +1,234 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!--
|
||||
This is the RNG file for validating Console_CommandLine xml definitions.
|
||||
|
||||
Author : David JEAN LOUIS
|
||||
Licence : MIT License
|
||||
Version : CVS: $Id$
|
||||
-->
|
||||
|
||||
<grammar xmlns="http://relaxng.org/ns/structure/1.0"
|
||||
datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
|
||||
|
||||
<!-- structure -->
|
||||
<start>
|
||||
<ref name="ref_command"/>
|
||||
</start>
|
||||
|
||||
<!-- Command node -->
|
||||
<define name="ref_command_subcommand_common">
|
||||
<interleave>
|
||||
<optional>
|
||||
<element name="name">
|
||||
<text/>
|
||||
</element>
|
||||
</optional>
|
||||
<optional>
|
||||
<element name="description">
|
||||
<text/>
|
||||
</element>
|
||||
</optional>
|
||||
<optional>
|
||||
<element name="version">
|
||||
<text/>
|
||||
</element>
|
||||
</optional>
|
||||
<optional>
|
||||
<element name="add_help_option">
|
||||
<ref name="ref_bool_choices"/>
|
||||
</element>
|
||||
</optional>
|
||||
<optional>
|
||||
<element name="add_version_option">
|
||||
<ref name="ref_bool_choices"/>
|
||||
</element>
|
||||
</optional>
|
||||
<optional>
|
||||
<element name="force_posix">
|
||||
<ref name="ref_bool_choices"/>
|
||||
</element>
|
||||
</optional>
|
||||
<optional>
|
||||
<ref name="ref_messages_common"/>
|
||||
</optional>
|
||||
<zeroOrMore>
|
||||
<ref name="ref_option"/>
|
||||
</zeroOrMore>
|
||||
<zeroOrMore>
|
||||
<ref name="ref_argument"/>
|
||||
</zeroOrMore>
|
||||
<zeroOrMore>
|
||||
<ref name="ref_subcommand"/>
|
||||
</zeroOrMore>
|
||||
</interleave>
|
||||
</define>
|
||||
|
||||
<!-- command element -->
|
||||
|
||||
<define name="ref_command">
|
||||
<element name="command">
|
||||
<interleave>
|
||||
<ref name="ref_command_subcommand_common"/>
|
||||
</interleave>
|
||||
</element>
|
||||
</define>
|
||||
|
||||
<!-- subcommand element -->
|
||||
|
||||
<define name="ref_subcommand">
|
||||
<element name="command">
|
||||
<interleave>
|
||||
<ref name="ref_command_subcommand_common"/>
|
||||
<optional>
|
||||
<element name="aliases">
|
||||
<zeroOrMore>
|
||||
<element name="alias">
|
||||
<text/>
|
||||
</element>
|
||||
</zeroOrMore>
|
||||
</element>
|
||||
</optional>
|
||||
</interleave>
|
||||
</element>
|
||||
</define>
|
||||
|
||||
<!-- custom messages common element -->
|
||||
|
||||
<define name="ref_messages_common">
|
||||
<element name="messages">
|
||||
<oneOrMore>
|
||||
<element name="message">
|
||||
<attribute name="name">
|
||||
<data type="string"/>
|
||||
</attribute>
|
||||
<text/>
|
||||
</element>
|
||||
</oneOrMore>
|
||||
</element>
|
||||
</define>
|
||||
|
||||
<!-- options and arguments common elements -->
|
||||
|
||||
<define name="ref_option_argument_common">
|
||||
<interleave>
|
||||
<optional>
|
||||
<element name="description">
|
||||
<text/>
|
||||
</element>
|
||||
</optional>
|
||||
<optional>
|
||||
<element name="help_name">
|
||||
<text/>
|
||||
</element>
|
||||
</optional>
|
||||
<optional>
|
||||
<element name="default">
|
||||
<text/>
|
||||
</element>
|
||||
</optional>
|
||||
<optional>
|
||||
<ref name="ref_messages_common"/>
|
||||
</optional>
|
||||
</interleave>
|
||||
</define>
|
||||
|
||||
<!-- Option node -->
|
||||
<define name="ref_option">
|
||||
<element name="option">
|
||||
<attribute name="name">
|
||||
<data type="string"/>
|
||||
</attribute>
|
||||
<interleave>
|
||||
<optional>
|
||||
<element name="short_name">
|
||||
<text/>
|
||||
</element>
|
||||
</optional>
|
||||
<optional>
|
||||
<element name="long_name">
|
||||
<text/>
|
||||
</element>
|
||||
</optional>
|
||||
<ref name="ref_option_argument_common"/>
|
||||
<optional>
|
||||
<element name="action">
|
||||
<text/>
|
||||
</element>
|
||||
</optional>
|
||||
<optional>
|
||||
<element name="choices">
|
||||
<zeroOrMore>
|
||||
<element name="choice">
|
||||
<text/>
|
||||
</element>
|
||||
</zeroOrMore>
|
||||
</element>
|
||||
</optional>
|
||||
<optional>
|
||||
<element name="add_list_option">
|
||||
<ref name="ref_bool_choices"/>
|
||||
</element>
|
||||
</optional>
|
||||
</interleave>
|
||||
</element>
|
||||
</define>
|
||||
|
||||
<!-- Argument node -->
|
||||
<define name="ref_argument">
|
||||
<element name="argument">
|
||||
<attribute name="name">
|
||||
<data type="string"/>
|
||||
</attribute>
|
||||
<interleave>
|
||||
<ref name="ref_option_argument_common"/>
|
||||
<optional>
|
||||
<element name="multiple">
|
||||
<ref name="ref_bool_choices"/>
|
||||
</element>
|
||||
</optional>
|
||||
<optional>
|
||||
<element name="optional">
|
||||
<ref name="ref_bool_choices"/>
|
||||
</element>
|
||||
</optional>
|
||||
<optional>
|
||||
<element name="choices">
|
||||
<zeroOrMore>
|
||||
<element name="choice">
|
||||
<text/>
|
||||
</element>
|
||||
</zeroOrMore>
|
||||
</element>
|
||||
</optional>
|
||||
</interleave>
|
||||
</element>
|
||||
</define>
|
||||
|
||||
<!-- boolean choices -->
|
||||
<define name="ref_bool_choices">
|
||||
<choice>
|
||||
<data type="token">
|
||||
<param name="pattern">[Tt][Rr][Uu][Ee]</param>
|
||||
</data>
|
||||
<data type="token">
|
||||
<param name="pattern">[On][Nn]</param>
|
||||
</data>
|
||||
<data type="token">
|
||||
<param name="pattern">[Yy][Ee][Ss]</param>
|
||||
</data>
|
||||
<value>1</value>
|
||||
<data type="token">
|
||||
<param name="pattern">[Ff][Aa][Ll][Ss][Ee]</param>
|
||||
</data>
|
||||
<data type="token">
|
||||
<param name="pattern">[Of][Ff][Ff]</param>
|
||||
</data>
|
||||
<data type="token">
|
||||
<param name="pattern">[Nn][Oo]</param>
|
||||
</data>
|
||||
<value>0</value>
|
||||
</choice>
|
||||
</define>
|
||||
|
||||
</grammar>
|
||||
+2155
File diff suppressed because it is too large
Load Diff
+2048
File diff suppressed because it is too large
Load Diff
+597
@@ -0,0 +1,597 @@
|
||||
<?php
|
||||
|
||||
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
|
||||
|
||||
/**
|
||||
* Various exception handling classes for Crypt_GPG
|
||||
*
|
||||
* Crypt_GPG provides an object oriented interface to GNU Privacy
|
||||
* Guard (GPG). It requires the GPG executable to be on the system.
|
||||
*
|
||||
* This file contains various exception classes used by the Crypt_GPG package.
|
||||
*
|
||||
* 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, see
|
||||
* <http://www.gnu.org/licenses/>
|
||||
*
|
||||
* @category Encryption
|
||||
* @package Crypt_GPG
|
||||
* @author Nathan Fredrickson <nathan@silverorange.com>
|
||||
* @author Michael Gauthier <mike@silverorange.com>
|
||||
* @copyright 2005-2011 silverorange
|
||||
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
|
||||
* @link http://pear.php.net/package/Crypt_GPG
|
||||
*/
|
||||
|
||||
/**
|
||||
* PEAR Exception handler and base class
|
||||
*/
|
||||
require_once 'PEAR/Exception.php';
|
||||
|
||||
// {{{ class Crypt_GPG_Exception
|
||||
|
||||
/**
|
||||
* An exception thrown by the Crypt_GPG package
|
||||
*
|
||||
* @category Encryption
|
||||
* @package Crypt_GPG
|
||||
* @author Michael Gauthier <mike@silverorange.com>
|
||||
* @copyright 2005 silverorange
|
||||
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
|
||||
* @link http://pear.php.net/package/Crypt_GPG
|
||||
*/
|
||||
class Crypt_GPG_Exception extends PEAR_Exception
|
||||
{
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ class Crypt_GPG_FileException
|
||||
|
||||
/**
|
||||
* An exception thrown when a file is used in ways it cannot be used
|
||||
*
|
||||
* For example, if an output file is specified and the file is not writeable, or
|
||||
* if an input file is specified and the file is not readable, this exception
|
||||
* is thrown.
|
||||
*
|
||||
* @category Encryption
|
||||
* @package Crypt_GPG
|
||||
* @author Michael Gauthier <mike@silverorange.com>
|
||||
* @copyright 2007-2008 silverorange
|
||||
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
|
||||
* @link http://pear.php.net/package/Crypt_GPG
|
||||
*/
|
||||
class Crypt_GPG_FileException extends Crypt_GPG_Exception
|
||||
{
|
||||
// {{{ private class properties
|
||||
|
||||
/**
|
||||
* The name of the file that caused this exception
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $_filename = '';
|
||||
|
||||
// }}}
|
||||
// {{{ __construct()
|
||||
|
||||
/**
|
||||
* Creates a new Crypt_GPG_FileException
|
||||
*
|
||||
* @param string $message an error message.
|
||||
* @param integer $code a user defined error code.
|
||||
* @param string $filename the name of the file that caused this exception.
|
||||
*/
|
||||
public function __construct($message, $code = 0, $filename = '')
|
||||
{
|
||||
$this->_filename = $filename;
|
||||
parent::__construct($message, $code);
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ getFilename()
|
||||
|
||||
/**
|
||||
* Returns the filename of the file that caused this exception
|
||||
*
|
||||
* @return string the filename of the file that caused this exception.
|
||||
*
|
||||
* @see Crypt_GPG_FileException::$_filename
|
||||
*/
|
||||
public function getFilename()
|
||||
{
|
||||
return $this->_filename;
|
||||
}
|
||||
|
||||
// }}}
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ class Crypt_GPG_OpenSubprocessException
|
||||
|
||||
/**
|
||||
* An exception thrown when the GPG subprocess cannot be opened
|
||||
*
|
||||
* This exception is thrown when the {@link Crypt_GPG_Engine} tries to open a
|
||||
* new subprocess and fails.
|
||||
*
|
||||
* @category Encryption
|
||||
* @package Crypt_GPG
|
||||
* @author Michael Gauthier <mike@silverorange.com>
|
||||
* @copyright 2005 silverorange
|
||||
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
|
||||
* @link http://pear.php.net/package/Crypt_GPG
|
||||
*/
|
||||
class Crypt_GPG_OpenSubprocessException extends Crypt_GPG_Exception
|
||||
{
|
||||
// {{{ private class properties
|
||||
|
||||
/**
|
||||
* The command used to try to open the subprocess
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $_command = '';
|
||||
|
||||
// }}}
|
||||
// {{{ __construct()
|
||||
|
||||
/**
|
||||
* Creates a new Crypt_GPG_OpenSubprocessException
|
||||
*
|
||||
* @param string $message an error message.
|
||||
* @param integer $code a user defined error code.
|
||||
* @param string $command the command that was called to open the
|
||||
* new subprocess.
|
||||
*
|
||||
* @see Crypt_GPG::_openSubprocess()
|
||||
*/
|
||||
public function __construct($message, $code = 0, $command = '')
|
||||
{
|
||||
$this->_command = $command;
|
||||
parent::__construct($message, $code);
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ getCommand()
|
||||
|
||||
/**
|
||||
* Returns the contents of the internal _command property
|
||||
*
|
||||
* @return string the command used to open the subprocess.
|
||||
*
|
||||
* @see Crypt_GPG_OpenSubprocessException::$_command
|
||||
*/
|
||||
public function getCommand()
|
||||
{
|
||||
return $this->_command;
|
||||
}
|
||||
|
||||
// }}}
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ class Crypt_GPG_InvalidOperationException
|
||||
|
||||
/**
|
||||
* An exception thrown when an invalid GPG operation is attempted
|
||||
*
|
||||
* @category Encryption
|
||||
* @package Crypt_GPG
|
||||
* @author Michael Gauthier <mike@silverorange.com>
|
||||
* @copyright 2008 silverorange
|
||||
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
|
||||
* @link http://pear.php.net/package/Crypt_GPG
|
||||
*/
|
||||
class Crypt_GPG_InvalidOperationException extends Crypt_GPG_Exception
|
||||
{
|
||||
// {{{ private class properties
|
||||
|
||||
/**
|
||||
* The attempted operation
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $_operation = '';
|
||||
|
||||
// }}}
|
||||
// {{{ __construct()
|
||||
|
||||
/**
|
||||
* Creates a new Crypt_GPG_OpenSubprocessException
|
||||
*
|
||||
* @param string $message an error message.
|
||||
* @param integer $code a user defined error code.
|
||||
* @param string $operation the operation.
|
||||
*/
|
||||
public function __construct($message, $code = 0, $operation = '')
|
||||
{
|
||||
$this->_operation = $operation;
|
||||
parent::__construct($message, $code);
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ getOperation()
|
||||
|
||||
/**
|
||||
* Returns the contents of the internal _operation property
|
||||
*
|
||||
* @return string the attempted operation.
|
||||
*
|
||||
* @see Crypt_GPG_InvalidOperationException::$_operation
|
||||
*/
|
||||
public function getOperation()
|
||||
{
|
||||
return $this->_operation;
|
||||
}
|
||||
|
||||
// }}}
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ class Crypt_GPG_KeyNotFoundException
|
||||
|
||||
/**
|
||||
* An exception thrown when Crypt_GPG fails to find the key for various
|
||||
* operations
|
||||
*
|
||||
* @category Encryption
|
||||
* @package Crypt_GPG
|
||||
* @author Michael Gauthier <mike@silverorange.com>
|
||||
* @copyright 2005 silverorange
|
||||
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
|
||||
* @link http://pear.php.net/package/Crypt_GPG
|
||||
*/
|
||||
class Crypt_GPG_KeyNotFoundException extends Crypt_GPG_Exception
|
||||
{
|
||||
// {{{ private class properties
|
||||
|
||||
/**
|
||||
* The key identifier that was searched for
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $_keyId = '';
|
||||
|
||||
// }}}
|
||||
// {{{ __construct()
|
||||
|
||||
/**
|
||||
* Creates a new Crypt_GPG_KeyNotFoundException
|
||||
*
|
||||
* @param string $message an error message.
|
||||
* @param integer $code a user defined error code.
|
||||
* @param string $keyId the key identifier of the key.
|
||||
*/
|
||||
public function __construct($message, $code = 0, $keyId= '')
|
||||
{
|
||||
$this->_keyId = $keyId;
|
||||
parent::__construct($message, $code);
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ getKeyId()
|
||||
|
||||
/**
|
||||
* Gets the key identifier of the key that was not found
|
||||
*
|
||||
* @return string the key identifier of the key that was not found.
|
||||
*/
|
||||
public function getKeyId()
|
||||
{
|
||||
return $this->_keyId;
|
||||
}
|
||||
|
||||
// }}}
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ class Crypt_GPG_NoDataException
|
||||
|
||||
/**
|
||||
* An exception thrown when Crypt_GPG cannot find valid data for various
|
||||
* operations
|
||||
*
|
||||
* @category Encryption
|
||||
* @package Crypt_GPG
|
||||
* @author Michael Gauthier <mike@silverorange.com>
|
||||
* @copyright 2006 silverorange
|
||||
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
|
||||
* @link http://pear.php.net/package/Crypt_GPG
|
||||
*/
|
||||
class Crypt_GPG_NoDataException extends Crypt_GPG_Exception
|
||||
{
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ class Crypt_GPG_BadPassphraseException
|
||||
|
||||
/**
|
||||
* An exception thrown when a required passphrase is incorrect or missing
|
||||
*
|
||||
* @category Encryption
|
||||
* @package Crypt_GPG
|
||||
* @author Michael Gauthier <mike@silverorange.com>
|
||||
* @copyright 2006-2008 silverorange
|
||||
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
|
||||
* @link http://pear.php.net/package/Crypt_GPG
|
||||
*/
|
||||
class Crypt_GPG_BadPassphraseException extends Crypt_GPG_Exception
|
||||
{
|
||||
// {{{ private class properties
|
||||
|
||||
/**
|
||||
* Keys for which the passhprase is missing
|
||||
*
|
||||
* This contains primary user ids indexed by sub-key id.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $_missingPassphrases = array();
|
||||
|
||||
/**
|
||||
* Keys for which the passhprase is incorrect
|
||||
*
|
||||
* This contains primary user ids indexed by sub-key id.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $_badPassphrases = array();
|
||||
|
||||
// }}}
|
||||
// {{{ __construct()
|
||||
|
||||
/**
|
||||
* Creates a new Crypt_GPG_BadPassphraseException
|
||||
*
|
||||
* @param string $message an error message.
|
||||
* @param integer $code a user defined error code.
|
||||
* @param array $badPassphrases an array containing user ids of keys
|
||||
* for which the passphrase is incorrect.
|
||||
* @param array $missingPassphrases an array containing user ids of keys
|
||||
* for which the passphrase is missing.
|
||||
*/
|
||||
public function __construct($message, $code = 0,
|
||||
array $badPassphrases = array(), array $missingPassphrases = array()
|
||||
) {
|
||||
$this->_badPassphrases = (array) $badPassphrases;
|
||||
$this->_missingPassphrases = (array) $missingPassphrases;
|
||||
|
||||
parent::__construct($message, $code);
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ getBadPassphrases()
|
||||
|
||||
/**
|
||||
* Gets keys for which the passhprase is incorrect
|
||||
*
|
||||
* @return array an array of keys for which the passphrase is incorrect.
|
||||
* The array contains primary user ids indexed by the sub-key
|
||||
* id.
|
||||
*/
|
||||
public function getBadPassphrases()
|
||||
{
|
||||
return $this->_badPassphrases;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ getMissingPassphrases()
|
||||
|
||||
/**
|
||||
* Gets keys for which the passhprase is missing
|
||||
*
|
||||
* @return array an array of keys for which the passphrase is missing.
|
||||
* The array contains primary user ids indexed by the sub-key
|
||||
* id.
|
||||
*/
|
||||
public function getMissingPassphrases()
|
||||
{
|
||||
return $this->_missingPassphrases;
|
||||
}
|
||||
|
||||
// }}}
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ class Crypt_GPG_DeletePrivateKeyException
|
||||
|
||||
/**
|
||||
* An exception thrown when an attempt is made to delete public key that has an
|
||||
* associated private key on the keyring
|
||||
*
|
||||
* @category Encryption
|
||||
* @package Crypt_GPG
|
||||
* @author Michael Gauthier <mike@silverorange.com>
|
||||
* @copyright 2008 silverorange
|
||||
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
|
||||
* @link http://pear.php.net/package/Crypt_GPG
|
||||
*/
|
||||
class Crypt_GPG_DeletePrivateKeyException extends Crypt_GPG_Exception
|
||||
{
|
||||
// {{{ private class properties
|
||||
|
||||
/**
|
||||
* The key identifier the deletion attempt was made upon
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $_keyId = '';
|
||||
|
||||
// }}}
|
||||
// {{{ __construct()
|
||||
|
||||
/**
|
||||
* Creates a new Crypt_GPG_DeletePrivateKeyException
|
||||
*
|
||||
* @param string $message an error message.
|
||||
* @param integer $code a user defined error code.
|
||||
* @param string $keyId the key identifier of the public key that was
|
||||
* attempted to delete.
|
||||
*
|
||||
* @see Crypt_GPG::deletePublicKey()
|
||||
*/
|
||||
public function __construct($message, $code = 0, $keyId = '')
|
||||
{
|
||||
$this->_keyId = $keyId;
|
||||
parent::__construct($message, $code);
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ getKeyId()
|
||||
|
||||
/**
|
||||
* Gets the key identifier of the key that was not found
|
||||
*
|
||||
* @return string the key identifier of the key that was not found.
|
||||
*/
|
||||
public function getKeyId()
|
||||
{
|
||||
return $this->_keyId;
|
||||
}
|
||||
|
||||
// }}}
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ class Crypt_GPG_KeyNotCreatedException
|
||||
|
||||
/**
|
||||
* An exception thrown when an attempt is made to generate a key and the
|
||||
* attempt fails
|
||||
*
|
||||
* @category Encryption
|
||||
* @package Crypt_GPG
|
||||
* @author Michael Gauthier <mike@silverorange.com>
|
||||
* @copyright 2011 silverorange
|
||||
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
|
||||
* @link http://pear.php.net/package/Crypt_GPG
|
||||
*/
|
||||
class Crypt_GPG_KeyNotCreatedException extends Crypt_GPG_Exception
|
||||
{
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ class Crypt_GPG_InvalidKeyParamsException
|
||||
|
||||
/**
|
||||
* An exception thrown when an attempt is made to generate a key and the
|
||||
* key parameters set on the key generator are invalid
|
||||
*
|
||||
* @category Encryption
|
||||
* @package Crypt_GPG
|
||||
* @author Michael Gauthier <mike@silverorange.com>
|
||||
* @copyright 2011 silverorange
|
||||
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
|
||||
* @link http://pear.php.net/package/Crypt_GPG
|
||||
*/
|
||||
class Crypt_GPG_InvalidKeyParamsException extends Crypt_GPG_Exception
|
||||
{
|
||||
// {{{ private class properties
|
||||
|
||||
/**
|
||||
* The key algorithm
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
private $_algorithm = 0;
|
||||
|
||||
/**
|
||||
* The key size
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
private $_size = 0;
|
||||
|
||||
/**
|
||||
* The key usage
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
private $_usage = 0;
|
||||
|
||||
// }}}
|
||||
// {{{ __construct()
|
||||
|
||||
/**
|
||||
* Creates a new Crypt_GPG_InvalidKeyParamsException
|
||||
*
|
||||
* @param string $message an error message.
|
||||
* @param integer $code a user defined error code.
|
||||
* @param string $algorithm the key algorithm.
|
||||
* @param string $size the key size.
|
||||
* @param string $usage the key usage.
|
||||
*/
|
||||
public function __construct(
|
||||
$message,
|
||||
$code = 0,
|
||||
$algorithm = 0,
|
||||
$size = 0,
|
||||
$usage = 0
|
||||
) {
|
||||
parent::__construct($message, $code);
|
||||
|
||||
$this->_algorithm = $algorithm;
|
||||
$this->_size = $size;
|
||||
$this->_usage = $usage;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ getAlgorithm()
|
||||
|
||||
/**
|
||||
* Gets the key algorithm
|
||||
*
|
||||
* @return integer the key algorithm.
|
||||
*/
|
||||
public function getAlgorithm()
|
||||
{
|
||||
return $this->_algorithm;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ getSize()
|
||||
|
||||
/**
|
||||
* Gets the key size
|
||||
*
|
||||
* @return integer the key size.
|
||||
*/
|
||||
public function getSize()
|
||||
{
|
||||
return $this->_size;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ getUsage()
|
||||
|
||||
/**
|
||||
* Gets the key usage
|
||||
*
|
||||
* @return integer the key usage.
|
||||
*/
|
||||
public function getUsage()
|
||||
{
|
||||
return $this->_usage;
|
||||
}
|
||||
|
||||
// }}}
|
||||
}
|
||||
|
||||
// }}}
|
||||
|
||||
?>
|
||||
+241
@@ -0,0 +1,241 @@
|
||||
<?php
|
||||
|
||||
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
|
||||
|
||||
/**
|
||||
* Contains a class representing GPG keys
|
||||
*
|
||||
* 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, see
|
||||
* <http://www.gnu.org/licenses/>
|
||||
*
|
||||
* @category Encryption
|
||||
* @package Crypt_GPG
|
||||
* @author Michael Gauthier <mike@silverorange.com>
|
||||
* @copyright 2008-2010 silverorange
|
||||
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
|
||||
* @link http://pear.php.net/package/Crypt_GPG
|
||||
*/
|
||||
|
||||
/**
|
||||
* Sub-key class definition
|
||||
*/
|
||||
require_once 'Crypt/GPG/SubKey.php';
|
||||
|
||||
/**
|
||||
* User id class definition
|
||||
*/
|
||||
require_once 'Crypt/GPG/UserId.php';
|
||||
|
||||
// {{{ class Crypt_GPG_Key
|
||||
|
||||
/**
|
||||
* A data class for GPG key information
|
||||
*
|
||||
* This class is used to store the results of the {@link Crypt_GPG::getKeys()}
|
||||
* method.
|
||||
*
|
||||
* @category Encryption
|
||||
* @package Crypt_GPG
|
||||
* @author Michael Gauthier <mike@silverorange.com>
|
||||
* @copyright 2008-2010 silverorange
|
||||
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
|
||||
* @link http://pear.php.net/package/Crypt_GPG
|
||||
* @see Crypt_GPG::getKeys()
|
||||
*/
|
||||
class Crypt_GPG_Key
|
||||
{
|
||||
// {{{ class properties
|
||||
|
||||
/**
|
||||
* The user ids associated with this key
|
||||
*
|
||||
* This is an array of {@link Crypt_GPG_UserId} objects.
|
||||
*
|
||||
* @var array
|
||||
*
|
||||
* @see Crypt_GPG_Key::addUserId()
|
||||
* @see Crypt_GPG_Key::getUserIds()
|
||||
*/
|
||||
private $_userIds = array();
|
||||
|
||||
/**
|
||||
* The subkeys of this key
|
||||
*
|
||||
* This is an array of {@link Crypt_GPG_SubKey} objects.
|
||||
*
|
||||
* @var array
|
||||
*
|
||||
* @see Crypt_GPG_Key::addSubKey()
|
||||
* @see Crypt_GPG_Key::getSubKeys()
|
||||
*/
|
||||
private $_subKeys = array();
|
||||
|
||||
// }}}
|
||||
// {{{ getSubKeys()
|
||||
|
||||
/**
|
||||
* Gets the sub-keys of this key
|
||||
*
|
||||
* @return array the sub-keys of this key.
|
||||
*
|
||||
* @see Crypt_GPG_Key::addSubKey()
|
||||
*/
|
||||
public function getSubKeys()
|
||||
{
|
||||
return $this->_subKeys;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ getUserIds()
|
||||
|
||||
/**
|
||||
* Gets the user ids of this key
|
||||
*
|
||||
* @return array the user ids of this key.
|
||||
*
|
||||
* @see Crypt_GPG_Key::addUserId()
|
||||
*/
|
||||
public function getUserIds()
|
||||
{
|
||||
return $this->_userIds;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ getPrimaryKey()
|
||||
|
||||
/**
|
||||
* Gets the primary sub-key of this key
|
||||
*
|
||||
* The primary key is the first added sub-key.
|
||||
*
|
||||
* @return Crypt_GPG_SubKey the primary sub-key of this key.
|
||||
*/
|
||||
public function getPrimaryKey()
|
||||
{
|
||||
$primary_key = null;
|
||||
if (count($this->_subKeys) > 0) {
|
||||
$primary_key = $this->_subKeys[0];
|
||||
}
|
||||
return $primary_key;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ canSign()
|
||||
|
||||
/**
|
||||
* Gets whether or not this key can sign data
|
||||
*
|
||||
* This key can sign data if any sub-key of this key can sign data.
|
||||
*
|
||||
* @return boolean true if this key can sign data and false if this key
|
||||
* cannot sign data.
|
||||
*/
|
||||
public function canSign()
|
||||
{
|
||||
$canSign = false;
|
||||
foreach ($this->_subKeys as $subKey) {
|
||||
if ($subKey->canSign()) {
|
||||
$canSign = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return $canSign;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ canEncrypt()
|
||||
|
||||
/**
|
||||
* Gets whether or not this key can encrypt data
|
||||
*
|
||||
* This key can encrypt data if any sub-key of this key can encrypt data.
|
||||
*
|
||||
* @return boolean true if this key can encrypt data and false if this
|
||||
* key cannot encrypt data.
|
||||
*/
|
||||
public function canEncrypt()
|
||||
{
|
||||
$canEncrypt = false;
|
||||
foreach ($this->_subKeys as $subKey) {
|
||||
if ($subKey->canEncrypt()) {
|
||||
$canEncrypt = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return $canEncrypt;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ addSubKey()
|
||||
|
||||
/**
|
||||
* Adds a sub-key to this key
|
||||
*
|
||||
* The first added sub-key will be the primary key of this key.
|
||||
*
|
||||
* @param Crypt_GPG_SubKey $subKey the sub-key to add.
|
||||
*
|
||||
* @return Crypt_GPG_Key the current object, for fluent interface.
|
||||
*/
|
||||
public function addSubKey(Crypt_GPG_SubKey $subKey)
|
||||
{
|
||||
$this->_subKeys[] = $subKey;
|
||||
return $this;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ addUserId()
|
||||
|
||||
/**
|
||||
* Adds a user id to this key
|
||||
*
|
||||
* @param Crypt_GPG_UserId $userId the user id to add.
|
||||
*
|
||||
* @return Crypt_GPG_Key the current object, for fluent interface.
|
||||
*/
|
||||
public function addUserId(Crypt_GPG_UserId $userId)
|
||||
{
|
||||
$this->_userIds[] = $userId;
|
||||
return $this;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ __toString()
|
||||
|
||||
/**
|
||||
* String representation of the key
|
||||
*
|
||||
* @return string The key ID.
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
foreach ($this->_subKeys as $subKey) {
|
||||
if ($id = $subKey->getId()) {
|
||||
return $id;
|
||||
}
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
// }}}
|
||||
}
|
||||
|
||||
// }}}
|
||||
|
||||
?>
|
||||
+683
@@ -0,0 +1,683 @@
|
||||
<?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, see
|
||||
* <http://www.gnu.org/licenses/>
|
||||
*
|
||||
* @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
|
||||
* @link http://pear.php.net/package/Crypt_GPG
|
||||
* @link http://www.gnupg.org/
|
||||
*/
|
||||
|
||||
/**
|
||||
* Base class for GPG methods
|
||||
*/
|
||||
require_once 'Crypt/GPGAbstract.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;
|
||||
|
||||
// }}}
|
||||
// {{{ __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>mixed 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);
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ 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)
|
||||
{
|
||||
$algorithm = 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)
|
||||
{
|
||||
$algorithm = 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;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ 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 (strlen($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;
|
||||
}
|
||||
|
||||
// This is required in GnuPG 2.1
|
||||
if (!strlen($this->passphrase)) {
|
||||
$keyParamsFormatted[] = '%no-protection';
|
||||
}
|
||||
|
||||
$input = implode("\n", $keyParamsFormatted) . "\n%commit\n";
|
||||
|
||||
$this->engine->reset();
|
||||
$this->engine->setProcessData('Handle', $handle);
|
||||
$this->engine->setInput($input);
|
||||
$this->engine->setOutput($output);
|
||||
$this->engine->setOperation('--gen-key', array('--batch'));
|
||||
|
||||
try {
|
||||
$this->engine->run();
|
||||
} catch (Crypt_GPG_InvalidKeyParamsException $e) {
|
||||
switch ($this->engine->getProcessData('LineNumber')) {
|
||||
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 $e;
|
||||
}
|
||||
}
|
||||
|
||||
$fingerprint = $this->engine->getProcessData('KeyCreated');
|
||||
$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;
|
||||
}
|
||||
|
||||
// }}}
|
||||
}
|
||||
|
||||
// }}}
|
||||
|
||||
?>
|
||||
+853
@@ -0,0 +1,853 @@
|
||||
<?php
|
||||
|
||||
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
|
||||
|
||||
/**
|
||||
* Contains a class implementing automatic pinentry for gpg-agent
|
||||
*
|
||||
* 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, see
|
||||
* <http://www.gnu.org/licenses/>
|
||||
*
|
||||
* @category Encryption
|
||||
* @package Crypt_GPG
|
||||
* @author Michael Gauthier <mike@silverorange.com>
|
||||
* @copyright 2013 silverorange
|
||||
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
|
||||
* @link http://pear.php.net/package/Crypt_GPG
|
||||
*/
|
||||
|
||||
/**
|
||||
* CLI user-interface and parser.
|
||||
*/
|
||||
require_once 'Console/CommandLine.php';
|
||||
|
||||
// {{{ class Crypt_GPG_PinEntry
|
||||
|
||||
/**
|
||||
* A command-line dummy pinentry program for use with gpg-agent and Crypt_GPG
|
||||
*
|
||||
* This pinentry receives passphrases through en environment variable and
|
||||
* automatically enters the PIN in response to gpg-agent requests. No user-
|
||||
* interaction required.
|
||||
*
|
||||
* Thie pinentry can be run independently for testing and debugging with the
|
||||
* following syntax:
|
||||
*
|
||||
* <pre>
|
||||
* Usage:
|
||||
* crypt-gpg-pinentry [options]
|
||||
*
|
||||
* Options:
|
||||
* -l log, --log=log Optional location to log pinentry activity.
|
||||
* -v, --verbose Sets verbosity level. Use multiples for more detail
|
||||
* (e.g. "-vv").
|
||||
* -h, --help show this help message and exit
|
||||
* --version show the program version and exit
|
||||
* </pre>
|
||||
*
|
||||
* @category Encryption
|
||||
* @package Crypt_GPG
|
||||
* @author Michael Gauthier <mike@silverorange.com>
|
||||
* @copyright 2013 silverorange
|
||||
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
|
||||
* @link http://pear.php.net/package/Crypt_GPG
|
||||
* @see Crypt_GPG::getKeys()
|
||||
*/
|
||||
class Crypt_GPG_PinEntry
|
||||
{
|
||||
// {{{ class constants
|
||||
|
||||
/**
|
||||
* Verbosity level for showing no output.
|
||||
*/
|
||||
const VERBOSITY_NONE = 0;
|
||||
|
||||
/**
|
||||
* Verbosity level for showing error output.
|
||||
*/
|
||||
const VERBOSITY_ERRORS = 1;
|
||||
|
||||
/**
|
||||
* Verbosity level for showing all output, including Assuan protocol
|
||||
* messages.
|
||||
*/
|
||||
const VERBOSITY_ALL = 2;
|
||||
|
||||
/**
|
||||
* Length of buffer for reading lines from the Assuan server.
|
||||
*
|
||||
* PHP reads 8192 bytes. If this is set to less than 8192, PHP reads 8192
|
||||
* and buffers the rest so we might as well just read 8192.
|
||||
*
|
||||
* Using values other than 8192 also triggers PHP bugs.
|
||||
*
|
||||
* @see http://bugs.php.net/bug.php?id=35224
|
||||
*/
|
||||
const CHUNK_SIZE = 8192;
|
||||
|
||||
// }}}
|
||||
// {{{ protected properties
|
||||
|
||||
/**
|
||||
* File handle for the input stream
|
||||
*
|
||||
* @var resource
|
||||
*/
|
||||
protected $stdin = null;
|
||||
|
||||
/**
|
||||
* File handle for the output stream
|
||||
*
|
||||
* @var resource
|
||||
*/
|
||||
protected $stdout = null;
|
||||
|
||||
/**
|
||||
* File handle for the log file if a log file is used
|
||||
*
|
||||
* @var resource
|
||||
*/
|
||||
protected $logFile = null;
|
||||
|
||||
/**
|
||||
* Whether or not this pinentry is finished and is exiting
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
protected $moribund = false;
|
||||
|
||||
/**
|
||||
* Verbosity level
|
||||
*
|
||||
* One of:
|
||||
* - {@link Crypt_GPG_PinEntry::VERBOSITY_NONE},
|
||||
* - {@link Crypt_GPG_PinEntry::VERBOSITY_ERRORS}, or
|
||||
* - {@link Crypt_GPG_PinEntry::VERBOSITY_ALL}
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
protected $verbosity = self::VERBOSITY_NONE;
|
||||
|
||||
/**
|
||||
* The command-line interface parser for this pinentry
|
||||
*
|
||||
* @var Console_CommandLine
|
||||
*
|
||||
* @see Crypt_GPG_PinEntry::getParser()
|
||||
*/
|
||||
protected $parser = null;
|
||||
|
||||
/**
|
||||
* PINs to be entered by this pinentry
|
||||
*
|
||||
* An indexed array of associative arrays in the form:
|
||||
* <code>
|
||||
* <?php
|
||||
* array(
|
||||
* array(
|
||||
* 'keyId' => $keyId,
|
||||
* 'passphrase' => $passphrase
|
||||
* ),
|
||||
* ...
|
||||
* );
|
||||
* ?>
|
||||
* </code>
|
||||
*
|
||||
* This array is parsed from the environment variable
|
||||
* <kbd>PINENTRY_USER_DATA</kbd>.
|
||||
*
|
||||
* @var array
|
||||
*
|
||||
* @see Crypt_GPG_PinEntry::initPinsFromENV()
|
||||
*/
|
||||
protected $pins = array();
|
||||
|
||||
/**
|
||||
* The PIN currently being requested by the Assuan server
|
||||
*
|
||||
* If set, this is an associative array in the form:
|
||||
* <code>
|
||||
* <?php
|
||||
* array(
|
||||
* 'keyId' => $shortKeyId,
|
||||
* 'userId' => $userIdString
|
||||
* );
|
||||
* ?>
|
||||
* </code>
|
||||
*
|
||||
* @var array|null
|
||||
*/
|
||||
protected $currentPin = null;
|
||||
|
||||
// }}}
|
||||
// {{{ __invoke()
|
||||
|
||||
/**
|
||||
* Runs this pinentry
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __invoke()
|
||||
{
|
||||
$this->parser = $this->getCommandLineParser();
|
||||
|
||||
try {
|
||||
$result = $this->parser->parse();
|
||||
|
||||
$this->setVerbosity($result->options['verbose']);
|
||||
$this->setLogFilename($result->options['log']);
|
||||
|
||||
$this->connect();
|
||||
$this->initPinsFromENV();
|
||||
|
||||
while (($line = fgets($this->stdin, self::CHUNK_SIZE)) !== false) {
|
||||
$this->parseCommand(mb_substr($line, 0, -1, '8bit'));
|
||||
if ($this->moribund) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$this->disconnect();
|
||||
|
||||
} catch (Console_CommandLineException $e) {
|
||||
$this->log($e->getMessage() . PHP_EOL, slf::VERBOSITY_ERRORS);
|
||||
exit(1);
|
||||
} catch (Exception $e) {
|
||||
$this->log($e->getMessage() . PHP_EOL, self::VERBOSITY_ERRORS);
|
||||
$this->log($e->getTraceAsString() . PHP_EOL, self::VERBOSITY_ERRORS);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ setVerbosity()
|
||||
|
||||
/**
|
||||
* Sets the verbosity of logging for this pinentry
|
||||
*
|
||||
* Verbosity levels are:
|
||||
*
|
||||
* - {@link Crypt_GPG_PinEntry::VERBOSITY_NONE} - no logging.
|
||||
* - {@link Crypt_GPG_PinEntry::VERBOSITY_ERRORS} - log errors only.
|
||||
* - {@link Crypt_GPG_PinEntry::VERBOSITY_ALL} - log everything, including
|
||||
* the assuan protocol.
|
||||
*
|
||||
* @param integer $verbosity the level of verbosity of this pinentry.
|
||||
*
|
||||
* @return Crypt_GPG_PinEntry the current object, for fluent interface.
|
||||
*/
|
||||
public function setVerbosity($verbosity)
|
||||
{
|
||||
$this->verbosity = (integer)$verbosity;
|
||||
return $this;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ setLogFilename()
|
||||
|
||||
/**
|
||||
* Sets the log file location
|
||||
*
|
||||
* @param string $filename the new log filename to use. If an empty string
|
||||
* is used, file-based logging is disabled.
|
||||
*
|
||||
* @return Crypt_GPG_PinEntry the current object, for fluent interface.
|
||||
*/
|
||||
public function setLogFilename($filename)
|
||||
{
|
||||
if (is_resource($this->logFile)) {
|
||||
fflush($this->logFile);
|
||||
fclose($this->logFile);
|
||||
$this->logFile = null;
|
||||
}
|
||||
|
||||
if ($filename != '') {
|
||||
if (($this->logFile = fopen($filename, 'w')) === false) {
|
||||
$this->log(
|
||||
'Unable to open log file "' . $filename . '" '
|
||||
. 'for writing.' . PHP_EOL,
|
||||
self::VERBOSITY_ERRORS
|
||||
);
|
||||
exit(1);
|
||||
} else {
|
||||
stream_set_write_buffer($this->logFile, 0);
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ getUIXML()
|
||||
|
||||
/**
|
||||
* Gets the CLI user-interface definition for this pinentry
|
||||
*
|
||||
* Detects whether or not this package is PEAR-installed and appropriately
|
||||
* locates the XML UI definition.
|
||||
*
|
||||
* @return string the location of the CLI user-interface definition XML.
|
||||
*/
|
||||
protected function getUIXML()
|
||||
{
|
||||
// Find PinEntry config depending on the way how the package is installed
|
||||
$ds = DIRECTORY_SEPARATOR;
|
||||
$root = __DIR__ . $ds . '..' . $ds . '..' . $ds;
|
||||
$paths = array(
|
||||
'/www/roundcube/releases/roundcubemail-1.3-beta/vendor/pear-pear.php.net/Crypt_GPG/data' . $ds . '@package-name@' . $ds . 'data', // PEAR
|
||||
$root . 'data', // Git
|
||||
$root . 'data' . $ds . 'Crypt_GPG' . $ds . 'data', // Composer
|
||||
);
|
||||
|
||||
foreach ($paths as $path) {
|
||||
if (file_exists($path . $ds . 'pinentry-cli.xml')) {
|
||||
return $path . $ds . 'pinentry-cli.xml';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ getCommandLineParser()
|
||||
|
||||
/**
|
||||
* Gets the CLI parser for this pinentry
|
||||
*
|
||||
* @return Console_CommandLine the CLI parser for this pinentry.
|
||||
*/
|
||||
protected function getCommandLineParser()
|
||||
{
|
||||
return Console_CommandLine::fromXmlFile($this->getUIXML());
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ log()
|
||||
|
||||
/**
|
||||
* Logs a message at the specified verbosity level
|
||||
*
|
||||
* If a log file is used, the message is written to the log. Otherwise,
|
||||
* the message is sent to STDERR.
|
||||
*
|
||||
* @param string $data the message to log.
|
||||
* @param integer $level the verbosity level above which the message should
|
||||
* be logged.
|
||||
*
|
||||
* @return Crypt_GPG_PinEntry the current object, for fluent interface.
|
||||
*/
|
||||
protected function log($data, $level)
|
||||
{
|
||||
if ($this->verbosity >= $level) {
|
||||
if (is_resource($this->logFile)) {
|
||||
fwrite($this->logFile, $data);
|
||||
fflush($this->logFile);
|
||||
} else {
|
||||
$this->parser->outputter->stderr($data);
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ connect()
|
||||
|
||||
/**
|
||||
* Connects this pinentry to the assuan server
|
||||
*
|
||||
* Opens I/O streams and sends initial handshake.
|
||||
*
|
||||
* @return Crypt_GPG_PinEntry the current object, for fluent interface.
|
||||
*/
|
||||
protected function connect()
|
||||
{
|
||||
// Binary operations will not work on Windows with PHP < 5.2.6.
|
||||
$rb = (version_compare(PHP_VERSION, '5.2.6') < 0) ? 'r' : 'rb';
|
||||
$wb = (version_compare(PHP_VERSION, '5.2.6') < 0) ? 'w' : 'wb';
|
||||
|
||||
$this->stdin = fopen('php://stdin', $rb);
|
||||
$this->stdout = fopen('php://stdout', $wb);
|
||||
|
||||
if (function_exists('stream_set_read_buffer')) {
|
||||
stream_set_read_buffer($this->stdin, 0);
|
||||
}
|
||||
stream_set_write_buffer($this->stdout, 0);
|
||||
|
||||
// initial handshake
|
||||
$this->send($this->getOK('Crypt_GPG pinentry ready and waiting'));
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ parseCommand()
|
||||
|
||||
/**
|
||||
* Parses an assuan command and performs the appropriate action
|
||||
*
|
||||
* Documentation of the assuan commands for pinentry is limited to
|
||||
* non-existent. Most of these commands were taken from the C source code
|
||||
* to gpg-agent and pinentry.
|
||||
*
|
||||
* Additional context was provided by using strace -f when calling the
|
||||
* gpg-agent.
|
||||
*
|
||||
* @param string $line the assuan command line to parse
|
||||
*
|
||||
* @return Crypt_GPG_PinEntry the current object, for fluent interface.
|
||||
*/
|
||||
protected function parseCommand($line)
|
||||
{
|
||||
$this->log('<- ' . $line . PHP_EOL, self::VERBOSITY_ALL);
|
||||
|
||||
$parts = explode(' ', $line, 2);
|
||||
|
||||
$command = $parts[0];
|
||||
|
||||
if (count($parts) === 2) {
|
||||
$data = $parts[1];
|
||||
} else {
|
||||
$data = null;
|
||||
}
|
||||
|
||||
switch ($command) {
|
||||
case 'SETDESC':
|
||||
return $this->sendSetDescription($data);
|
||||
|
||||
case 'MESSAGE':
|
||||
return $this->sendMessage();
|
||||
|
||||
case 'CONFIRM':
|
||||
return $this->sendConfirm();
|
||||
|
||||
case 'GETINFO':
|
||||
return $this->sendGetInfo($data);
|
||||
|
||||
case 'GETPIN':
|
||||
return $this->sendGetPin($data);
|
||||
|
||||
case 'RESET':
|
||||
return $this->sendReset();
|
||||
|
||||
case 'BYE':
|
||||
return $this->sendBye();
|
||||
|
||||
default:
|
||||
return $this->sendNotImplementedOK();
|
||||
}
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ initPinsFromENV()
|
||||
|
||||
/**
|
||||
* Initializes the PINs to be entered by this pinentry from the environment
|
||||
* variable PINENTRY_USER_DATA
|
||||
*
|
||||
* The PINs are parsed from a JSON-encoded string.
|
||||
*
|
||||
* @return Crypt_GPG_PinEntry the current object, for fluent interface.
|
||||
*/
|
||||
protected function initPinsFromENV()
|
||||
{
|
||||
if (($userData = getenv('PINENTRY_USER_DATA')) !== false) {
|
||||
$pins = json_decode($userData, true);
|
||||
if ($pins === null) {
|
||||
$this->log(
|
||||
'-- failed to parse user data' . PHP_EOL,
|
||||
self::VERBOSITY_ERRORS
|
||||
);
|
||||
} else {
|
||||
$this->pins = $pins;
|
||||
$this->log(
|
||||
'-- got user data [not showing passphrases]' . PHP_EOL,
|
||||
self::VERBOSITY_ALL
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ disconnect()
|
||||
|
||||
/**
|
||||
* Disconnects this pinentry from the Assuan server
|
||||
*
|
||||
* @return Crypt_GPG_PinEntry the current object, for fluent interface.
|
||||
*/
|
||||
protected function disconnect()
|
||||
{
|
||||
$this->log('-- disconnecting' . PHP_EOL, self::VERBOSITY_ALL);
|
||||
|
||||
fflush($this->stdout);
|
||||
fclose($this->stdout);
|
||||
fclose($this->stdin);
|
||||
|
||||
$this->stdin = null;
|
||||
$this->stdout = null;
|
||||
|
||||
$this->log('-- disconnected' . PHP_EOL, self::VERBOSITY_ALL);
|
||||
|
||||
if (is_resource($this->logFile)) {
|
||||
fflush($this->logFile);
|
||||
fclose($this->logFile);
|
||||
$this->logFile = null;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ sendNotImplementedOK()
|
||||
|
||||
/**
|
||||
* Sends an OK response for a not implemented feature
|
||||
*
|
||||
* @return Crypt_GPG_PinEntry the current object, for fluent interface.
|
||||
*/
|
||||
protected function sendNotImplementedOK()
|
||||
{
|
||||
return $this->send($this->getOK());
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ sendSetDescription()
|
||||
|
||||
/**
|
||||
* Parses the currently requested key identifier and user identifier from
|
||||
* the description passed to this pinentry
|
||||
*
|
||||
* @param string $text the raw description sent from gpg-agent.
|
||||
*
|
||||
* @return Crypt_GPG_PinEntry the current object, for fluent interface.
|
||||
*/
|
||||
protected function sendSetDescription($text)
|
||||
{
|
||||
$text = rawurldecode($text);
|
||||
$matches = array();
|
||||
// TODO: handle user id with quotation marks
|
||||
$exp = '/\n"(.+)"\n.*\sID ([A-Z0-9]+),\n/mu';
|
||||
if (preg_match($exp, $text, $matches) === 1) {
|
||||
$userId = $matches[1];
|
||||
$keyId = $matches[2];
|
||||
|
||||
if ($this->currentPin === null || $this->currentPin['keyId'] !== $keyId) {
|
||||
$this->currentPin = array(
|
||||
'userId' => $userId,
|
||||
'keyId' => $keyId
|
||||
);
|
||||
$this->log(
|
||||
'-- looking for PIN for ' . $keyId . PHP_EOL,
|
||||
self::VERBOSITY_ALL
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->send($this->getOK());
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ sendConfirm()
|
||||
|
||||
/**
|
||||
* Tells the assuan server to confirm the operation
|
||||
*
|
||||
* @return Crypt_GPG_PinEntry the current object, for fluent interface.
|
||||
*/
|
||||
protected function sendConfirm()
|
||||
{
|
||||
return $this->send($this->getOK());
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ sendMessage()
|
||||
|
||||
/**
|
||||
* Tells the assuan server that any requested pop-up messages were confirmed
|
||||
* by pressing the fake 'close' button
|
||||
*
|
||||
* @return Crypt_GPG_PinEntry the current object, for fluent interface.
|
||||
*/
|
||||
protected function sendMessage()
|
||||
{
|
||||
return $this->sendButtonInfo('close');
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ sendButtonInfo()
|
||||
|
||||
/**
|
||||
* Sends information about pressed buttons to the assuan server
|
||||
*
|
||||
* This is used to fake a user-interface for this pinentry.
|
||||
*
|
||||
* @param string $text the button status to send.
|
||||
*
|
||||
* @return Crypt_GPG_PinEntry the current object, for fluent interface.
|
||||
*/
|
||||
protected function sendButtonInfo($text)
|
||||
{
|
||||
return $this->send('BUTTON_INFO ' . $text . "\n");
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ sendGetPin()
|
||||
|
||||
/**
|
||||
* Sends the PIN value for the currently requested key
|
||||
*
|
||||
* @return Crypt_GPG_PinEntry the current object, for fluent interface.
|
||||
*/
|
||||
protected function sendGetPin()
|
||||
{
|
||||
$foundPin = '';
|
||||
|
||||
if (is_array($this->currentPin)) {
|
||||
$keyIdLength = mb_strlen($this->currentPin['keyId'], '8bit');
|
||||
|
||||
// search for the pin
|
||||
foreach ($this->pins as $_keyId => $pin) {
|
||||
// Warning: GnuPG 2.1 asks 3 times for passphrase if it is invalid
|
||||
$keyId = $this->currentPin['keyId'];
|
||||
$_keyIdLength = mb_strlen($_keyId, '8bit');
|
||||
|
||||
// Get last X characters of key identifier to compare
|
||||
// Most GnuPG versions use 8 characters, but recent ones can use 16,
|
||||
// We support 8 for backward compatibility
|
||||
if ($keyIdLength < $_keyIdLength) {
|
||||
$_keyId = mb_substr($_keyId, -$keyIdLength, $keyIdLength, '8bit');
|
||||
} else if ($keyIdLength > $_keyIdLength) {
|
||||
$keyId = mb_substr($keyId, -$_keyIdLength, $_keyIdLength, '8bit');
|
||||
}
|
||||
|
||||
if ($_keyId === $keyId) {
|
||||
$foundPin = $pin;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this
|
||||
->send($this->getData($foundPin))
|
||||
->send($this->getOK());
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ sendGetInfo()
|
||||
|
||||
/**
|
||||
* Sends information about this pinentry
|
||||
*
|
||||
* @param string $data the information requested by the assuan server.
|
||||
* Currently only 'pid' is supported. Other requests
|
||||
* return no information.
|
||||
*
|
||||
* @return Crypt_GPG_PinEntry the current object, for fluent interface.
|
||||
*/
|
||||
protected function sendGetInfo($data)
|
||||
{
|
||||
$parts = explode(' ', $data, 2);
|
||||
$command = reset($parts);
|
||||
|
||||
switch ($command) {
|
||||
case 'pid':
|
||||
return $this->sendGetInfoPID();
|
||||
default:
|
||||
return $this->send($this->getOK());
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
// }}}
|
||||
// {{{ sendGetInfoPID()
|
||||
|
||||
/**
|
||||
* Sends the PID of this pinentry to the assuan server
|
||||
*
|
||||
* @return Crypt_GPG_PinEntry the current object, for fluent interface.
|
||||
*/
|
||||
protected function sendGetInfoPID()
|
||||
{
|
||||
return $this
|
||||
->send($this->getData(getmypid()))
|
||||
->send($this->getOK());
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ sendBye()
|
||||
|
||||
/**
|
||||
* Flags this pinentry for disconnection and sends an OK response
|
||||
*
|
||||
* @return Crypt_GPG_PinEntry the current object, for fluent interface.
|
||||
*/
|
||||
protected function sendBye()
|
||||
{
|
||||
$return = $this->send($this->getOK('closing connection'));
|
||||
$this->moribund = true;
|
||||
return $return;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ sendReset()
|
||||
|
||||
/**
|
||||
* Resets this pinentry and sends an OK response
|
||||
*
|
||||
* @return Crypt_GPG_PinEntry the current object, for fluent interface.
|
||||
*/
|
||||
protected function sendReset()
|
||||
{
|
||||
$this->currentPin = null;
|
||||
return $this->send($this->getOK());
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ getOK()
|
||||
|
||||
/**
|
||||
* Gets an OK response to send to the assuan server
|
||||
*
|
||||
* @param string $data an optional message to include with the OK response.
|
||||
*
|
||||
* @return string the OK response.
|
||||
*/
|
||||
protected function getOK($data = null)
|
||||
{
|
||||
$return = 'OK';
|
||||
|
||||
if ($data) {
|
||||
$return .= ' ' . $data;
|
||||
}
|
||||
|
||||
return $return . "\n";
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ getData()
|
||||
|
||||
/**
|
||||
* Gets data ready to send to the assuan server
|
||||
*
|
||||
* Data is appropriately escaped and long lines are wrapped.
|
||||
*
|
||||
* @param string $data the data to send to the assuan server.
|
||||
*
|
||||
* @return string the properly escaped, formatted data.
|
||||
*
|
||||
* @see http://www.gnupg.org/documentation/manuals/assuan/Server-responses.html
|
||||
*/
|
||||
protected function getData($data)
|
||||
{
|
||||
// Escape data. Only %, \n and \r need to be escaped but other
|
||||
// values are allowed to be escaped. See
|
||||
// http://www.gnupg.org/documentation/manuals/assuan/Server-responses.html
|
||||
$data = rawurlencode($data);
|
||||
$data = $this->getWordWrappedData($data, 'D');
|
||||
return $data;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ getComment()
|
||||
|
||||
/**
|
||||
* Gets a comment ready to send to the assuan server
|
||||
*
|
||||
* @param string $data the comment to send to the assuan server.
|
||||
*
|
||||
* @return string the properly formatted comment.
|
||||
*
|
||||
* @see http://www.gnupg.org/documentation/manuals/assuan/Server-responses.html
|
||||
*/
|
||||
protected function getComment($data)
|
||||
{
|
||||
return $this->getWordWrappedData($data, '#');
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ getWordWrappedData()
|
||||
|
||||
/**
|
||||
* Wraps strings at 1,000 bytes without splitting UTF-8 multibyte
|
||||
* characters
|
||||
*
|
||||
* Each line is prepended with the specified line prefix. Wrapped lines
|
||||
* are automatically appended with \ characters.
|
||||
*
|
||||
* Protocol strings are UTF-8 but maximum line length is 1,000 bytes.
|
||||
* <kbd>mb_strcut()</kbd> is used so we can limit line length by bytes
|
||||
* and not split characters across multiple lines.
|
||||
*
|
||||
* @param string $data the data to wrap.
|
||||
* @param string $prefix a single character to use as the line prefix. For
|
||||
* example, 'D' or '#'.
|
||||
*
|
||||
* @return string the word-wrapped, prefixed string.
|
||||
*
|
||||
* @see http://www.gnupg.org/documentation/manuals/assuan/Server-responses.html
|
||||
*/
|
||||
protected function getWordWrappedData($data, $prefix)
|
||||
{
|
||||
$lines = array();
|
||||
|
||||
do {
|
||||
if (mb_strlen($data, '8bit') > 997) {
|
||||
$line = $prefix . ' ' . mb_strcut($data, 0, 996, 'utf-8') . "\\\n";
|
||||
$lines[] = $line;
|
||||
$lineLength = mb_strlen($line, '8bit') - 1;
|
||||
$dataLength = mb_substr($data, '8bit');
|
||||
$data = mb_substr(
|
||||
$data,
|
||||
$lineLength,
|
||||
$dataLength - $lineLength,
|
||||
'8bit'
|
||||
);
|
||||
} else {
|
||||
$lines[] = $prefix . ' ' . $data . "\n";
|
||||
$data = '';
|
||||
}
|
||||
} while ($data != '');
|
||||
|
||||
return implode('', $lines);
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ send()
|
||||
|
||||
/**
|
||||
* Sends raw data to the assuan server
|
||||
*
|
||||
* @param string $data the data to send.
|
||||
*
|
||||
* @return Crypt_GPG_PinEntry the current object, for fluent interface.
|
||||
*/
|
||||
protected function send($data)
|
||||
{
|
||||
$this->log('-> ' . $data, self::VERBOSITY_ALL);
|
||||
fwrite($this->stdout, $data);
|
||||
fflush($this->stdout);
|
||||
return $this;
|
||||
}
|
||||
|
||||
// }}}
|
||||
}
|
||||
|
||||
// }}}
|
||||
|
||||
?>
|
||||
+152
@@ -0,0 +1,152 @@
|
||||
<?php
|
||||
|
||||
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
|
||||
|
||||
/**
|
||||
* A class for monitoring and terminating processes
|
||||
*
|
||||
* 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, see
|
||||
* <http://www.gnu.org/licenses/>
|
||||
*
|
||||
* @category Encryption
|
||||
* @package Crypt_GPG
|
||||
* @author Michael Gauthier <mike@silverorange.com>
|
||||
* @copyright 2013 silverorange
|
||||
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
|
||||
* @link http://pear.php.net/package/Crypt_GPG
|
||||
*/
|
||||
|
||||
// {{{ class Crypt_GPG_ProcessControl
|
||||
|
||||
/**
|
||||
* A class for monitoring and terminating processes by PID
|
||||
*
|
||||
* This is used to safely terminate the gpg-agent for GnuPG 2.x. This class
|
||||
* is limited in its abilities and can only check if a PID is running and
|
||||
* send a PID SIGTERM.
|
||||
*
|
||||
* @category Encryption
|
||||
* @package Crypt_GPG
|
||||
* @author Michael Gauthier <mike@silverorange.com>
|
||||
* @copyright 2013 silverorange
|
||||
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
|
||||
* @link http://pear.php.net/package/Crypt_GPG
|
||||
*/
|
||||
class Crypt_GPG_ProcessControl
|
||||
{
|
||||
// {{{ protected properties
|
||||
|
||||
/**
|
||||
* The PID (process identifier) being monitored
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
protected $pid;
|
||||
|
||||
// }}}
|
||||
// {{{ __construct()
|
||||
|
||||
/**
|
||||
* Creates a new process controller from the given PID (process identifier)
|
||||
*
|
||||
* @param integer $pid the PID (process identifier).
|
||||
*/
|
||||
public function __construct($pid)
|
||||
{
|
||||
$this->pid = $pid;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ public function getPid()
|
||||
|
||||
/**
|
||||
* Gets the PID (process identifier) being controlled
|
||||
*
|
||||
* @return integer the PID being controlled.
|
||||
*/
|
||||
public function getPid()
|
||||
{
|
||||
return $this->pid;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ isRunning()
|
||||
|
||||
/**
|
||||
* Checks if the process is running
|
||||
*
|
||||
* If the <kbd>posix</kbd> extension is available, <kbd>posix_getpgid()</kbd>
|
||||
* is used. Otherwise <kbd>ps</kbd> is used on UNIX-like systems and
|
||||
* <kbd>tasklist</kbd> on Windows.
|
||||
*
|
||||
* @return boolean true if the process is running, false if not.
|
||||
*/
|
||||
public function isRunning()
|
||||
{
|
||||
$running = false;
|
||||
|
||||
if (function_exists('posix_getpgid')) {
|
||||
$running = false !== posix_getpgid($this->pid);
|
||||
} elseif (PHP_OS === 'WINNT') {
|
||||
$command = 'tasklist /fo csv /nh /fi '
|
||||
. escapeshellarg('PID eq ' . $this->pid);
|
||||
|
||||
$result = exec($command);
|
||||
$parts = explode(',', $result);
|
||||
$running = (count($parts) > 1 && trim($parts[1], '"') == $this->pid);
|
||||
} else {
|
||||
$result = exec('ps -p ' . escapeshellarg($this->pid) . ' -o pid=');
|
||||
$running = (trim($result) == $this->pid);
|
||||
}
|
||||
|
||||
return $running;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ terminate()
|
||||
|
||||
/**
|
||||
* Ends the process gracefully
|
||||
*
|
||||
* The signal SIGTERM is sent to the process. The gpg-agent process will
|
||||
* end gracefully upon receiving the SIGTERM signal. Upon 3 consecutive
|
||||
* SIGTERM signals the gpg-agent will forcefully shut down.
|
||||
*
|
||||
* If the <kbd>posix</kbd> extension is available, <kbd>posix_kill()</kbd>
|
||||
* is used. Otherwise <kbd>kill</kbd> is used on UNIX-like systems and
|
||||
* <kbd>taskkill</kbd> is used in Windows.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function terminate()
|
||||
{
|
||||
if (function_exists('posix_kill')) {
|
||||
posix_kill($this->pid, 15);
|
||||
} elseif (PHP_OS === 'WINNT') {
|
||||
exec('taskkill /PID ' . escapeshellarg($this->pid));
|
||||
} else {
|
||||
exec('kill -15 ' . escapeshellarg($this->pid));
|
||||
}
|
||||
}
|
||||
|
||||
// }}}
|
||||
}
|
||||
|
||||
// }}}
|
||||
|
||||
?>
|
||||
+893
@@ -0,0 +1,893 @@
|
||||
<?php
|
||||
|
||||
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
|
||||
|
||||
/**
|
||||
* Crypt_GPG is a package to use GPG from PHP
|
||||
*
|
||||
* This file contains handler for status and error pipes of GPG process.
|
||||
*
|
||||
* 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, see
|
||||
* <http://www.gnu.org/licenses/>
|
||||
*
|
||||
* @category Encryption
|
||||
* @package Crypt_GPG
|
||||
* @author Nathan Fredrickson <nathan@silverorange.com>
|
||||
* @author Michael Gauthier <mike@silverorange.com>
|
||||
* @author Aleksander Machniak <alec@alec.pl>
|
||||
* @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/
|
||||
*/
|
||||
|
||||
/**
|
||||
* GPG exception classes.
|
||||
*/
|
||||
require_once 'Crypt/GPG/Exceptions.php';
|
||||
|
||||
/**
|
||||
* Signature object class definition
|
||||
*/
|
||||
require_once 'Crypt/GPG/Signature.php';
|
||||
|
||||
// {{{ class Crypt_GPG_ProcessHandler
|
||||
|
||||
/**
|
||||
* Status/Error handler for GPG process pipes.
|
||||
*
|
||||
* This class is used internally by Crypt_GPG_Engine and does not need to be used
|
||||
* directly. See the {@link Crypt_GPG} class for end-user API.
|
||||
*
|
||||
* @category Encryption
|
||||
* @package Crypt_GPG
|
||||
* @author Nathan Fredrickson <nathan@silverorange.com>
|
||||
* @author Michael Gauthier <mike@silverorange.com>
|
||||
* @author Aleksander Machniak <alec@alec.pl>
|
||||
* @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_ProcessHandler
|
||||
{
|
||||
// {{{ protected class properties
|
||||
|
||||
/**
|
||||
* Engine used to control the GPG subprocess
|
||||
*
|
||||
* @var Crypt_GPG_Engine
|
||||
*/
|
||||
protected $engine;
|
||||
|
||||
/**
|
||||
* The error code of the current operation
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
protected $errorCode = Crypt_GPG::ERROR_NONE;
|
||||
|
||||
/**
|
||||
* The number of currently needed passphrases
|
||||
*
|
||||
* If this is not zero when the GPG command is completed, the error code is
|
||||
* set to {@link Crypt_GPG::ERROR_MISSING_PASSPHRASE}.
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
protected $needPassphrase = 0;
|
||||
|
||||
/**
|
||||
* Some data collected while processing the operation
|
||||
* or set for the operation
|
||||
*
|
||||
* @var array
|
||||
* @see self::setData()
|
||||
* @see self::getData()
|
||||
*/
|
||||
protected $data = array();
|
||||
|
||||
/**
|
||||
* The name of the current operation
|
||||
*
|
||||
* @var string
|
||||
* @see self::setOperation()
|
||||
*/
|
||||
protected $operation = null;
|
||||
|
||||
/**
|
||||
* The value of the argument of current operation
|
||||
*
|
||||
* @var string
|
||||
* @see self::setOperation()
|
||||
*/
|
||||
protected $operationArg = null;
|
||||
|
||||
// }}}
|
||||
// {{{ __construct()
|
||||
|
||||
/**
|
||||
* Creates a new instance
|
||||
*
|
||||
* @param Crypt_GPG_Engine $engine Engine object
|
||||
*/
|
||||
public function __construct($engine)
|
||||
{
|
||||
$this->engine = $engine;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ setOperation()
|
||||
|
||||
/**
|
||||
* Sets the operation that is being performed by the engine.
|
||||
*
|
||||
* @param string $operation The GPG operation to perform.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setOperation($operation)
|
||||
{
|
||||
$op = null;
|
||||
$opArg = null;
|
||||
|
||||
// Regexp matching all GPG "operational" arguments
|
||||
$regexp = '/--('
|
||||
. 'version|import|list-public-keys|list-secret-keys'
|
||||
. '|list-keys|delete-key|delete-secret-key|encrypt|sign|clearsign'
|
||||
. '|detach-sign|decrypt|verify|export-secret-keys|export|gen-key'
|
||||
. ')/';
|
||||
|
||||
if (strpos($operation, ' ') === false) {
|
||||
$op = trim($operation, '- ');
|
||||
} else if (preg_match($regexp, $operation, $matches, PREG_OFFSET_CAPTURE)) {
|
||||
$op = trim($matches[0][0], '-');
|
||||
$op_len = $matches[0][1] + mb_strlen($op, '8bit') + 3;
|
||||
$command = mb_substr($operation, $op_len, null, '8bit');
|
||||
|
||||
// we really need the argument if it is a key ID/fingerprint or email
|
||||
// address se we can use simplified regexp to "revert escapeshellarg()"
|
||||
if (preg_match('/^[\'"]([a-zA-Z0-9:@._-]+)[\'"]/', $command, $matches)) {
|
||||
$opArg = $matches[1];
|
||||
}
|
||||
}
|
||||
|
||||
$this->operation = $op;
|
||||
$this->operationArg = $opArg;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ handleStatus()
|
||||
|
||||
/**
|
||||
* Handles error values in the status output from GPG
|
||||
*
|
||||
* This method is responsible for setting the
|
||||
* {@link self::$errorCode}. See <b>doc/DETAILS</b> in the
|
||||
* {@link http://www.gnupg.org/download/ GPG distribution} for detailed
|
||||
* information on GPG's status output.
|
||||
*
|
||||
* @param string $line the status line to handle.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handleStatus($line)
|
||||
{
|
||||
$tokens = explode(' ', $line);
|
||||
switch ($tokens[0]) {
|
||||
case 'NODATA':
|
||||
$this->errorCode = Crypt_GPG::ERROR_NO_DATA;
|
||||
break;
|
||||
|
||||
case 'DECRYPTION_OKAY':
|
||||
// If the message is encrypted, this is the all-clear signal.
|
||||
$this->data['DecryptionOkay'] = true;
|
||||
$this->errorCode = Crypt_GPG::ERROR_NONE;
|
||||
break;
|
||||
|
||||
case 'DELETE_PROBLEM':
|
||||
if ($tokens[1] == '1') {
|
||||
$this->errorCode = Crypt_GPG::ERROR_KEY_NOT_FOUND;
|
||||
break;
|
||||
} elseif ($tokens[1] == '2') {
|
||||
$this->errorCode = Crypt_GPG::ERROR_DELETE_PRIVATE_KEY;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'IMPORT_OK':
|
||||
$this->data['Import']['fingerprint'] = $tokens[2];
|
||||
|
||||
if (empty($this->data['Import']['fingerprints'])) {
|
||||
$this->data['Import']['fingerprints'] = array($tokens[2]);
|
||||
} else if (!in_array($tokens[2], $this->data['Import']['fingerprints'])) {
|
||||
$this->data['Import']['fingerprints'][] = $tokens[2];
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'IMPORT_RES':
|
||||
$this->data['Import']['public_imported'] = intval($tokens[3]);
|
||||
$this->data['Import']['public_unchanged'] = intval($tokens[5]);
|
||||
$this->data['Import']['private_imported'] = intval($tokens[11]);
|
||||
$this->data['Import']['private_unchanged'] = intval($tokens[12]);
|
||||
break;
|
||||
|
||||
case 'NO_PUBKEY':
|
||||
case 'NO_SECKEY':
|
||||
$this->data['ErrorKeyId'] = $tokens[1];
|
||||
|
||||
if ($this->errorCode != Crypt_GPG::ERROR_MISSING_PASSPHRASE
|
||||
&& $this->errorCode != Crypt_GPG::ERROR_BAD_PASSPHRASE
|
||||
) {
|
||||
$this->errorCode = Crypt_GPG::ERROR_KEY_NOT_FOUND;
|
||||
}
|
||||
|
||||
// note: this message is also received if there are multiple
|
||||
// recipients and a previous key had a correct passphrase.
|
||||
$this->data['MissingKeys'][$tokens[1]] = $tokens[1];
|
||||
|
||||
// @FIXME: remove missing passphrase registered in ENC_TO handler
|
||||
// This is for GnuPG 2.1
|
||||
unset($this->data['MissingPassphrases'][$tokens[1]]);
|
||||
break;
|
||||
|
||||
case 'KEY_CONSIDERED':
|
||||
// In GnuPG 2.1.x exporting/importing a secret key requires passphrase
|
||||
// However, no NEED_PASSPRASE is returned, https://bugs.gnupg.org/gnupg/issue2667
|
||||
// So, handling KEY_CONSIDERED and GET_HIDDEN is needed.
|
||||
if (!array_key_exists('KeyConsidered', $this->data)) {
|
||||
$this->data['KeyConsidered'] = $tokens[1];
|
||||
}
|
||||
break;
|
||||
|
||||
case 'USERID_HINT':
|
||||
// remember the user id for pretty exception messages
|
||||
// GnuPG 2.1.15 gives me: "USERID_HINT 0000000000000000 [?]"
|
||||
$keyId = $tokens[1];
|
||||
if (strcspn($keyId, '0')) {
|
||||
$username = implode(' ', array_splice($tokens, 2));
|
||||
$this->data['BadPassphrases'][$keyId] = $username;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'ENC_TO':
|
||||
// Now we know the message is encrypted. Set flag to check if
|
||||
// decryption succeeded.
|
||||
$this->data['DecryptionOkay'] = false;
|
||||
|
||||
// this is the new key message
|
||||
$this->data['CurrentSubKeyId'] = $keyId = $tokens[1];
|
||||
|
||||
// For some reason in GnuPG 2.1.11 I get only ENC_TO and no
|
||||
// NEED_PASSPHRASE/MISSING_PASSPHRASE/USERID_HINT
|
||||
// This is not needed for GnuPG 2.1.15
|
||||
if (!empty($_ENV['PINENTRY_USER_DATA'])) {
|
||||
$passphrases = json_decode($_ENV['PINENTRY_USER_DATA'], true);
|
||||
} else {
|
||||
$passphrases = array();
|
||||
}
|
||||
|
||||
// @TODO: Get user name/email
|
||||
$this->data['BadPassphrases'][$keyId] = $keyId;
|
||||
if (empty($passphrases) || empty($passphrases[$keyId])) {
|
||||
$this->data['MissingPassphrases'][$keyId] = $keyId;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'GOOD_PASSPHRASE':
|
||||
// if we got a good passphrase, remove the key from the list of
|
||||
// bad passphrases.
|
||||
if (isset($this->data['CurrentSubKeyId'])) {
|
||||
unset($this->data['BadPassphrases'][$this->data['CurrentSubKeyId']]);
|
||||
unset($this->data['MissingPassphrases'][$this->data['CurrentSubKeyId']]);
|
||||
}
|
||||
|
||||
$this->needPassphrase--;
|
||||
break;
|
||||
|
||||
case 'BAD_PASSPHRASE':
|
||||
$this->errorCode = Crypt_GPG::ERROR_BAD_PASSPHRASE;
|
||||
break;
|
||||
|
||||
case 'MISSING_PASSPHRASE':
|
||||
if (isset($this->data['CurrentSubKeyId'])) {
|
||||
$this->data['MissingPassphrases'][$this->data['CurrentSubKeyId']]
|
||||
= $this->data['CurrentSubKeyId'];
|
||||
}
|
||||
|
||||
$this->errorCode = Crypt_GPG::ERROR_MISSING_PASSPHRASE;
|
||||
break;
|
||||
|
||||
case 'GET_HIDDEN':
|
||||
if ($tokens[1] == 'passphrase.enter' && isset($this->data['KeyConsidered'])) {
|
||||
$tokens[1] = $this->data['KeyConsidered'];
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
// no break
|
||||
|
||||
case 'NEED_PASSPHRASE':
|
||||
$passphrase = $this->getPin($tokens[1]);
|
||||
|
||||
$this->engine->sendCommand($passphrase);
|
||||
|
||||
if ($passphrase === '') {
|
||||
$this->needPassphrase++;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'SIG_CREATED':
|
||||
$this->data['SigCreated'] = $line;
|
||||
break;
|
||||
|
||||
case 'SIG_ID':
|
||||
// note: signature id comes before new signature line and may not
|
||||
// exist for some signature types
|
||||
$this->data['SignatureId'] = $tokens[1];
|
||||
break;
|
||||
|
||||
case 'EXPSIG':
|
||||
case 'EXPKEYSIG':
|
||||
case 'REVKEYSIG':
|
||||
case 'BADSIG':
|
||||
case 'ERRSIG':
|
||||
$this->errorCode = Crypt_GPG::ERROR_BAD_SIGNATURE;
|
||||
// no break
|
||||
case 'GOODSIG':
|
||||
$signature = new Crypt_GPG_Signature();
|
||||
|
||||
// if there was a signature id, set it on the new signature
|
||||
if (!empty($this->data['SignatureId'])) {
|
||||
$signature->setId($this->data['SignatureId']);
|
||||
$this->data['SignatureId'] = '';
|
||||
}
|
||||
|
||||
// Detect whether fingerprint or key id was returned and set
|
||||
// signature values appropriately. Key ids are strings of either
|
||||
// 16 or 8 hexadecimal characters. Fingerprints are strings of 40
|
||||
// hexadecimal characters. The key id is the last 16 characters of
|
||||
// the key fingerprint.
|
||||
if (mb_strlen($tokens[1], '8bit') > 16) {
|
||||
$signature->setKeyFingerprint($tokens[1]);
|
||||
$signature->setKeyId(mb_substr($tokens[1], -16, null, '8bit'));
|
||||
} else {
|
||||
$signature->setKeyId($tokens[1]);
|
||||
}
|
||||
|
||||
// get user id string
|
||||
if ($tokens[0] != 'ERRSIG') {
|
||||
$string = implode(' ', array_splice($tokens, 2));
|
||||
$string = rawurldecode($string);
|
||||
|
||||
$signature->setUserId(Crypt_GPG_UserId::parse($string));
|
||||
}
|
||||
|
||||
$this->data['Signatures'][] = $signature;
|
||||
break;
|
||||
|
||||
case 'VALIDSIG':
|
||||
if (empty($this->data['Signatures'])) {
|
||||
break;
|
||||
}
|
||||
|
||||
$signature = end($this->data['Signatures']);
|
||||
|
||||
$signature->setValid(true);
|
||||
$signature->setKeyFingerprint($tokens[1]);
|
||||
|
||||
if (strpos($tokens[3], 'T') === false) {
|
||||
$signature->setCreationDate($tokens[3]);
|
||||
} else {
|
||||
$signature->setCreationDate(strtotime($tokens[3]));
|
||||
}
|
||||
|
||||
if (array_key_exists(4, $tokens)) {
|
||||
if (strpos($tokens[4], 'T') === false) {
|
||||
$signature->setExpirationDate($tokens[4]);
|
||||
} else {
|
||||
$signature->setExpirationDate(strtotime($tokens[4]));
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'KEY_CREATED':
|
||||
if (isset($this->data['Handle']) && $tokens[3] == $this->data['Handle']) {
|
||||
$this->data['KeyCreated'] = $tokens[2];
|
||||
}
|
||||
break;
|
||||
|
||||
case 'KEY_NOT_CREATED':
|
||||
if (isset($this->data['Handle']) && $tokens[1] == $this->data['Handle']) {
|
||||
$this->errorCode = Crypt_GPG::ERROR_KEY_NOT_CREATED;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'PROGRESS':
|
||||
// todo: at some point, support reporting status async
|
||||
break;
|
||||
|
||||
// GnuPG 2.1 uses FAILURE and ERROR responses
|
||||
case 'FAILURE':
|
||||
case 'ERROR':
|
||||
$errnum = (int) $tokens[2];
|
||||
$source = $errnum >> 24;
|
||||
$errcode = $errnum & 0xFFFFFF;
|
||||
|
||||
switch ($errcode) {
|
||||
case 11: // bad passphrase
|
||||
case 87: // bad PIN
|
||||
$this->errorCode = Crypt_GPG::ERROR_BAD_PASSPHRASE;
|
||||
break;
|
||||
|
||||
case 177: // no passphrase
|
||||
case 178: // no PIN
|
||||
$this->errorCode = Crypt_GPG::ERROR_MISSING_PASSPHRASE;
|
||||
break;
|
||||
|
||||
case 58:
|
||||
$this->errorCode = Crypt_GPG::ERROR_NO_DATA;
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ handleError()
|
||||
|
||||
/**
|
||||
* Handles error values in the error output from GPG
|
||||
*
|
||||
* This method is responsible for setting the
|
||||
* {@link Crypt_GPG_Engine::$_errorCode}.
|
||||
*
|
||||
* @param string $line the error line to handle.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handleError($line)
|
||||
{
|
||||
if ($this->errorCode === Crypt_GPG::ERROR_NONE) {
|
||||
$pattern = '/no valid OpenPGP data found/';
|
||||
if (preg_match($pattern, $line) === 1) {
|
||||
$this->errorCode = Crypt_GPG::ERROR_NO_DATA;
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->errorCode === Crypt_GPG::ERROR_NONE) {
|
||||
$pattern = '/No secret key|secret key not available/';
|
||||
if (preg_match($pattern, $line) === 1) {
|
||||
$this->errorCode = Crypt_GPG::ERROR_KEY_NOT_FOUND;
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->errorCode === Crypt_GPG::ERROR_NONE) {
|
||||
$pattern = '/No public key|public key not found/';
|
||||
if (preg_match($pattern, $line) === 1) {
|
||||
$this->errorCode = Crypt_GPG::ERROR_KEY_NOT_FOUND;
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->errorCode === Crypt_GPG::ERROR_NONE) {
|
||||
$matches = array();
|
||||
$pattern = '/can\'t (?:access|open) `(.*?)\'/';
|
||||
if (preg_match($pattern, $line, $matches) === 1) {
|
||||
$this->data['ErrorFilename'] = $matches[1];
|
||||
$this->errorCode = Crypt_GPG::ERROR_FILE_PERMISSIONS;
|
||||
}
|
||||
}
|
||||
|
||||
// GnuPG 2.1: It should return MISSING_PASSPHRASE, but it does not
|
||||
// we have to detect it this way. This happens e.g. on private key import
|
||||
if ($this->errorCode === Crypt_GPG::ERROR_NONE) {
|
||||
$matches = array();
|
||||
$pattern = '/key ([0-9A-F]+).* (Bad|No) passphrase/';
|
||||
if (preg_match($pattern, $line, $matches) === 1) {
|
||||
$keyId = $matches[1];
|
||||
// @TODO: Get user name/email
|
||||
if (empty($this->data['BadPassphrases'][$keyId])) {
|
||||
$this->data['BadPassphrases'][$keyId] = $keyId;
|
||||
}
|
||||
if ($matches[2] == 'Bad') {
|
||||
$this->errorCode = Crypt_GPG::ERROR_BAD_PASSPHRASE;
|
||||
} else {
|
||||
$this->errorCode = Crypt_GPG::ERROR_MISSING_PASSPHRASE;
|
||||
if (empty($this->data['MissingPassphrases'][$keyId])) {
|
||||
$this->data['MissingPassphrases'][$keyId] = $keyId;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->errorCode === Crypt_GPG::ERROR_NONE && $this->operation == 'gen-key') {
|
||||
$pattern = '/:([0-9]+): invalid algorithm$/';
|
||||
if (preg_match($pattern, $line, $matches) === 1) {
|
||||
$this->errorCode = Crypt_GPG::ERROR_BAD_KEY_PARAMS;
|
||||
$this->data['LineNumber'] = intval($matches[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ throwException()
|
||||
|
||||
/**
|
||||
* On error throws exception
|
||||
*
|
||||
* @param int $exitcode GPG process exit code
|
||||
*
|
||||
* @return void
|
||||
* @throws Crypt_GPG_Exception
|
||||
*/
|
||||
public function throwException($exitcode = 0)
|
||||
{
|
||||
if ($exitcode != 0 && $this->errorCode === Crypt_GPG::ERROR_NONE) {
|
||||
if ($this->needPassphrase > 0) {
|
||||
$this->errorCode = Crypt_GPG::ERROR_MISSING_PASSPHRASE;
|
||||
} else if ($this->operation != 'import') {
|
||||
$this->errorCode = Crypt_GPG::ERROR_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->errorCode === Crypt_GPG::ERROR_NONE) {
|
||||
return;
|
||||
}
|
||||
|
||||
$code = $this->errorCode;
|
||||
$note = "Please use the 'debug' option when creating the Crypt_GPG " .
|
||||
"object, and file a bug report at " . Crypt_GPG::BUG_URI;
|
||||
|
||||
switch ($this->operation) {
|
||||
case 'version':
|
||||
throw new Crypt_GPG_Exception(
|
||||
'Unknown error getting GnuPG version information. ' . $note,
|
||||
$code
|
||||
);
|
||||
|
||||
case 'list-secret-keys':
|
||||
case 'list-public-keys':
|
||||
case 'list-keys':
|
||||
switch ($code) {
|
||||
case Crypt_GPG::ERROR_KEY_NOT_FOUND:
|
||||
// ignore not found key errors
|
||||
break;
|
||||
|
||||
case Crypt_GPG::ERROR_FILE_PERMISSIONS:
|
||||
if (!empty($this->data['ErrorFilename'])) {
|
||||
throw new Crypt_GPG_FileException(
|
||||
sprintf(
|
||||
'Error reading GnuPG data file \'%s\'. Check to make ' .
|
||||
'sure it is readable by the current user.',
|
||||
$this->data['ErrorFilename']
|
||||
),
|
||||
$code,
|
||||
$this->data['ErrorFilename']
|
||||
);
|
||||
}
|
||||
throw new Crypt_GPG_FileException(
|
||||
'Error reading GnuPG data file. Check to make sure that ' .
|
||||
'GnuPG data files are readable by the current user.',
|
||||
$code
|
||||
);
|
||||
|
||||
default:
|
||||
throw new Crypt_GPG_Exception(
|
||||
'Unknown error getting keys. ' . $note, $code
|
||||
);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'delete-key':
|
||||
case 'delete-secret-key':
|
||||
switch ($code) {
|
||||
case Crypt_GPG::ERROR_KEY_NOT_FOUND:
|
||||
throw new Crypt_GPG_KeyNotFoundException(
|
||||
'Key not found: ' . $this->operationArg,
|
||||
$code,
|
||||
$this->operationArg
|
||||
);
|
||||
|
||||
case Crypt_GPG::ERROR_DELETE_PRIVATE_KEY:
|
||||
throw new Crypt_GPG_DeletePrivateKeyException(
|
||||
'Private key must be deleted before public key can be ' .
|
||||
'deleted.',
|
||||
$code,
|
||||
$this->operationArg
|
||||
);
|
||||
|
||||
default:
|
||||
throw new Crypt_GPG_Exception(
|
||||
'Unknown error deleting key. ' . $note, $code
|
||||
);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'import':
|
||||
switch ($code) {
|
||||
case Crypt_GPG::ERROR_NO_DATA:
|
||||
throw new Crypt_GPG_NoDataException(
|
||||
'No valid GPG key data found.', $code
|
||||
);
|
||||
|
||||
case Crypt_GPG::ERROR_BAD_PASSPHRASE:
|
||||
case Crypt_GPG::ERROR_MISSING_PASSPHRASE:
|
||||
throw $this->badPassException($code, 'Cannot import private key.');
|
||||
|
||||
default:
|
||||
throw new Crypt_GPG_Exception(
|
||||
'Unknown error importing GPG key. ' . $note, $code
|
||||
);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'export':
|
||||
case 'export-secret-keys':
|
||||
switch ($code) {
|
||||
case Crypt_GPG::ERROR_BAD_PASSPHRASE:
|
||||
case Crypt_GPG::ERROR_MISSING_PASSPHRASE:
|
||||
throw $this->badPassException($code, 'Cannot export private key.');
|
||||
|
||||
default:
|
||||
throw new Crypt_GPG_Exception(
|
||||
'Unknown error exporting a key. ' . $note, $code
|
||||
);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'encrypt':
|
||||
case 'sign':
|
||||
case 'clearsign':
|
||||
case 'detach-sign':
|
||||
switch ($code) {
|
||||
case Crypt_GPG::ERROR_KEY_NOT_FOUND:
|
||||
throw new Crypt_GPG_KeyNotFoundException(
|
||||
'Cannot sign data. Private key not found. Import the '.
|
||||
'private key before trying to sign data.',
|
||||
$code,
|
||||
!empty($this->data['ErrorKeyId']) ? $this->data['ErrorKeyId'] : null
|
||||
);
|
||||
|
||||
case Crypt_GPG::ERROR_BAD_PASSPHRASE:
|
||||
throw new Crypt_GPG_BadPassphraseException(
|
||||
'Cannot sign data. Incorrect passphrase provided.', $code
|
||||
);
|
||||
|
||||
case Crypt_GPG::ERROR_MISSING_PASSPHRASE:
|
||||
throw new Crypt_GPG_BadPassphraseException(
|
||||
'Cannot sign data. No passphrase provided.', $code
|
||||
);
|
||||
|
||||
default:
|
||||
throw new Crypt_GPG_Exception(
|
||||
"Unknown error {$this->operation}ing data. $note", $code
|
||||
);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'verify':
|
||||
switch ($code) {
|
||||
case Crypt_GPG::ERROR_BAD_SIGNATURE:
|
||||
// ignore bad signature errors
|
||||
break;
|
||||
|
||||
case Crypt_GPG::ERROR_NO_DATA:
|
||||
throw new Crypt_GPG_NoDataException(
|
||||
'No valid signature data found.', $code
|
||||
);
|
||||
|
||||
case Crypt_GPG::ERROR_KEY_NOT_FOUND:
|
||||
throw new Crypt_GPG_KeyNotFoundException(
|
||||
'Public key required for data verification not in keyring.',
|
||||
$code,
|
||||
!empty($this->data['ErrorKeyId']) ? $this->data['ErrorKeyId'] : null
|
||||
);
|
||||
|
||||
default:
|
||||
throw new Crypt_GPG_Exception(
|
||||
'Unknown error validating signature details. ' . $note,
|
||||
$code
|
||||
);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'decrypt':
|
||||
switch ($code) {
|
||||
case Crypt_GPG::ERROR_BAD_SIGNATURE:
|
||||
// ignore bad signature errors
|
||||
break;
|
||||
|
||||
case Crypt_GPG::ERROR_KEY_NOT_FOUND:
|
||||
if (!empty($this->data['MissingKeys'])) {
|
||||
$keyId = reset($this->data['MissingKeys']);
|
||||
} else {
|
||||
$keyId = '';
|
||||
}
|
||||
|
||||
throw new Crypt_GPG_KeyNotFoundException(
|
||||
'Cannot decrypt data. No suitable private key is in the ' .
|
||||
'keyring. Import a suitable private key before trying to ' .
|
||||
'decrypt this data.',
|
||||
$code,
|
||||
$keyId
|
||||
);
|
||||
|
||||
case Crypt_GPG::ERROR_BAD_PASSPHRASE:
|
||||
case Crypt_GPG::ERROR_MISSING_PASSPHRASE:
|
||||
throw $this->badPassException($code, 'Cannot decrypt data.');
|
||||
|
||||
case Crypt_GPG::ERROR_NO_DATA:
|
||||
throw new Crypt_GPG_NoDataException(
|
||||
'Cannot decrypt data. No PGP encrypted data was found in '.
|
||||
'the provided data.',
|
||||
$code
|
||||
);
|
||||
|
||||
default:
|
||||
throw new Crypt_GPG_Exception(
|
||||
'Unknown error decrypting data.', $code
|
||||
);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'gen-key':
|
||||
switch ($code) {
|
||||
case Crypt_GPG::ERROR_BAD_KEY_PARAMS:
|
||||
throw new Crypt_GPG_InvalidKeyParamsException(
|
||||
'Invalid key algorithm specified.', $code
|
||||
);
|
||||
|
||||
default:
|
||||
throw new Crypt_GPG_Exception(
|
||||
'Unknown error generating key-pair. ' . $note, $code
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ getData()
|
||||
|
||||
/**
|
||||
* Get data from the last process execution.
|
||||
*
|
||||
* @param string $name Data element name:
|
||||
* - SigCreated: The last SIG_CREATED status.
|
||||
* - KeyConsidered: The last KEY_CONSIDERED status identifier.
|
||||
* - KeyCreated: The KEY_CREATED status (for specified Handle).
|
||||
* - Signatures: Signatures data from verification process.
|
||||
* - LineNumber: Number of the gen-key error line.
|
||||
* - Import: Result of IMPORT_OK/IMPORT_RES
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getData($name)
|
||||
{
|
||||
return isset($this->data[$name]) ? $this->data[$name] : null;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ setData()
|
||||
|
||||
/**
|
||||
* Set data for the process execution.
|
||||
*
|
||||
* @param string $name Data element name:
|
||||
* - Handle: The unique key handle used by this handler
|
||||
* The key handle is used to track GPG status output
|
||||
* for a particular key on --gen-key command before
|
||||
* the key has its own identifier.
|
||||
* @param mixed $value Data element value
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setData($name, $value)
|
||||
{
|
||||
switch ($name) {
|
||||
case 'Handle':
|
||||
$this->data[$name] = strval($value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ setData()
|
||||
|
||||
/**
|
||||
* Create Crypt_GPG_BadPassphraseException from operation data.
|
||||
*
|
||||
* @param int $code Error code
|
||||
* @param string $message Error message
|
||||
*
|
||||
* @return Crypt_GPG_BadPassphraseException
|
||||
*/
|
||||
protected function badPassException($code, $message)
|
||||
{
|
||||
$badPassphrases = array_diff_key(
|
||||
isset($this->data['BadPassphrases']) ? $this->data['BadPassphrases'] : array(),
|
||||
isset($this->data['MissingPassphrases']) ? $this->data['MissingPassphrases'] : array()
|
||||
);
|
||||
|
||||
$missingPassphrases = array_intersect_key(
|
||||
isset($this->data['BadPassphrases']) ? $this->data['BadPassphrases'] : array(),
|
||||
isset($this->data['MissingPassphrases']) ? $this->data['MissingPassphrases'] : array()
|
||||
);
|
||||
|
||||
if (count($badPassphrases) > 0) {
|
||||
$message .= ' Incorrect passphrase provided for keys: "' .
|
||||
implode('", "', $badPassphrases) . '".';
|
||||
}
|
||||
if (count($missingPassphrases) > 0) {
|
||||
$message .= ' No passphrase provided for keys: "' .
|
||||
implode('", "', $missingPassphrases) . '".';
|
||||
}
|
||||
|
||||
return new Crypt_GPG_BadPassphraseException(
|
||||
$message,
|
||||
$code,
|
||||
$badPassphrases,
|
||||
$missingPassphrases
|
||||
);
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ getPin()
|
||||
|
||||
/**
|
||||
* Get registered passphrase for specified key.
|
||||
*
|
||||
* @param string $key Key identifier
|
||||
*
|
||||
* @return string Passphrase
|
||||
*/
|
||||
protected function getPin($key)
|
||||
{
|
||||
$passphrase = '';
|
||||
$keyIdLength = mb_strlen($key, '8bit');
|
||||
|
||||
if ($keyIdLength && !empty($_ENV['PINENTRY_USER_DATA'])) {
|
||||
$passphrases = json_decode($_ENV['PINENTRY_USER_DATA'], true);
|
||||
foreach ($passphrases as $_keyId => $pass) {
|
||||
$keyId = $key;
|
||||
$_keyIdLength = mb_strlen($_keyId, '8bit');
|
||||
|
||||
// Get last X characters of key identifier to compare
|
||||
if ($keyIdLength < $_keyIdLength) {
|
||||
$_keyId = mb_substr($_keyId, -$keyIdLength, null, '8bit');
|
||||
} else if ($keyIdLength > $_keyIdLength) {
|
||||
$keyId = mb_substr($keyId, -$_keyIdLength, null, '8bit');
|
||||
}
|
||||
|
||||
if ($_keyId === $keyId) {
|
||||
$passphrase = $pass;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $passphrase;
|
||||
}
|
||||
|
||||
// }}}
|
||||
}
|
||||
|
||||
// }}}
|
||||
|
||||
?>
|
||||
+426
@@ -0,0 +1,426 @@
|
||||
<?php
|
||||
|
||||
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
|
||||
|
||||
/**
|
||||
* A class representing GPG signatures
|
||||
*
|
||||
* This file contains a data class representing a GPG signature.
|
||||
*
|
||||
* 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, see
|
||||
* <http://www.gnu.org/licenses/>
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
|
||||
/**
|
||||
* User id class definition
|
||||
*/
|
||||
require_once 'Crypt/GPG/UserId.php';
|
||||
|
||||
// {{{ class Crypt_GPG_Signature
|
||||
|
||||
/**
|
||||
* A class for GPG signature information
|
||||
*
|
||||
* This class is used to store the results of the Crypt_GPG::verify() method.
|
||||
*
|
||||
* @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
|
||||
* @see Crypt_GPG::verify()
|
||||
*/
|
||||
class Crypt_GPG_Signature
|
||||
{
|
||||
// {{{ class properties
|
||||
|
||||
/**
|
||||
* A base64-encoded string containing a unique id for this signature if
|
||||
* this signature has been verified as ok
|
||||
*
|
||||
* This id is used to prevent replay attacks and is not present for all
|
||||
* types of signatures.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $_id = '';
|
||||
|
||||
/**
|
||||
* The fingerprint of the key used to create the signature
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $_keyFingerprint = '';
|
||||
|
||||
/**
|
||||
* The id of the key used to create the signature
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $_keyId = '';
|
||||
|
||||
/**
|
||||
* The creation date of this signature
|
||||
*
|
||||
* This is a Unix timestamp.
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
private $_creationDate = 0;
|
||||
|
||||
/**
|
||||
* The expiration date of the signature
|
||||
*
|
||||
* This is a Unix timestamp. If this signature does not expire, this will
|
||||
* be zero.
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
private $_expirationDate = 0;
|
||||
|
||||
/**
|
||||
* The user id associated with this signature
|
||||
*
|
||||
* @var Crypt_GPG_UserId
|
||||
*/
|
||||
private $_userId = null;
|
||||
|
||||
/**
|
||||
* Whether or not this signature is valid
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
private $_isValid = false;
|
||||
|
||||
// }}}
|
||||
// {{{ __construct()
|
||||
|
||||
/**
|
||||
* Creates a new signature
|
||||
*
|
||||
* Signatures can be initialized from an array of named values. Available
|
||||
* names are:
|
||||
*
|
||||
* - <kbd>string id</kbd> - the unique id of this signature.
|
||||
* - <kbd>string fingerprint</kbd> - the fingerprint of the key used to
|
||||
* create the signature. The fingerprint
|
||||
* should not contain formatting
|
||||
* characters.
|
||||
* - <kbd>string keyId</kbd> - the id of the key used to create the
|
||||
* the signature.
|
||||
* - <kbd>integer creation</kbd> - the date the signature was created.
|
||||
* This is a UNIX timestamp.
|
||||
* - <kbd>integer expiration</kbd> - the date the signature expired. This
|
||||
* is a UNIX timestamp. If the signature
|
||||
* does not expire, use 0.
|
||||
* - <kbd>boolean valid</kbd> - whether or not the signature is valid.
|
||||
* - <kbd>string userId</kbd> - the user id associated with the
|
||||
* signature. This may also be a
|
||||
* {@link Crypt_GPG_UserId} object.
|
||||
*
|
||||
* @param Crypt_GPG_Signature|array $signature optional. Either an existing
|
||||
* signature object, which is copied; or an array of initial values.
|
||||
*/
|
||||
public function __construct($signature = null)
|
||||
{
|
||||
// copy from object
|
||||
if ($signature instanceof Crypt_GPG_Signature) {
|
||||
$this->_id = $signature->_id;
|
||||
$this->_keyFingerprint = $signature->_keyFingerprint;
|
||||
$this->_keyId = $signature->_keyId;
|
||||
$this->_creationDate = $signature->_creationDate;
|
||||
$this->_expirationDate = $signature->_expirationDate;
|
||||
$this->_isValid = $signature->_isValid;
|
||||
|
||||
if ($signature->_userId instanceof Crypt_GPG_UserId) {
|
||||
$this->_userId = clone $signature->_userId;
|
||||
}
|
||||
}
|
||||
|
||||
// initialize from array
|
||||
if (is_array($signature)) {
|
||||
if (array_key_exists('id', $signature)) {
|
||||
$this->setId($signature['id']);
|
||||
}
|
||||
|
||||
if (array_key_exists('fingerprint', $signature)) {
|
||||
$this->setKeyFingerprint($signature['fingerprint']);
|
||||
}
|
||||
|
||||
if (array_key_exists('keyId', $signature)) {
|
||||
$this->setKeyId($signature['keyId']);
|
||||
}
|
||||
|
||||
if (array_key_exists('creation', $signature)) {
|
||||
$this->setCreationDate($signature['creation']);
|
||||
}
|
||||
|
||||
if (array_key_exists('expiration', $signature)) {
|
||||
$this->setExpirationDate($signature['expiration']);
|
||||
}
|
||||
|
||||
if (array_key_exists('valid', $signature)) {
|
||||
$this->setValid($signature['valid']);
|
||||
}
|
||||
|
||||
if (array_key_exists('userId', $signature)) {
|
||||
$userId = new Crypt_GPG_UserId($signature['userId']);
|
||||
$this->setUserId($userId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ getId()
|
||||
|
||||
/**
|
||||
* Gets the id of this signature
|
||||
*
|
||||
* @return string a base64-encoded string containing a unique id for this
|
||||
* signature. This id is used to prevent replay attacks and
|
||||
* is not present for all types of signatures.
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
return $this->_id;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ getKeyFingerprint()
|
||||
|
||||
/**
|
||||
* Gets the fingerprint of the key used to create this signature
|
||||
*
|
||||
* @return string the fingerprint of the key used to create this signature.
|
||||
*/
|
||||
public function getKeyFingerprint()
|
||||
{
|
||||
return $this->_keyFingerprint;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ getKeyId()
|
||||
|
||||
/**
|
||||
* Gets the id of the key used to create this signature
|
||||
*
|
||||
* Whereas the fingerprint of the signing key may not always be available
|
||||
* (for example if the signature is bad), the id should always be
|
||||
* available.
|
||||
*
|
||||
* @return string the id of the key used to create this signature.
|
||||
*/
|
||||
public function getKeyId()
|
||||
{
|
||||
return $this->_keyId;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ getCreationDate()
|
||||
|
||||
/**
|
||||
* Gets the creation date of this signature
|
||||
*
|
||||
* @return integer the creation date of this signature. This is a Unix
|
||||
* timestamp.
|
||||
*/
|
||||
public function getCreationDate()
|
||||
{
|
||||
return $this->_creationDate;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ getExpirationDate()
|
||||
|
||||
/**
|
||||
* Gets the expiration date of the signature
|
||||
*
|
||||
* @return integer the expiration date of this signature. This is a Unix
|
||||
* timestamp. If this signature does not expire, this will
|
||||
* be zero.
|
||||
*/
|
||||
public function getExpirationDate()
|
||||
{
|
||||
return $this->_expirationDate;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ getUserId()
|
||||
|
||||
/**
|
||||
* Gets the user id associated with this signature
|
||||
*
|
||||
* @return Crypt_GPG_UserId the user id associated with this signature.
|
||||
*/
|
||||
public function getUserId()
|
||||
{
|
||||
return $this->_userId;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ isValid()
|
||||
|
||||
/**
|
||||
* Gets whether or no this signature is valid
|
||||
*
|
||||
* @return boolean true if this signature is valid and false if it is not.
|
||||
*/
|
||||
public function isValid()
|
||||
{
|
||||
return $this->_isValid;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ setId()
|
||||
|
||||
/**
|
||||
* Sets the id of this signature
|
||||
*
|
||||
* @param string $id a base64-encoded string containing a unique id for
|
||||
* this signature.
|
||||
*
|
||||
* @return Crypt_GPG_Signature the current object, for fluent interface.
|
||||
*
|
||||
* @see Crypt_GPG_Signature::getId()
|
||||
*/
|
||||
public function setId($id)
|
||||
{
|
||||
$this->_id = strval($id);
|
||||
return $this;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ setKeyFingerprint()
|
||||
|
||||
/**
|
||||
* Sets the key fingerprint of this signature
|
||||
*
|
||||
* @param string $fingerprint the key fingerprint of this signature. This
|
||||
* is the fingerprint of the primary key used to
|
||||
* create this signature.
|
||||
*
|
||||
* @return Crypt_GPG_Signature the current object, for fluent interface.
|
||||
*/
|
||||
public function setKeyFingerprint($fingerprint)
|
||||
{
|
||||
$this->_keyFingerprint = strval($fingerprint);
|
||||
return $this;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ setKeyId()
|
||||
|
||||
/**
|
||||
* Sets the key id of this signature
|
||||
*
|
||||
* @param string $id the key id of this signature. This is the id of the
|
||||
* primary key used to create this signature.
|
||||
*
|
||||
* @return Crypt_GPG_Signature the current object, for fluent interface.
|
||||
*/
|
||||
public function setKeyId($id)
|
||||
{
|
||||
$this->_keyId = strval($id);
|
||||
return $this;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ setCreationDate()
|
||||
|
||||
/**
|
||||
* Sets the creation date of this signature
|
||||
*
|
||||
* @param integer $creationDate the creation date of this signature. This
|
||||
* is a Unix timestamp.
|
||||
*
|
||||
* @return Crypt_GPG_Signature the current object, for fluent interface.
|
||||
*/
|
||||
public function setCreationDate($creationDate)
|
||||
{
|
||||
$this->_creationDate = intval($creationDate);
|
||||
return $this;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ setExpirationDate()
|
||||
|
||||
/**
|
||||
* Sets the expiration date of this signature
|
||||
*
|
||||
* @param integer $expirationDate the expiration date of this signature.
|
||||
* This is a Unix timestamp. Specify zero if
|
||||
* this signature does not expire.
|
||||
*
|
||||
* @return Crypt_GPG_Signature the current object, for fluent interface.
|
||||
*/
|
||||
public function setExpirationDate($expirationDate)
|
||||
{
|
||||
$this->_expirationDate = intval($expirationDate);
|
||||
return $this;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ setUserId()
|
||||
|
||||
/**
|
||||
* Sets the user id associated with this signature
|
||||
*
|
||||
* @param Crypt_GPG_UserId $userId the user id associated with this
|
||||
* signature.
|
||||
*
|
||||
* @return Crypt_GPG_Signature the current object, for fluent interface.
|
||||
*/
|
||||
public function setUserId(Crypt_GPG_UserId $userId)
|
||||
{
|
||||
$this->_userId = $userId;
|
||||
return $this;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ setValid()
|
||||
|
||||
/**
|
||||
* Sets whether or not this signature is valid
|
||||
*
|
||||
* @param boolean $isValid true if this signature is valid and false if it
|
||||
* is not.
|
||||
*
|
||||
* @return Crypt_GPG_Signature the current object, for fluent interface.
|
||||
*/
|
||||
public function setValid($isValid)
|
||||
{
|
||||
$this->_isValid = ($isValid) ? true : false;
|
||||
return $this;
|
||||
}
|
||||
|
||||
// }}}
|
||||
}
|
||||
|
||||
// }}}
|
||||
|
||||
?>
|
||||
Generated
Vendored
+224
@@ -0,0 +1,224 @@
|
||||
<?php
|
||||
/**
|
||||
* Part of Crypt_GPG
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* @category Encryption
|
||||
* @package Crypt_GPG
|
||||
* @author Christian Weiske <cweiske@php.net>
|
||||
* @copyright 2015 PEAR
|
||||
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
|
||||
* @link http://pear.php.net/package/Crypt_GPG
|
||||
* @link http://pear.php.net/manual/en/package.encryption.crypt-gpg.php
|
||||
* @link http://www.gnupg.org/
|
||||
*/
|
||||
|
||||
/**
|
||||
* Information about a recently created signature.
|
||||
*
|
||||
* @category Encryption
|
||||
* @package Crypt_GPG
|
||||
* @author Christian Weiske <cweiske@php.net>
|
||||
* @copyright 2015 PEAR
|
||||
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
|
||||
* @link http://pear.php.net/package/Crypt_GPG
|
||||
* @link http://pear.php.net/manual/en/package.encryption.crypt-gpg.php
|
||||
* @link http://www.gnupg.org/
|
||||
*/
|
||||
class Crypt_GPG_SignatureCreationInfo
|
||||
{
|
||||
/**
|
||||
* One of the three signature types:
|
||||
* - {@link Crypt_GPG::SIGN_MODE_NORMAL}
|
||||
* - {@link Crypt_GPG::SIGN_MODE_CLEAR}
|
||||
* - {@link Crypt_GPG::SIGN_MODE_DETACHED}
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
protected $mode;
|
||||
|
||||
/**
|
||||
* Public Key algorithm
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
protected $pkAlgorithm;
|
||||
|
||||
/**
|
||||
* Algorithm to hash the data
|
||||
*
|
||||
* @see RFC 2440 / 9.4. Hash Algorithm
|
||||
* @var integer
|
||||
*/
|
||||
protected $hashAlgorithm;
|
||||
|
||||
/**
|
||||
* OpenPGP signature class
|
||||
*
|
||||
* @var mixed
|
||||
*/
|
||||
protected $class;
|
||||
|
||||
/**
|
||||
* Unix timestamp when the signature was created
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
protected $timestamp;
|
||||
|
||||
/**
|
||||
* Key fingerprint
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $keyFingerprint;
|
||||
|
||||
/**
|
||||
* If the line given to the constructor was valid
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
protected $valid;
|
||||
|
||||
/**
|
||||
* Names for the hash algorithm IDs.
|
||||
*
|
||||
* Names taken from RFC 3156, without the leading "pgp-".
|
||||
*
|
||||
* @see RFC 2440 / 9.4. Hash Algorithm
|
||||
* @see RFC 3156 / 5. OpenPGP signed data
|
||||
* @var array
|
||||
*/
|
||||
protected static $hashAlgorithmNames = array(
|
||||
1 => 'md5',
|
||||
2 => 'sha1',
|
||||
3 => 'ripemd160',
|
||||
5 => 'md2',
|
||||
6 => 'tiger192',
|
||||
7 => 'haval-5-160',
|
||||
);
|
||||
|
||||
/**
|
||||
* Parse a SIG_CREATED line from gnupg
|
||||
*
|
||||
* @param string $sigCreatedLine Line beginning with "SIG_CREATED "
|
||||
*/
|
||||
public function __construct($sigCreatedLine = null)
|
||||
{
|
||||
if ($sigCreatedLine === null) {
|
||||
$this->valid = false;
|
||||
return;
|
||||
}
|
||||
|
||||
$parts = explode(' ', $sigCreatedLine);
|
||||
if (count($parts) !== 7) {
|
||||
$this->valid = false;
|
||||
return;
|
||||
}
|
||||
list(
|
||||
$title, $mode, $pkAlgorithm, $hashAlgorithm,
|
||||
$class, $timestamp, $keyFingerprint
|
||||
) = $parts;
|
||||
|
||||
switch (strtoupper($mode[0])) {
|
||||
case 'D':
|
||||
$this->mode = Crypt_GPG::SIGN_MODE_DETACHED;
|
||||
break;
|
||||
case 'C':
|
||||
$this->mode = Crypt_GPG::SIGN_MODE_CLEAR;
|
||||
break;
|
||||
case 'S':
|
||||
$this->mode = Crypt_GPG::SIGN_MODE_NORMAL;
|
||||
break;
|
||||
}
|
||||
|
||||
$this->pkAlgorithm = (int) $pkAlgorithm;
|
||||
$this->hashAlgorithm = (int) $hashAlgorithm;
|
||||
$this->class = $class;
|
||||
if (is_numeric($timestamp)) {
|
||||
$this->timestamp = (int) $timestamp;
|
||||
} else {
|
||||
$this->timestamp = strtotime($timestamp);
|
||||
}
|
||||
$this->keyFingerprint = $keyFingerprint;
|
||||
$this->valid = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the signature type
|
||||
* - {@link Crypt_GPG::SIGN_MODE_NORMAL}
|
||||
* - {@link Crypt_GPG::SIGN_MODE_CLEAR}
|
||||
* - {@link Crypt_GPG::SIGN_MODE_DETACHED}
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function getMode()
|
||||
{
|
||||
return $this->mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the public key algorithm used.
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function getPkAlgorithm()
|
||||
{
|
||||
return $this->pkAlgorithm;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the hash algorithm used to hash the data to sign.
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function getHashAlgorithm()
|
||||
{
|
||||
return $this->hashAlgorithm;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a name for the used hashing algorithm.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getHashAlgorithmName()
|
||||
{
|
||||
if (!isset(self::$hashAlgorithmNames[$this->hashAlgorithm])) {
|
||||
return null;
|
||||
}
|
||||
return self::$hashAlgorithmNames[$this->hashAlgorithm];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the timestamp at which the signature was created
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function getTimestamp()
|
||||
{
|
||||
return $this->timestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the key's fingerprint
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getKeyFingerprint()
|
||||
{
|
||||
return $this->keyFingerprint;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell if the fingerprint line given to the constructor was valid
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function isValid()
|
||||
{
|
||||
return $this->valid;
|
||||
}
|
||||
}
|
||||
?>
|
||||
+715
@@ -0,0 +1,715 @@
|
||||
<?php
|
||||
|
||||
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
|
||||
|
||||
/**
|
||||
* Contains a class representing GPG sub-keys and constants for GPG algorithms
|
||||
*
|
||||
* 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, see
|
||||
* <http://www.gnu.org/licenses/>
|
||||
*
|
||||
* @category Encryption
|
||||
* @package Crypt_GPG
|
||||
* @author Michael Gauthier <mike@silverorange.com>
|
||||
* @author Nathan Fredrickson <nathan@silverorange.com>
|
||||
* @copyright 2005-2010 silverorange
|
||||
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
|
||||
* @link http://pear.php.net/package/Crypt_GPG
|
||||
*/
|
||||
|
||||
// {{{ class Crypt_GPG_SubKey
|
||||
|
||||
/**
|
||||
* A class for GPG sub-key information
|
||||
*
|
||||
* This class is used to store the results of the {@link Crypt_GPG::getKeys()}
|
||||
* method. Sub-key objects are members of a {@link Crypt_GPG_Key} object.
|
||||
*
|
||||
* @category Encryption
|
||||
* @package Crypt_GPG
|
||||
* @author Michael Gauthier <mike@silverorange.com>
|
||||
* @author Nathan Fredrickson <nathan@silverorange.com>
|
||||
* @copyright 2005-2010 silverorange
|
||||
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
|
||||
* @link http://pear.php.net/package/Crypt_GPG
|
||||
* @see Crypt_GPG::getKeys()
|
||||
* @see Crypt_GPG_Key::getSubKeys()
|
||||
*/
|
||||
class Crypt_GPG_SubKey
|
||||
{
|
||||
// {{{ algorithm class constants
|
||||
|
||||
/**
|
||||
* RSA encryption algorithm.
|
||||
*/
|
||||
const ALGORITHM_RSA = 1;
|
||||
|
||||
/**
|
||||
* Elgamal encryption algorithm (encryption only).
|
||||
*/
|
||||
const ALGORITHM_ELGAMAL_ENC = 16;
|
||||
|
||||
/**
|
||||
* DSA encryption algorithm (sometimes called DH, sign only).
|
||||
*/
|
||||
const ALGORITHM_DSA = 17;
|
||||
|
||||
/**
|
||||
* Elgamal encryption algorithm (signage and encryption - should not be
|
||||
* used).
|
||||
*/
|
||||
const ALGORITHM_ELGAMAL_ENC_SGN = 20;
|
||||
|
||||
// }}}
|
||||
// {{{ usage class constants
|
||||
|
||||
/**
|
||||
* Key can be used to encrypt
|
||||
*/
|
||||
const USAGE_ENCRYPT = 1;
|
||||
|
||||
/**
|
||||
* Key can be used to sign
|
||||
*/
|
||||
const USAGE_SIGN = 2;
|
||||
|
||||
/**
|
||||
* Key can be used to certify other keys
|
||||
*/
|
||||
const USAGE_CERTIFY = 4;
|
||||
|
||||
/**
|
||||
* Key can be used for authentication
|
||||
*/
|
||||
const USAGE_AUTHENTICATION = 8;
|
||||
|
||||
// }}}
|
||||
// {{{ class properties
|
||||
|
||||
/**
|
||||
* The id of this sub-key
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $_id = '';
|
||||
|
||||
/**
|
||||
* The algorithm used to create this sub-key
|
||||
*
|
||||
* The value is one of the Crypt_GPG_SubKey::ALGORITHM_* constants.
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
private $_algorithm = 0;
|
||||
|
||||
/**
|
||||
* The fingerprint of this sub-key
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $_fingerprint = '';
|
||||
|
||||
/**
|
||||
* Length of this sub-key in bits
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
private $_length = 0;
|
||||
|
||||
/**
|
||||
* Date this sub-key was created
|
||||
*
|
||||
* This is a Unix timestamp.
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
private $_creationDate = 0;
|
||||
|
||||
/**
|
||||
* Date this sub-key expires
|
||||
*
|
||||
* This is a Unix timestamp. If this sub-key does not expire, this will be
|
||||
* zero.
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
private $_expirationDate = 0;
|
||||
|
||||
/**
|
||||
* Contains usage flags of this sub-key
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private $_usage = 0;
|
||||
|
||||
/**
|
||||
* Whether or not the private key for this sub-key exists in the keyring
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
private $_hasPrivate = false;
|
||||
|
||||
/**
|
||||
* Whether or not this sub-key is revoked
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
private $_isRevoked = false;
|
||||
|
||||
// }}}
|
||||
// {{{ __construct()
|
||||
|
||||
/**
|
||||
* Creates a new sub-key object
|
||||
*
|
||||
* Sub-keys can be initialized from an array of named values. Available
|
||||
* names are:
|
||||
*
|
||||
* - <kbd>string id</kbd> - the key id of the sub-key.
|
||||
* - <kbd>integer algorithm</kbd> - the encryption algorithm of the
|
||||
* sub-key.
|
||||
* - <kbd>string fingerprint</kbd> - the fingerprint of the sub-key. The
|
||||
* fingerprint should not contain
|
||||
* formatting characters.
|
||||
* - <kbd>integer length</kbd> - the length of the sub-key in bits.
|
||||
* - <kbd>integer creation</kbd> - the date the sub-key was created.
|
||||
* This is a UNIX timestamp.
|
||||
* - <kbd>integer expiration</kbd> - the date the sub-key expires. This
|
||||
* is a UNIX timestamp. If the sub-key
|
||||
* does not expire, use 0.
|
||||
* - <kbd>boolean canSign</kbd> - whether or not the sub-key can be
|
||||
* used to sign data.
|
||||
* - <kbd>boolean canEncrypt</kbd> - whether or not the sub-key can be
|
||||
* used to encrypt data.
|
||||
* - <kbd>integer usage</kbd> - the sub-key usage flags
|
||||
* - <kbd>boolean hasPrivate</kbd> - whether or not the private key for
|
||||
* the sub-key exists in the keyring.
|
||||
* - <kbd>boolean isRevoked</kbd> - whether or not this sub-key is
|
||||
* revoked.
|
||||
*
|
||||
* @param Crypt_GPG_SubKey|string|array $key optional. Either an existing
|
||||
* sub-key object, which is copied; a sub-key string, which is
|
||||
* parsed; or an array of initial values.
|
||||
*/
|
||||
public function __construct($key = null)
|
||||
{
|
||||
// parse from string
|
||||
if (is_string($key)) {
|
||||
$key = self::parse($key);
|
||||
}
|
||||
|
||||
// copy from object
|
||||
if ($key instanceof Crypt_GPG_SubKey) {
|
||||
$this->_id = $key->_id;
|
||||
$this->_algorithm = $key->_algorithm;
|
||||
$this->_fingerprint = $key->_fingerprint;
|
||||
$this->_length = $key->_length;
|
||||
$this->_creationDate = $key->_creationDate;
|
||||
$this->_expirationDate = $key->_expirationDate;
|
||||
$this->_usage = $key->_usage;
|
||||
$this->_hasPrivate = $key->_hasPrivate;
|
||||
$this->_isRevoked = $key->_isRevoked;
|
||||
}
|
||||
|
||||
// initialize from array
|
||||
if (is_array($key)) {
|
||||
if (array_key_exists('id', $key)) {
|
||||
$this->setId($key['id']);
|
||||
}
|
||||
|
||||
if (array_key_exists('algorithm', $key)) {
|
||||
$this->setAlgorithm($key['algorithm']);
|
||||
}
|
||||
|
||||
if (array_key_exists('fingerprint', $key)) {
|
||||
$this->setFingerprint($key['fingerprint']);
|
||||
}
|
||||
|
||||
if (array_key_exists('length', $key)) {
|
||||
$this->setLength($key['length']);
|
||||
}
|
||||
|
||||
if (array_key_exists('creation', $key)) {
|
||||
$this->setCreationDate($key['creation']);
|
||||
}
|
||||
|
||||
if (array_key_exists('expiration', $key)) {
|
||||
$this->setExpirationDate($key['expiration']);
|
||||
}
|
||||
|
||||
if (array_key_exists('usage', $key)) {
|
||||
$this->setUsage($key['usage']);
|
||||
}
|
||||
|
||||
if (array_key_exists('canSign', $key)) {
|
||||
$this->setCanSign($key['canSign']);
|
||||
}
|
||||
|
||||
if (array_key_exists('canEncrypt', $key)) {
|
||||
$this->setCanEncrypt($key['canEncrypt']);
|
||||
}
|
||||
|
||||
if (array_key_exists('hasPrivate', $key)) {
|
||||
$this->setHasPrivate($key['hasPrivate']);
|
||||
}
|
||||
|
||||
if (array_key_exists('isRevoked', $key)) {
|
||||
$this->setRevoked($key['isRevoked']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ getId()
|
||||
|
||||
/**
|
||||
* Gets the id of this sub-key
|
||||
*
|
||||
* @return string the id of this sub-key.
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
return $this->_id;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ getAlgorithm()
|
||||
|
||||
/**
|
||||
* Gets the algorithm used by this sub-key
|
||||
*
|
||||
* The algorithm should be one of the Crypt_GPG_SubKey::ALGORITHM_*
|
||||
* constants.
|
||||
*
|
||||
* @return integer the algorithm used by this sub-key.
|
||||
*/
|
||||
public function getAlgorithm()
|
||||
{
|
||||
return $this->_algorithm;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ getCreationDate()
|
||||
|
||||
/**
|
||||
* Gets the creation date of this sub-key
|
||||
*
|
||||
* This is a Unix timestamp.
|
||||
*
|
||||
* @return integer the creation date of this sub-key.
|
||||
*/
|
||||
public function getCreationDate()
|
||||
{
|
||||
return $this->_creationDate;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ getExpirationDate()
|
||||
|
||||
/**
|
||||
* Gets the date this sub-key expires
|
||||
*
|
||||
* This is a Unix timestamp. If this sub-key does not expire, this will be
|
||||
* zero.
|
||||
*
|
||||
* @return integer the date this sub-key expires.
|
||||
*/
|
||||
public function getExpirationDate()
|
||||
{
|
||||
return $this->_expirationDate;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ getFingerprint()
|
||||
|
||||
/**
|
||||
* Gets the fingerprint of this sub-key
|
||||
*
|
||||
* @return string the fingerprint of this sub-key.
|
||||
*/
|
||||
public function getFingerprint()
|
||||
{
|
||||
return $this->_fingerprint;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ getLength()
|
||||
|
||||
/**
|
||||
* Gets the length of this sub-key in bits
|
||||
*
|
||||
* @return integer the length of this sub-key in bits.
|
||||
*/
|
||||
public function getLength()
|
||||
{
|
||||
return $this->_length;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ canSign()
|
||||
|
||||
/**
|
||||
* Gets whether or not this sub-key can sign data
|
||||
*
|
||||
* @return boolean true if this sub-key can sign data and false if this
|
||||
* sub-key can not sign data.
|
||||
*/
|
||||
public function canSign()
|
||||
{
|
||||
return ($this->_usage & self::USAGE_SIGN) != 0;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ canEncrypt()
|
||||
|
||||
/**
|
||||
* Gets whether or not this sub-key can encrypt data
|
||||
*
|
||||
* @return boolean true if this sub-key can encrypt data and false if this
|
||||
* sub-key can not encrypt data.
|
||||
*/
|
||||
public function canEncrypt()
|
||||
{
|
||||
return ($this->_usage & self::USAGE_ENCRYPT) != 0;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ usage()
|
||||
|
||||
/**
|
||||
* Gets usage flags of this sub-key
|
||||
*
|
||||
* @return int Sum of usage flags
|
||||
*/
|
||||
public function usage()
|
||||
{
|
||||
return $this->_usage;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ hasPrivate()
|
||||
|
||||
/**
|
||||
* Gets whether or not the private key for this sub-key exists in the
|
||||
* keyring
|
||||
*
|
||||
* @return boolean true the private key for this sub-key exists in the
|
||||
* keyring and false if it does not.
|
||||
*/
|
||||
public function hasPrivate()
|
||||
{
|
||||
return $this->_hasPrivate;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ isRevoked()
|
||||
|
||||
/**
|
||||
* Gets whether or not this sub-key is revoked
|
||||
*
|
||||
* @return boolean true if this sub-key is revoked and false if it is not.
|
||||
*/
|
||||
public function isRevoked()
|
||||
{
|
||||
return $this->_isRevoked;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ setCreationDate()
|
||||
|
||||
/**
|
||||
* Sets the creation date of this sub-key
|
||||
*
|
||||
* The creation date is a Unix timestamp.
|
||||
*
|
||||
* @param integer $creationDate the creation date of this sub-key.
|
||||
*
|
||||
* @return Crypt_GPG_SubKey the current object, for fluent interface.
|
||||
*/
|
||||
public function setCreationDate($creationDate)
|
||||
{
|
||||
$this->_creationDate = intval($creationDate);
|
||||
return $this;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ setExpirationDate()
|
||||
|
||||
/**
|
||||
* Sets the expiration date of this sub-key
|
||||
*
|
||||
* The expiration date is a Unix timestamp. Specify zero if this sub-key
|
||||
* does not expire.
|
||||
*
|
||||
* @param integer $expirationDate the expiration date of this sub-key.
|
||||
*
|
||||
* @return Crypt_GPG_SubKey the current object, for fluent interface.
|
||||
*/
|
||||
public function setExpirationDate($expirationDate)
|
||||
{
|
||||
$this->_expirationDate = intval($expirationDate);
|
||||
return $this;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ setId()
|
||||
|
||||
/**
|
||||
* Sets the id of this sub-key
|
||||
*
|
||||
* @param string $id the id of this sub-key.
|
||||
*
|
||||
* @return Crypt_GPG_SubKey the current object, for fluent interface.
|
||||
*/
|
||||
public function setId($id)
|
||||
{
|
||||
$this->_id = strval($id);
|
||||
return $this;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ setAlgorithm()
|
||||
|
||||
/**
|
||||
* Sets the algorithm used by this sub-key
|
||||
*
|
||||
* @param integer $algorithm the algorithm used by this sub-key.
|
||||
*
|
||||
* @return Crypt_GPG_SubKey the current object, for fluent interface.
|
||||
*/
|
||||
public function setAlgorithm($algorithm)
|
||||
{
|
||||
$this->_algorithm = intval($algorithm);
|
||||
return $this;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ setFingerprint()
|
||||
|
||||
/**
|
||||
* Sets the fingerprint of this sub-key
|
||||
*
|
||||
* @param string $fingerprint the fingerprint of this sub-key.
|
||||
*
|
||||
* @return Crypt_GPG_SubKey the current object, for fluent interface.
|
||||
*/
|
||||
public function setFingerprint($fingerprint)
|
||||
{
|
||||
$this->_fingerprint = strval($fingerprint);
|
||||
return $this;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ setLength()
|
||||
|
||||
/**
|
||||
* Sets the length of this sub-key in bits
|
||||
*
|
||||
* @param integer $length the length of this sub-key in bits.
|
||||
*
|
||||
* @return Crypt_GPG_SubKey the current object, for fluent interface.
|
||||
*/
|
||||
public function setLength($length)
|
||||
{
|
||||
$this->_length = intval($length);
|
||||
return $this;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ setCanSign()
|
||||
|
||||
/**
|
||||
* Sets whether or not this sub-key can sign data
|
||||
*
|
||||
* @param boolean $canSign true if this sub-key can sign data and false if
|
||||
* it can not.
|
||||
*
|
||||
* @return Crypt_GPG_SubKey the current object, for fluent interface.
|
||||
*/
|
||||
public function setCanSign($canSign)
|
||||
{
|
||||
if ($canSign) {
|
||||
$this->_usage |= self::USAGE_SIGN;
|
||||
} else {
|
||||
$this->_usage &= ~self::USAGE_SIGN;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ setCanEncrypt()
|
||||
|
||||
/**
|
||||
* Sets whether or not this sub-key can encrypt data
|
||||
*
|
||||
* @param boolean $canEncrypt true if this sub-key can encrypt data and
|
||||
* false if it can not.
|
||||
*
|
||||
* @return Crypt_GPG_SubKey the current object, for fluent interface.
|
||||
*/
|
||||
public function setCanEncrypt($canEncrypt)
|
||||
{
|
||||
if ($canEncrypt) {
|
||||
$this->_usage |= self::USAGE_ENCRYPT;
|
||||
} else {
|
||||
$this->_usage &= ~self::USAGE_ENCRYPT;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ setUsage()
|
||||
|
||||
/**
|
||||
* Sets usage flags of the sub-key
|
||||
*
|
||||
* @param integer $usage Usage flags
|
||||
*
|
||||
* @return Crypt_GPG_SubKey the current object, for fluent interface.
|
||||
*/
|
||||
public function setUsage($usage)
|
||||
{
|
||||
$this->_usage = (int) $usage;
|
||||
return $this;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ setHasPrivate()
|
||||
|
||||
/**
|
||||
* Sets whether of not the private key for this sub-key exists in the
|
||||
* keyring
|
||||
*
|
||||
* @param boolean $hasPrivate true if the private key for this sub-key
|
||||
* exists in the keyring and false if it does
|
||||
* not.
|
||||
*
|
||||
* @return Crypt_GPG_SubKey the current object, for fluent interface.
|
||||
*/
|
||||
public function setHasPrivate($hasPrivate)
|
||||
{
|
||||
$this->_hasPrivate = ($hasPrivate) ? true : false;
|
||||
return $this;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ setRevoked()
|
||||
|
||||
/**
|
||||
* Sets whether or not this sub-key is revoked
|
||||
*
|
||||
* @param boolean $isRevoked whether or not this sub-key is revoked.
|
||||
*
|
||||
* @return Crypt_GPG_SubKey the current object, for fluent interface.
|
||||
*/
|
||||
public function setRevoked($isRevoked)
|
||||
{
|
||||
$this->_isRevoked = ($isRevoked) ? true : false;
|
||||
return $this;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ parse()
|
||||
|
||||
/**
|
||||
* Parses a sub-key object from a sub-key string
|
||||
*
|
||||
* See <b>doc/DETAILS</b> in the
|
||||
* {@link http://www.gnupg.org/download/ GPG distribution} for information
|
||||
* on how the sub-key string is parsed.
|
||||
*
|
||||
* @param string $string the string containing the sub-key.
|
||||
*
|
||||
* @return Crypt_GPG_SubKey the sub-key object parsed from the string.
|
||||
*/
|
||||
public static function parse($string)
|
||||
{
|
||||
$tokens = explode(':', $string);
|
||||
|
||||
$subKey = new Crypt_GPG_SubKey();
|
||||
|
||||
$subKey->setId($tokens[4]);
|
||||
$subKey->setLength($tokens[2]);
|
||||
$subKey->setAlgorithm($tokens[3]);
|
||||
$subKey->setCreationDate(self::_parseDate($tokens[5]));
|
||||
$subKey->setExpirationDate(self::_parseDate($tokens[6]));
|
||||
|
||||
if ($tokens[1] == 'r') {
|
||||
$subKey->setRevoked(true);
|
||||
}
|
||||
|
||||
$usage = 0;
|
||||
$usage_map = array(
|
||||
'a' => self::USAGE_AUTHENTICATION,
|
||||
'c' => self::USAGE_CERTIFY,
|
||||
'e' => self::USAGE_ENCRYPT,
|
||||
's' => self::USAGE_SIGN,
|
||||
);
|
||||
|
||||
foreach ($usage_map as $key => $flag) {
|
||||
if (strpos($tokens[11], $key) !== false) {
|
||||
$usage |= $flag;
|
||||
}
|
||||
}
|
||||
|
||||
$subKey->setUsage($usage);
|
||||
|
||||
return $subKey;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ _parseDate()
|
||||
|
||||
/**
|
||||
* Parses a date string as provided by GPG into a UNIX timestamp
|
||||
*
|
||||
* @param string $string the date string.
|
||||
*
|
||||
* @return integer the UNIX timestamp corresponding to the provided date
|
||||
* string.
|
||||
*/
|
||||
private static function _parseDate($string)
|
||||
{
|
||||
if ($string == '') {
|
||||
$timestamp = 0;
|
||||
} else {
|
||||
// all times are in UTC according to GPG documentation
|
||||
$timeZone = new DateTimeZone('UTC');
|
||||
|
||||
if (strpos($string, 'T') === false) {
|
||||
// interpret as UNIX timestamp
|
||||
$string = '@' . $string;
|
||||
}
|
||||
|
||||
$date = new DateTime($string, $timeZone);
|
||||
|
||||
// convert to UNIX timestamp
|
||||
$timestamp = intval($date->format('U'));
|
||||
}
|
||||
|
||||
return $timestamp;
|
||||
}
|
||||
|
||||
// }}}
|
||||
}
|
||||
|
||||
// }}}
|
||||
|
||||
?>
|
||||
+378
@@ -0,0 +1,378 @@
|
||||
<?php
|
||||
|
||||
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
|
||||
|
||||
/**
|
||||
* Contains a data class representing a GPG user id
|
||||
*
|
||||
* 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, see
|
||||
* <http://www.gnu.org/licenses/>
|
||||
*
|
||||
* @category Encryption
|
||||
* @package Crypt_GPG
|
||||
* @author Michael Gauthier <mike@silverorange.com>
|
||||
* @copyright 2008-2010 silverorange
|
||||
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
|
||||
* @link http://pear.php.net/package/Crypt_GPG
|
||||
*/
|
||||
|
||||
// {{{ class Crypt_GPG_UserId
|
||||
|
||||
/**
|
||||
* A class for GPG user id information
|
||||
*
|
||||
* This class is used to store the results of the {@link Crypt_GPG::getKeys()}
|
||||
* method. User id objects are members of a {@link Crypt_GPG_Key} object.
|
||||
*
|
||||
* @category Encryption
|
||||
* @package Crypt_GPG
|
||||
* @author Michael Gauthier <mike@silverorange.com>
|
||||
* @copyright 2008-2010 silverorange
|
||||
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
|
||||
* @link http://pear.php.net/package/Crypt_GPG
|
||||
* @see Crypt_GPG::getKeys()
|
||||
* @see Crypt_GPG_Key::getUserIds()
|
||||
*/
|
||||
class Crypt_GPG_UserId
|
||||
{
|
||||
// {{{ class properties
|
||||
|
||||
/**
|
||||
* The name field of this user id
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $_name = '';
|
||||
|
||||
/**
|
||||
* The comment field of this user id
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $_comment = '';
|
||||
|
||||
/**
|
||||
* The email field of this user id
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $_email = '';
|
||||
|
||||
/**
|
||||
* Whether or not this user id is revoked
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
private $_isRevoked = false;
|
||||
|
||||
/**
|
||||
* Whether or not this user id is valid
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
private $_isValid = true;
|
||||
|
||||
// }}}
|
||||
// {{{ __construct()
|
||||
|
||||
/**
|
||||
* Creates a new user id
|
||||
*
|
||||
* User ids can be initialized from an array of named values. Available
|
||||
* names are:
|
||||
*
|
||||
* - <kbd>string name</kbd> - the name field of the user id.
|
||||
* - <kbd>string comment</kbd> - the comment field of the user id.
|
||||
* - <kbd>string email</kbd> - the email field of the user id.
|
||||
* - <kbd>boolean valid</kbd> - whether or not the user id is valid.
|
||||
* - <kbd>boolean revoked</kbd> - whether or not the user id is revoked.
|
||||
*
|
||||
* @param Crypt_GPG_UserId|string|array $userId optional. Either an
|
||||
* existing user id object, which is copied; a user id string, which
|
||||
* is parsed; or an array of initial values.
|
||||
*/
|
||||
public function __construct($userId = null)
|
||||
{
|
||||
// parse from string
|
||||
if (is_string($userId)) {
|
||||
$userId = self::parse($userId);
|
||||
}
|
||||
|
||||
// copy from object
|
||||
if ($userId instanceof Crypt_GPG_UserId) {
|
||||
$this->_name = $userId->_name;
|
||||
$this->_comment = $userId->_comment;
|
||||
$this->_email = $userId->_email;
|
||||
$this->_isRevoked = $userId->_isRevoked;
|
||||
$this->_isValid = $userId->_isValid;
|
||||
}
|
||||
|
||||
// initialize from array
|
||||
if (is_array($userId)) {
|
||||
if (array_key_exists('name', $userId)) {
|
||||
$this->setName($userId['name']);
|
||||
}
|
||||
|
||||
if (array_key_exists('comment', $userId)) {
|
||||
$this->setComment($userId['comment']);
|
||||
}
|
||||
|
||||
if (array_key_exists('email', $userId)) {
|
||||
$this->setEmail($userId['email']);
|
||||
}
|
||||
|
||||
if (array_key_exists('revoked', $userId)) {
|
||||
$this->setRevoked($userId['revoked']);
|
||||
}
|
||||
|
||||
if (array_key_exists('valid', $userId)) {
|
||||
$this->setValid($userId['valid']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ getName()
|
||||
|
||||
/**
|
||||
* Gets the name field of this user id
|
||||
*
|
||||
* @return string the name field of this user id.
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return $this->_name;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ getComment()
|
||||
|
||||
/**
|
||||
* Gets the comments field of this user id
|
||||
*
|
||||
* @return string the comments field of this user id.
|
||||
*/
|
||||
public function getComment()
|
||||
{
|
||||
return $this->_comment;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ getEmail()
|
||||
|
||||
/**
|
||||
* Gets the email field of this user id
|
||||
*
|
||||
* @return string the email field of this user id.
|
||||
*/
|
||||
public function getEmail()
|
||||
{
|
||||
return $this->_email;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ isRevoked()
|
||||
|
||||
/**
|
||||
* Gets whether or not this user id is revoked
|
||||
*
|
||||
* @return boolean true if this user id is revoked and false if it is not.
|
||||
*/
|
||||
public function isRevoked()
|
||||
{
|
||||
return $this->_isRevoked;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ isValid()
|
||||
|
||||
/**
|
||||
* Gets whether or not this user id is valid
|
||||
*
|
||||
* @return boolean true if this user id is valid and false if it is not.
|
||||
*/
|
||||
public function isValid()
|
||||
{
|
||||
return $this->_isValid;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ __toString()
|
||||
|
||||
/**
|
||||
* Gets a string representation of this user id
|
||||
*
|
||||
* The string is formatted as:
|
||||
* <b><kbd>name (comment) <email-address></kbd></b>.
|
||||
*
|
||||
* @return string a string representation of this user id.
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
$components = array();
|
||||
|
||||
if (mb_strlen($this->_name, '8bit') > 0) {
|
||||
$components[] = $this->_name;
|
||||
}
|
||||
|
||||
if (mb_strlen($this->_comment, '8bit') > 0) {
|
||||
$components[] = '(' . $this->_comment . ')';
|
||||
}
|
||||
|
||||
if (mb_strlen($this->_email, '8bit') > 0) {
|
||||
$components[] = '<' . $this->_email. '>';
|
||||
}
|
||||
|
||||
return implode(' ', $components);
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ setName()
|
||||
|
||||
/**
|
||||
* Sets the name field of this user id
|
||||
*
|
||||
* @param string $name the name field of this user id.
|
||||
*
|
||||
* @return Crypt_GPG_UserId the current object, for fluent interface.
|
||||
*/
|
||||
public function setName($name)
|
||||
{
|
||||
$this->_name = strval($name);
|
||||
return $this;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ setComment()
|
||||
|
||||
/**
|
||||
* Sets the comment field of this user id
|
||||
*
|
||||
* @param string $comment the comment field of this user id.
|
||||
*
|
||||
* @return Crypt_GPG_UserId the current object, for fluent interface.
|
||||
*/
|
||||
public function setComment($comment)
|
||||
{
|
||||
$this->_comment = strval($comment);
|
||||
return $this;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ setEmail()
|
||||
|
||||
/**
|
||||
* Sets the email field of this user id
|
||||
*
|
||||
* @param string $email the email field of this user id.
|
||||
*
|
||||
* @return Crypt_GPG_UserId the current object, for fluent interface.
|
||||
*/
|
||||
public function setEmail($email)
|
||||
{
|
||||
$this->_email = strval($email);
|
||||
return $this;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ setRevoked()
|
||||
|
||||
/**
|
||||
* Sets whether or not this user id is revoked
|
||||
*
|
||||
* @param boolean $isRevoked whether or not this user id is revoked.
|
||||
*
|
||||
* @return Crypt_GPG_UserId the current object, for fluent interface.
|
||||
*/
|
||||
public function setRevoked($isRevoked)
|
||||
{
|
||||
$this->_isRevoked = ($isRevoked) ? true : false;
|
||||
return $this;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ setValid()
|
||||
|
||||
/**
|
||||
* Sets whether or not this user id is valid
|
||||
*
|
||||
* @param boolean $isValid whether or not this user id is valid.
|
||||
*
|
||||
* @return Crypt_GPG_UserId the current object, for fluent interface.
|
||||
*/
|
||||
public function setValid($isValid)
|
||||
{
|
||||
$this->_isValid = ($isValid) ? true : false;
|
||||
return $this;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ parse()
|
||||
|
||||
/**
|
||||
* Parses a user id object from a user id string
|
||||
*
|
||||
* A user id string is of the form:
|
||||
* <b><kbd>name (comment) <email-address></kbd></b> with the <i>comment</i>
|
||||
* and <i>email-address</i> fields being optional.
|
||||
*
|
||||
* @param string $string the user id string to parse.
|
||||
*
|
||||
* @return Crypt_GPG_UserId the user id object parsed from the string.
|
||||
*/
|
||||
public static function parse($string)
|
||||
{
|
||||
$userId = new Crypt_GPG_UserId();
|
||||
$name = '';
|
||||
$email = '';
|
||||
$comment = '';
|
||||
|
||||
// get email address from end of string if it exists
|
||||
$matches = array();
|
||||
if (preg_match('/^(.*?)<([^>]+)>$/', $string, $matches) === 1) {
|
||||
$string = trim($matches[1]);
|
||||
$email = $matches[2];
|
||||
}
|
||||
|
||||
// get comment from end of string if it exists
|
||||
$matches = array();
|
||||
if (preg_match('/^(.+?) \(([^\)]+)\)$/', $string, $matches) === 1) {
|
||||
$string = $matches[1];
|
||||
$comment = $matches[2];
|
||||
}
|
||||
|
||||
// there can be an email without a name
|
||||
if (!$email && preg_match('/^[\S]+@[\S]+$/', $string, $matches) === 1) {
|
||||
$email = $string;
|
||||
} else {
|
||||
$name = $string;
|
||||
}
|
||||
|
||||
$userId->setName($name);
|
||||
$userId->setComment($comment);
|
||||
$userId->setEmail($email);
|
||||
|
||||
return $userId;
|
||||
}
|
||||
|
||||
// }}}
|
||||
}
|
||||
|
||||
// }}}
|
||||
|
||||
?>
|
||||
+448
@@ -0,0 +1,448 @@
|
||||
<?php
|
||||
|
||||
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
|
||||
|
||||
/**
|
||||
* Crypt_GPG is a package to use GPG from PHP
|
||||
*
|
||||
* This package provides an object oriented interface to GNU Privacy
|
||||
* Guard (GPG). It requires the GPG executable to be on the system.
|
||||
*
|
||||
* Though GPG can support symmetric-key cryptography, this package is intended
|
||||
* only to facilitate public-key cryptography.
|
||||
*
|
||||
* This file contains an abstract implementation of a user of the
|
||||
* {@link Crypt_GPG_Engine} class.
|
||||
*
|
||||
* 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, see
|
||||
* <http://www.gnu.org/licenses/>
|
||||
*
|
||||
* @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://pear.php.net/manual/en/package.encryption.crypt-gpg.php
|
||||
* @link http://www.gnupg.org/
|
||||
*/
|
||||
|
||||
/**
|
||||
* GPG key class
|
||||
*/
|
||||
require_once 'Crypt/GPG/Key.php';
|
||||
|
||||
/**
|
||||
* GPG sub-key class
|
||||
*/
|
||||
require_once 'Crypt/GPG/SubKey.php';
|
||||
|
||||
/**
|
||||
* GPG user id class
|
||||
*/
|
||||
require_once 'Crypt/GPG/UserId.php';
|
||||
|
||||
/**
|
||||
* GPG process and I/O engine class
|
||||
*/
|
||||
require_once 'Crypt/GPG/Engine.php';
|
||||
|
||||
// {{{ class Crypt_GPGAbstract
|
||||
|
||||
/**
|
||||
* Base class for implementing a user of {@link Crypt_GPG_Engine}
|
||||
*
|
||||
* @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/
|
||||
*/
|
||||
abstract class Crypt_GPGAbstract
|
||||
{
|
||||
// {{{ class error constants
|
||||
|
||||
/**
|
||||
* Error code returned when there is no error.
|
||||
*/
|
||||
const ERROR_NONE = 0;
|
||||
|
||||
/**
|
||||
* Error code returned when an unknown or unhandled error occurs.
|
||||
*/
|
||||
const ERROR_UNKNOWN = 1;
|
||||
|
||||
/**
|
||||
* Error code returned when a bad passphrase is used.
|
||||
*/
|
||||
const ERROR_BAD_PASSPHRASE = 2;
|
||||
|
||||
/**
|
||||
* Error code returned when a required passphrase is missing.
|
||||
*/
|
||||
const ERROR_MISSING_PASSPHRASE = 3;
|
||||
|
||||
/**
|
||||
* Error code returned when a key that is already in the keyring is
|
||||
* imported.
|
||||
*/
|
||||
const ERROR_DUPLICATE_KEY = 4;
|
||||
|
||||
/**
|
||||
* Error code returned the required data is missing for an operation.
|
||||
*
|
||||
* This could be missing key data, missing encrypted data or missing
|
||||
* signature data.
|
||||
*/
|
||||
const ERROR_NO_DATA = 5;
|
||||
|
||||
/**
|
||||
* Error code returned when an unsigned key is used.
|
||||
*/
|
||||
const ERROR_UNSIGNED_KEY = 6;
|
||||
|
||||
/**
|
||||
* Error code returned when a key that is not self-signed is used.
|
||||
*/
|
||||
const ERROR_NOT_SELF_SIGNED = 7;
|
||||
|
||||
/**
|
||||
* Error code returned when a public or private key that is not in the
|
||||
* keyring is used.
|
||||
*/
|
||||
const ERROR_KEY_NOT_FOUND = 8;
|
||||
|
||||
/**
|
||||
* Error code returned when an attempt to delete public key having a
|
||||
* private key is made.
|
||||
*/
|
||||
const ERROR_DELETE_PRIVATE_KEY = 9;
|
||||
|
||||
/**
|
||||
* Error code returned when one or more bad signatures are detected.
|
||||
*/
|
||||
const ERROR_BAD_SIGNATURE = 10;
|
||||
|
||||
/**
|
||||
* Error code returned when there is a problem reading GnuPG data files.
|
||||
*/
|
||||
const ERROR_FILE_PERMISSIONS = 11;
|
||||
|
||||
/**
|
||||
* Error code returned when a key could not be created.
|
||||
*/
|
||||
const ERROR_KEY_NOT_CREATED = 12;
|
||||
|
||||
/**
|
||||
* Error code returned when bad key parameters are used during key
|
||||
* generation.
|
||||
*/
|
||||
const ERROR_BAD_KEY_PARAMS = 13;
|
||||
|
||||
// }}}
|
||||
// {{{ other class constants
|
||||
|
||||
/**
|
||||
* URI at which package bugs may be reported.
|
||||
*/
|
||||
const BUG_URI = 'http://pear.php.net/bugs/report.php?package=Crypt_GPG';
|
||||
|
||||
// }}}
|
||||
// {{{ protected class properties
|
||||
|
||||
/**
|
||||
* Engine used to control the GPG subprocess
|
||||
*
|
||||
* @var Crypt_GPG_Engine
|
||||
*
|
||||
* @see Crypt_GPGAbstract::setEngine()
|
||||
*/
|
||||
protected $engine = null;
|
||||
|
||||
// }}}
|
||||
// {{{ __construct()
|
||||
|
||||
/**
|
||||
* Creates a new GPG object
|
||||
*
|
||||
* 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())
|
||||
{
|
||||
$this->setEngine(new Crypt_GPG_Engine($options));
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ setEngine()
|
||||
|
||||
/**
|
||||
* Sets the I/O engine to use for GnuPG operations
|
||||
*
|
||||
* Normally this method does not need to be used. It provides a means for
|
||||
* dependency injection.
|
||||
*
|
||||
* @param Crypt_GPG_Engine $engine the engine to use.
|
||||
*
|
||||
* @return Crypt_GPGAbstract the current object, for fluent interface.
|
||||
*/
|
||||
public function setEngine(Crypt_GPG_Engine $engine)
|
||||
{
|
||||
$this->engine = $engine;
|
||||
return $this;
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ getVersion()
|
||||
|
||||
/**
|
||||
* Returns version of the engine (GnuPG) used for operation.
|
||||
*
|
||||
* @return string GnuPG version.
|
||||
*
|
||||
* @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 getVersion()
|
||||
{
|
||||
return $this->engine->getVersion();
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ _getKeys()
|
||||
|
||||
/**
|
||||
* Gets the available keys in the keyring
|
||||
*
|
||||
* Calls GPG with the <kbd>--list-keys</kbd> command and grabs keys. See
|
||||
* the first section of <b>doc/DETAILS</b> in the
|
||||
* {@link http://www.gnupg.org/download/ GPG package} for a detailed
|
||||
* description of how the GPG command output is parsed.
|
||||
*
|
||||
* @param string $keyId optional. Only keys with that match the specified
|
||||
* pattern are returned. The pattern may be part of
|
||||
* a user id, a key id or a key fingerprint. If not
|
||||
* specified, all keys are returned.
|
||||
*
|
||||
* @return array an array of {@link Crypt_GPG_Key} objects. If no keys
|
||||
* match the specified <kbd>$keyId</kbd> an empty array is
|
||||
* returned.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @see Crypt_GPG_Key
|
||||
*/
|
||||
protected function _getKeys($keyId = '')
|
||||
{
|
||||
// get private key fingerprints
|
||||
if ($keyId == '') {
|
||||
$operation = '--list-secret-keys';
|
||||
} else {
|
||||
$operation = '--utf8-strings --list-secret-keys ' . escapeshellarg($keyId);
|
||||
}
|
||||
|
||||
// According to The file 'doc/DETAILS' in the GnuPG distribution, using
|
||||
// double '--with-fingerprint' also prints the fingerprint for subkeys.
|
||||
$arguments = array(
|
||||
'--with-colons',
|
||||
'--with-fingerprint',
|
||||
'--with-fingerprint',
|
||||
'--fixed-list-mode'
|
||||
);
|
||||
|
||||
$output = '';
|
||||
|
||||
$this->engine->reset();
|
||||
$this->engine->setOutput($output);
|
||||
$this->engine->setOperation($operation, $arguments);
|
||||
$this->engine->run();
|
||||
|
||||
$privateKeyFingerprints = array();
|
||||
|
||||
foreach (explode(PHP_EOL, $output) as $line) {
|
||||
$lineExp = explode(':', $line);
|
||||
if ($lineExp[0] == 'fpr') {
|
||||
$privateKeyFingerprints[] = $lineExp[9];
|
||||
}
|
||||
}
|
||||
|
||||
// get public keys
|
||||
if ($keyId == '') {
|
||||
$operation = '--list-public-keys';
|
||||
} else {
|
||||
$operation = '--utf8-strings --list-public-keys ' . escapeshellarg($keyId);
|
||||
}
|
||||
|
||||
$output = '';
|
||||
|
||||
$this->engine->reset();
|
||||
$this->engine->setOutput($output);
|
||||
$this->engine->setOperation($operation, $arguments);
|
||||
$this->engine->run();
|
||||
|
||||
$keys = array();
|
||||
$key = null; // current key
|
||||
$subKey = null; // current sub-key
|
||||
|
||||
foreach (explode(PHP_EOL, $output) as $line) {
|
||||
$lineExp = explode(':', $line);
|
||||
|
||||
if ($lineExp[0] == 'pub') {
|
||||
|
||||
// new primary key means last key should be added to the array
|
||||
if ($key !== null) {
|
||||
$keys[] = $key;
|
||||
}
|
||||
|
||||
$key = new Crypt_GPG_Key();
|
||||
|
||||
$subKey = Crypt_GPG_SubKey::parse($line);
|
||||
$key->addSubKey($subKey);
|
||||
|
||||
} elseif ($lineExp[0] == 'sub') {
|
||||
|
||||
$subKey = Crypt_GPG_SubKey::parse($line);
|
||||
$key->addSubKey($subKey);
|
||||
|
||||
} elseif ($lineExp[0] == 'fpr') {
|
||||
|
||||
$fingerprint = $lineExp[9];
|
||||
|
||||
// set current sub-key fingerprint
|
||||
$subKey->setFingerprint($fingerprint);
|
||||
|
||||
// if private key exists, set has private to true
|
||||
if (in_array($fingerprint, $privateKeyFingerprints)) {
|
||||
$subKey->setHasPrivate(true);
|
||||
}
|
||||
|
||||
} elseif ($lineExp[0] == 'uid') {
|
||||
|
||||
$string = stripcslashes($lineExp[9]); // as per documentation
|
||||
$userId = new Crypt_GPG_UserId($string);
|
||||
|
||||
if ($lineExp[1] == 'r') {
|
||||
$userId->setRevoked(true);
|
||||
}
|
||||
|
||||
$key->addUserId($userId);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// add last key
|
||||
if ($key !== null) {
|
||||
$keys[] = $key;
|
||||
}
|
||||
|
||||
return $keys;
|
||||
}
|
||||
|
||||
// }}}
|
||||
}
|
||||
|
||||
// }}}
|
||||
|
||||
?>
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
#! /usr/bin/env php
|
||||
<?php
|
||||
|
||||
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
|
||||
|
||||
// Check if we're running directly from git repo or if we're running
|
||||
// from a PEAR or Composer packaged version.
|
||||
$ds = DIRECTORY_SEPARATOR;
|
||||
$root = __DIR__ . $ds . '..' ;
|
||||
$paths = array(
|
||||
'/www/roundcube/releases/roundcubemail-1.3-beta/vendor/pear-pear.php.net/Crypt_GPG', // PEAR or Composer
|
||||
$root, // Git (or Composer with wrong /www/roundcube/releases/roundcubemail-1.3-beta/vendor/pear-pear.php.net/Crypt_GPG)
|
||||
$root . $ds . '..' . $ds . 'Console_CommandLine', // Composer
|
||||
$root . $ds . '..' . $ds . 'console_commandline', // Composer
|
||||
// and composer-installed PEAR_Exception for Console_CommandLine (#21074)
|
||||
$root . $ds . '..' . $ds . '..' . $ds . 'pear' . $ds . 'pear_exception',
|
||||
);
|
||||
|
||||
foreach ($paths as $idx => $path) {
|
||||
if (!is_dir($path)) {
|
||||
unset($paths[$idx]);
|
||||
}
|
||||
}
|
||||
|
||||
// We depend on Console_CommandLine, so we append also the default include path
|
||||
set_include_path(implode(PATH_SEPARATOR, $paths) . PATH_SEPARATOR . get_include_path());
|
||||
|
||||
require_once 'Crypt/GPG/PinEntry.php';
|
||||
|
||||
$pinentry = new Crypt_GPG_PinEntry();
|
||||
$pinentry->__invoke();
|
||||
|
||||
?>
|
||||
Generated
Vendored
+18
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
|
||||
<command>
|
||||
<description>Utility that emulates GnuPG 1.x passphrase handling over pipe-based IPC for GnuPG 2.x.</description>
|
||||
<version>1.6.0b3</version>
|
||||
<option name="log">
|
||||
<short_name>-l</short_name>
|
||||
<long_name>--log</long_name>
|
||||
<description>Optional location to log pinentry activity.</description>
|
||||
<action>StoreString</action>
|
||||
</option>
|
||||
<option name="verbose">
|
||||
<short_name>-v</short_name>
|
||||
<long_name>--verbose</long_name>
|
||||
<description>Sets verbosity level. Use multiples for more detail (e.g. "-vv").</description>
|
||||
<action>Counter</action>
|
||||
<default>0</default>
|
||||
</option>
|
||||
</command>
|
||||
+1626
File diff suppressed because it is too large
Load Diff
+1249
File diff suppressed because it is too large
Load Diff
+3402
File diff suppressed because it is too large
Load Diff
+4
@@ -0,0 +1,4 @@
|
||||
<?php
|
||||
class Net_IDNA2_Exception extends Exception
|
||||
{
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
<?php
|
||||
require_once 'Net/IDNA2/Exception.php';
|
||||
|
||||
class Net_IDNA2_Exception_Nameprep extends Net_IDNA2_Exception
|
||||
{
|
||||
}
|
||||
+1802
File diff suppressed because it is too large
Load Diff
+1096
File diff suppressed because it is too large
Load Diff
+675
@@ -0,0 +1,675 @@
|
||||
<?php
|
||||
/* vim: set expandtab tabstop=4 shiftwidth=4: */
|
||||
/**
|
||||
* File containing the Net_LDAP2_Filter interface class.
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* @category Net
|
||||
* @package Net_LDAP2
|
||||
* @author Benedikt Hallinger <beni@php.net>
|
||||
* @copyright 2009 Benedikt Hallinger
|
||||
* @license http://www.gnu.org/licenses/lgpl-3.0.txt LGPLv3
|
||||
* @version SVN: $Id$
|
||||
* @link http://pear.php.net/package/Net_LDAP2/
|
||||
*/
|
||||
|
||||
/**
|
||||
* Includes
|
||||
*/
|
||||
require_once 'PEAR.php';
|
||||
require_once 'Net/LDAP2/Util.php';
|
||||
require_once 'Net/LDAP2/Entry.php';
|
||||
|
||||
/**
|
||||
* Object representation of a part of a LDAP filter.
|
||||
*
|
||||
* This Class is not completely compatible to the PERL interface!
|
||||
*
|
||||
* The purpose of this class is, that users can easily build LDAP filters
|
||||
* without having to worry about right escaping etc.
|
||||
* A Filter is built using several independent filter objects
|
||||
* which are combined afterwards. This object works in two
|
||||
* modes, depending how the object is created.
|
||||
* If the object is created using the {@link create()} method, then this is a leaf-object.
|
||||
* If the object is created using the {@link combine()} method, then this is a container object.
|
||||
*
|
||||
* LDAP filters are defined in RFC-2254 and can be found under
|
||||
* {@link http://www.ietf.org/rfc/rfc2254.txt}
|
||||
*
|
||||
* Here a quick copy&paste example:
|
||||
* <code>
|
||||
* $filter0 = Net_LDAP2_Filter::create('stars', 'equals', '***');
|
||||
* $filter_not0 = Net_LDAP2_Filter::combine('not', $filter0);
|
||||
*
|
||||
* $filter1 = Net_LDAP2_Filter::create('gn', 'begins', 'bar');
|
||||
* $filter2 = Net_LDAP2_Filter::create('gn', 'ends', 'baz');
|
||||
* $filter_comp = Net_LDAP2_Filter::combine('or',array($filter_not0, $filter1, $filter2));
|
||||
*
|
||||
* echo $filter_comp->asString();
|
||||
* // This will output: (|(!(stars=\0x5c0x2a\0x5c0x2a\0x5c0x2a))(gn=bar*)(gn=*baz))
|
||||
* // The stars in $filter0 are treaten as real stars unless you disable escaping.
|
||||
* </code>
|
||||
*
|
||||
* @category Net
|
||||
* @package Net_LDAP2
|
||||
* @author Benedikt Hallinger <beni@php.net>
|
||||
* @license http://www.gnu.org/copyleft/lesser.html LGPL
|
||||
* @link http://pear.php.net/package/Net_LDAP2/
|
||||
*/
|
||||
class Net_LDAP2_Filter extends PEAR
|
||||
{
|
||||
/**
|
||||
* Storage for combination of filters
|
||||
*
|
||||
* This variable holds a array of filter objects
|
||||
* that should be combined by this filter object.
|
||||
*
|
||||
* @access protected
|
||||
* @var array
|
||||
*/
|
||||
protected $_subfilters = array();
|
||||
|
||||
/**
|
||||
* Match of this filter
|
||||
*
|
||||
* If this is a leaf filter, then a matching rule is stored,
|
||||
* if it is a container, then it is a logical operator
|
||||
*
|
||||
* @access protected
|
||||
* @var string
|
||||
*/
|
||||
protected $_match;
|
||||
|
||||
/**
|
||||
* Single filter
|
||||
*
|
||||
* If we operate in leaf filter mode,
|
||||
* then the constructing method stores
|
||||
* the filter representation here
|
||||
*
|
||||
* @acces private
|
||||
* @var string
|
||||
*/
|
||||
protected $_filter;
|
||||
|
||||
/**
|
||||
* Create a new Net_LDAP2_Filter object and parse $filter.
|
||||
*
|
||||
* This is for PERL Net::LDAP interface.
|
||||
* Construction of Net_LDAP2_Filter objects should happen through either
|
||||
* {@link create()} or {@link combine()} which give you more control.
|
||||
* However, you may use the perl iterface if you already have generated filters.
|
||||
*
|
||||
* @param string $filter LDAP filter string
|
||||
*
|
||||
* @see parse()
|
||||
*/
|
||||
public function __construct($filter = false)
|
||||
{
|
||||
// The optional parameter must remain here, because otherwise create() crashes
|
||||
if (false !== $filter) {
|
||||
$filter_o = self::parse($filter);
|
||||
if (PEAR::isError($filter_o)) {
|
||||
$this->_filter = $filter_o; // assign error, so asString() can report it
|
||||
} else {
|
||||
$this->_filter = $filter_o->asString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor of a new part of a LDAP filter.
|
||||
*
|
||||
* The following matching rules exists:
|
||||
* - equals: One of the attributes values is exactly $value
|
||||
* Please note that case sensitiviness is depends on the
|
||||
* attributes syntax configured in the server.
|
||||
* - begins: One of the attributes values must begin with $value
|
||||
* - ends: One of the attributes values must end with $value
|
||||
* - contains: One of the attributes values must contain $value
|
||||
* - present | any: The attribute can contain any value but must be existent
|
||||
* - greater: The attributes value is greater than $value
|
||||
* - less: The attributes value is less than $value
|
||||
* - greaterOrEqual: The attributes value is greater or equal than $value
|
||||
* - lessOrEqual: The attributes value is less or equal than $value
|
||||
* - approx: One of the attributes values is similar to $value
|
||||
*
|
||||
* Negation ("not") can be done by prepending the above operators with the
|
||||
* "not" or "!" keyword, see example below.
|
||||
*
|
||||
* If $escape is set to true (default) then $value will be escaped
|
||||
* properly. If it is set to false then $value will be treaten as raw filter value string.
|
||||
* You should escape yourself using {@link Net_LDAP2_Util::escape_filter_value()}!
|
||||
*
|
||||
* Examples:
|
||||
* <code>
|
||||
* // This will find entries that contain an attribute "sn" that ends with "foobar":
|
||||
* $filter = Net_LDAP2_Filter::create('sn', 'ends', 'foobar');
|
||||
*
|
||||
* // This will find entries that contain an attribute "sn" that has any value set:
|
||||
* $filter = Net_LDAP2_Filter::create('sn', 'any');
|
||||
*
|
||||
* // This will build a negated equals filter:
|
||||
* $filter = Net_LDAP2_Filter::create('sn', 'not equals', 'foobar');
|
||||
* </code>
|
||||
*
|
||||
* @param string $attr_name Name of the attribute the filter should apply to
|
||||
* @param string $match Matching rule (equals, begins, ends, contains, greater, less, greaterOrEqual, lessOrEqual, approx, any)
|
||||
* @param string $value (optional) if given, then this is used as a filter
|
||||
* @param boolean $escape Should $value be escaped? (default: yes, see {@link Net_LDAP2_Util::escape_filter_value()} for detailed information)
|
||||
*
|
||||
* @return Net_LDAP2_Filter|Net_LDAP2_Error
|
||||
*/
|
||||
public static function create($attr_name, $match, $value = '', $escape = true)
|
||||
{
|
||||
$leaf_filter = new Net_LDAP2_Filter();
|
||||
if ($escape) {
|
||||
$array = Net_LDAP2_Util::escape_filter_value(array($value));
|
||||
$value = $array[0];
|
||||
}
|
||||
|
||||
$match = strtolower($match);
|
||||
|
||||
// detect negation
|
||||
$neg_matches = array();
|
||||
$negate_filter = false;
|
||||
if (preg_match('/^(?:not|!)[\s_-](.+)/', $match, $neg_matches)) {
|
||||
$negate_filter = true;
|
||||
$match = $neg_matches[1];
|
||||
}
|
||||
|
||||
// build basic filter
|
||||
switch ($match) {
|
||||
case 'equals':
|
||||
case '=':
|
||||
case '==':
|
||||
$leaf_filter->_filter = '(' . $attr_name . '=' . $value . ')';
|
||||
break;
|
||||
case 'begins':
|
||||
$leaf_filter->_filter = '(' . $attr_name . '=' . $value . '*)';
|
||||
break;
|
||||
case 'ends':
|
||||
$leaf_filter->_filter = '(' . $attr_name . '=*' . $value . ')';
|
||||
break;
|
||||
case 'contains':
|
||||
$leaf_filter->_filter = '(' . $attr_name . '=*' . $value . '*)';
|
||||
break;
|
||||
case 'greater':
|
||||
case '>':
|
||||
$leaf_filter->_filter = '(' . $attr_name . '>' . $value . ')';
|
||||
break;
|
||||
case 'less':
|
||||
case '<':
|
||||
$leaf_filter->_filter = '(' . $attr_name . '<' . $value . ')';
|
||||
break;
|
||||
case 'greaterorequal':
|
||||
case '>=':
|
||||
$leaf_filter->_filter = '(' . $attr_name . '>=' . $value . ')';
|
||||
break;
|
||||
case 'lessorequal':
|
||||
case '<=':
|
||||
$leaf_filter->_filter = '(' . $attr_name . '<=' . $value . ')';
|
||||
break;
|
||||
case 'approx':
|
||||
case '~=':
|
||||
$leaf_filter->_filter = '(' . $attr_name . '~=' . $value . ')';
|
||||
break;
|
||||
case 'any':
|
||||
case 'present': // alias that may improve user code readability
|
||||
$leaf_filter->_filter = '(' . $attr_name . '=*)';
|
||||
break;
|
||||
default:
|
||||
return PEAR::raiseError('Net_LDAP2_Filter create error: matching rule "' . $match . '" not known!');
|
||||
}
|
||||
|
||||
// negate if requested
|
||||
if ($negate_filter) {
|
||||
$leaf_filter = Net_LDAP2_Filter::combine('!', $leaf_filter);
|
||||
}
|
||||
|
||||
return $leaf_filter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Combine two or more filter objects using a logical operator
|
||||
*
|
||||
* This static method combines two or more filter objects and returns one single
|
||||
* filter object that contains all the others.
|
||||
* Call this method statically: $filter = Net_LDAP2_Filter::combine('or', array($filter1, $filter2))
|
||||
* If the array contains filter strings instead of filter objects, we will try to parse them.
|
||||
*
|
||||
* @param string $log_op The locical operator. May be "and", "or", "not" or the subsequent logical equivalents "&", "|", "!"
|
||||
* @param array|Net_LDAP2_Filter $filters array with Net_LDAP2_Filter objects
|
||||
*
|
||||
* @return Net_LDAP2_Filter|Net_LDAP2_Error
|
||||
* @static
|
||||
*/
|
||||
public static function &combine($log_op, $filters)
|
||||
{
|
||||
if (PEAR::isError($filters)) {
|
||||
return $filters;
|
||||
}
|
||||
|
||||
// substitude named operators to logical operators
|
||||
if ($log_op == 'and') $log_op = '&';
|
||||
if ($log_op == 'or') $log_op = '|';
|
||||
if ($log_op == 'not') $log_op = '!';
|
||||
|
||||
// tests for sane operation
|
||||
if ($log_op == '!') {
|
||||
// Not-combination, here we only accept one filter object or filter string
|
||||
if ($filters instanceof Net_LDAP2_Filter) {
|
||||
$filters = array($filters); // force array
|
||||
} elseif (is_string($filters)) {
|
||||
$filter_o = self::parse($filters);
|
||||
if (PEAR::isError($filter_o)) {
|
||||
$err = PEAR::raiseError('Net_LDAP2_Filter combine error: '.$filter_o->getMessage());
|
||||
return $err;
|
||||
} else {
|
||||
$filters = array($filter_o);
|
||||
}
|
||||
} elseif (is_array($filters)) {
|
||||
if (count($filters) != 1) {
|
||||
$err = PEAR::raiseError('Net_LDAP2_Filter combine error: operator is "not" but $filter is an array!');
|
||||
return $err;
|
||||
} elseif (!($filters[0] instanceof Net_LDAP2_Filter)) {
|
||||
$err = PEAR::raiseError('Net_LDAP2_Filter combine error: operator is "not" but $filter is not a valid Net_LDAP2_Filter nor a filter string!');
|
||||
return $err;
|
||||
}
|
||||
} else {
|
||||
$err = PEAR::raiseError('Net_LDAP2_Filter combine error: operator is "not" but $filter is not a valid Net_LDAP2_Filter nor a filter string!');
|
||||
return $err;
|
||||
}
|
||||
} elseif ($log_op == '&' || $log_op == '|') {
|
||||
if (!is_array($filters) || count($filters) < 2) {
|
||||
$err = PEAR::raiseError('Net_LDAP2_Filter combine error: parameter $filters is not an array or contains less than two Net_LDAP2_Filter objects!');
|
||||
return $err;
|
||||
}
|
||||
} else {
|
||||
$err = PEAR::raiseError('Net_LDAP2_Filter combine error: logical operator is not known!');
|
||||
return $err;
|
||||
}
|
||||
|
||||
$combined_filter = new Net_LDAP2_Filter();
|
||||
foreach ($filters as $key => $testfilter) { // check for errors
|
||||
if (PEAR::isError($testfilter)) {
|
||||
return $testfilter;
|
||||
} elseif (is_string($testfilter)) {
|
||||
// string found, try to parse into an filter object
|
||||
$filter_o = self::parse($testfilter);
|
||||
if (PEAR::isError($filter_o)) {
|
||||
return $filter_o;
|
||||
} else {
|
||||
$filters[$key] = $filter_o;
|
||||
}
|
||||
} elseif (!$testfilter instanceof Net_LDAP2_Filter) {
|
||||
$err = PEAR::raiseError('Net_LDAP2_Filter combine error: invalid object passed in array $filters!');
|
||||
return $err;
|
||||
}
|
||||
}
|
||||
|
||||
$combined_filter->_subfilters = $filters;
|
||||
$combined_filter->_match = $log_op;
|
||||
return $combined_filter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse FILTER into a Net_LDAP2_Filter object
|
||||
*
|
||||
* This parses an filter string into Net_LDAP2_Filter objects.
|
||||
*
|
||||
* @param string $FILTER The filter string
|
||||
*
|
||||
* @access static
|
||||
* @return Net_LDAP2_Filter|Net_LDAP2_Error
|
||||
* @todo Leaf-mode: Do we need to escape at all? what about *-chars?check for the need of encoding values, tackle problems (see code comments)
|
||||
*/
|
||||
public static function parse($FILTER)
|
||||
{
|
||||
if (preg_match('/^\((.+?)\)$/', $FILTER, $matches)) {
|
||||
// Check for right bracket syntax: count of unescaped opening
|
||||
// brackets must match count of unescaped closing brackets.
|
||||
// At this stage we may have:
|
||||
// 1. one filter component with already removed outer brackets
|
||||
// 2. one or more subfilter components
|
||||
$c_openbracks = preg_match_all('/(?<!\\\\)\(/' , $matches[1], $notrelevant);
|
||||
$c_closebracks = preg_match_all('/(?<!\\\\)\)/' , $matches[1], $notrelevant);
|
||||
if ($c_openbracks != $c_closebracks) {
|
||||
return PEAR::raiseError("Filter parsing error: invalid filter syntax - opening brackets do not match close brackets!");
|
||||
}
|
||||
|
||||
if (in_array(substr($matches[1], 0, 1), array('!', '|', '&'))) {
|
||||
// Subfilter processing: pass subfilters to parse() and combine
|
||||
// the objects using the logical operator detected
|
||||
// we have now something like "&(...)(...)(...)" but at least one part ("!(...)").
|
||||
// Each subfilter could be an arbitary complex subfilter.
|
||||
|
||||
// extract logical operator and filter arguments
|
||||
$log_op = substr($matches[1], 0, 1);
|
||||
$remaining_component = substr($matches[1], 1);
|
||||
|
||||
// split $remaining_component into individual subfilters
|
||||
// we cannot use split() for this, because we do not know the
|
||||
// complexiness of the subfilter. Thus, we look trough the filter
|
||||
// string and just recognize ending filters at the first level.
|
||||
// We record the index number of the char and use that information
|
||||
// later to split the string.
|
||||
$sub_index_pos = array();
|
||||
$prev_char = ''; // previous character looked at
|
||||
$level = 0; // denotes the current bracket level we are,
|
||||
// >1 is too deep, 1 is ok, 0 is outside any
|
||||
// subcomponent
|
||||
for ($curpos = 0; $curpos < strlen($remaining_component); $curpos++) {
|
||||
$cur_char = substr($remaining_component, $curpos, 1);
|
||||
|
||||
// rise/lower bracket level
|
||||
if ($cur_char == '(' && $prev_char != '\\') {
|
||||
$level++;
|
||||
} elseif ($cur_char == ')' && $prev_char != '\\') {
|
||||
$level--;
|
||||
}
|
||||
|
||||
if ($cur_char == '(' && $prev_char == ')' && $level == 1) {
|
||||
array_push($sub_index_pos, $curpos); // mark the position for splitting
|
||||
}
|
||||
$prev_char = $cur_char;
|
||||
}
|
||||
|
||||
// now perform the splits. To get also the last part, we
|
||||
// need to add the "END" index to the split array
|
||||
array_push($sub_index_pos, strlen($remaining_component));
|
||||
$subfilters = array();
|
||||
$oldpos = 0;
|
||||
foreach ($sub_index_pos as $s_pos) {
|
||||
$str_part = substr($remaining_component, $oldpos, $s_pos - $oldpos);
|
||||
array_push($subfilters, $str_part);
|
||||
$oldpos = $s_pos;
|
||||
}
|
||||
|
||||
// some error checking...
|
||||
if (count($subfilters) == 1) {
|
||||
// only one subfilter found
|
||||
} elseif (count($subfilters) > 1) {
|
||||
// several subfilters found
|
||||
if ($log_op == "!") {
|
||||
return PEAR::raiseError("Filter parsing error: invalid filter syntax - NOT operator detected but several arguments given!");
|
||||
}
|
||||
} else {
|
||||
// this should not happen unless the user specified a wrong filter
|
||||
return PEAR::raiseError("Filter parsing error: invalid filter syntax - got operator '$log_op' but no argument!");
|
||||
}
|
||||
|
||||
// Now parse the subfilters into objects and combine them using the operator
|
||||
$subfilters_o = array();
|
||||
foreach ($subfilters as $s_s) {
|
||||
$o = self::parse($s_s);
|
||||
if (PEAR::isError($o)) {
|
||||
return $o;
|
||||
} else {
|
||||
array_push($subfilters_o, self::parse($s_s));
|
||||
}
|
||||
}
|
||||
|
||||
$filter_o = self::combine($log_op, $subfilters_o);
|
||||
return $filter_o;
|
||||
|
||||
} else {
|
||||
// This is one leaf filter component, do some syntax checks, then escape and build filter_o
|
||||
// $matches[1] should be now something like "foo=bar"
|
||||
|
||||
// detect multiple leaf components
|
||||
// [TODO] Maybe this will make problems with filters containing brackets inside the value
|
||||
if (stristr($matches[1], ')(')) {
|
||||
return PEAR::raiseError("Filter parsing error: invalid filter syntax - multiple leaf components detected!");
|
||||
} else {
|
||||
$filter_parts = Net_LDAP2_Util::split_attribute_string($matches[1], true, true);
|
||||
if (count($filter_parts) != 3) {
|
||||
return PEAR::raiseError("Filter parsing error: invalid filter syntax - unknown matching rule used");
|
||||
} else {
|
||||
$filter_o = new Net_LDAP2_Filter();
|
||||
// [TODO]: Do we need to escape at all? what about *-chars user provide and that should remain special?
|
||||
// I think, those prevent escaping! We need to check against PERL Net::LDAP!
|
||||
// $value_arr = Net_LDAP2_Util::escape_filter_value(array($filter_parts[2]));
|
||||
// $value = $value_arr[0];
|
||||
$value = $filter_parts[2];
|
||||
$filter_o->_filter = '('.$filter_parts[0].$filter_parts[1].$value.')';
|
||||
return $filter_o;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// ERROR: Filter components must be enclosed in round brackets
|
||||
return PEAR::raiseError("Filter parsing error: invalid filter syntax - filter components must be enclosed in round brackets");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the string representation of this filter
|
||||
*
|
||||
* This method runs through all filter objects and creates
|
||||
* the string representation of the filter. If this
|
||||
* filter object is a leaf filter, then it will return
|
||||
* the string representation of this filter.
|
||||
*
|
||||
* @return string|Net_LDAP2_Error
|
||||
*/
|
||||
public function asString()
|
||||
{
|
||||
if ($this->isLeaf()) {
|
||||
$return = $this->_filter;
|
||||
} else {
|
||||
$return = '';
|
||||
foreach ($this->_subfilters as $filter) {
|
||||
$return = $return.$filter->asString();
|
||||
}
|
||||
$return = '(' . $this->_match . $return . ')';
|
||||
}
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias for perl interface as_string()
|
||||
*
|
||||
* @see asString()
|
||||
* @return string|Net_LDAP2_Error
|
||||
*/
|
||||
public function as_string()
|
||||
{
|
||||
return $this->asString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Print the text representation of the filter to FH, or the currently selected output handle if FH is not given
|
||||
*
|
||||
* This method is only for compatibility to the perl interface.
|
||||
* However, the original method was called "print" but due to PHP language restrictions,
|
||||
* we can't have a print() method.
|
||||
*
|
||||
* @param resource $FH (optional) A filehandle resource
|
||||
*
|
||||
* @return true|Net_LDAP2_Error
|
||||
*/
|
||||
public function printMe($FH = false)
|
||||
{
|
||||
if (!is_resource($FH)) {
|
||||
if (PEAR::isError($FH)) {
|
||||
return $FH;
|
||||
}
|
||||
$filter_str = $this->asString();
|
||||
if (PEAR::isError($filter_str)) {
|
||||
return $filter_str;
|
||||
} else {
|
||||
print($filter_str);
|
||||
}
|
||||
} else {
|
||||
$filter_str = $this->asString();
|
||||
if (PEAR::isError($filter_str)) {
|
||||
return $filter_str;
|
||||
} else {
|
||||
$res = @fwrite($FH, $this->asString());
|
||||
if ($res == false) {
|
||||
return PEAR::raiseError("Unable to write filter string to filehandle \$FH!");
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* This can be used to escape a string to provide a valid LDAP-Filter.
|
||||
*
|
||||
* LDAP will only recognise certain characters as the
|
||||
* character istself if they are properly escaped. This is
|
||||
* what this method does.
|
||||
* The method can be called statically, so you can use it outside
|
||||
* for your own purposes (eg for escaping only parts of strings)
|
||||
*
|
||||
* In fact, this is just a shorthand to {@link Net_LDAP2_Util::escape_filter_value()}.
|
||||
* For upward compatibiliy reasons you are strongly encouraged to use the escape
|
||||
* methods provided by the Net_LDAP2_Util class.
|
||||
*
|
||||
* @param string $value Any string who should be escaped
|
||||
*
|
||||
* @static
|
||||
* @return string The string $string, but escaped
|
||||
* @deprecated Do not use this method anymore, instead use Net_LDAP2_Util::escape_filter_value() directly
|
||||
*/
|
||||
public static function escape($value)
|
||||
{
|
||||
$return = Net_LDAP2_Util::escape_filter_value(array($value));
|
||||
return $return[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this a container or a leaf filter object?
|
||||
*
|
||||
* @access protected
|
||||
* @return boolean
|
||||
*/
|
||||
protected function isLeaf()
|
||||
{
|
||||
if (count($this->_subfilters) > 0) {
|
||||
return false; // Container!
|
||||
} else {
|
||||
return true; // Leaf!
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter entries using this filter or see if a filter matches
|
||||
*
|
||||
* @todo Currently slow and naive implementation with preg_match, could be optimized (esp. begins, ends filters etc)
|
||||
* @todo Currently only "="-based matches (equals, begins, ends, contains, any) implemented; Implement all the stuff!
|
||||
* @todo Implement expert code with schema checks in case $entry is connected to a directory
|
||||
* @param array|Net_LDAP2_Entry The entry (or array with entries) to check
|
||||
* @param array If given, the array will be appended with entries who matched the filter. Return value is true if any entry matched.
|
||||
* @return int|Net_LDAP2_Error Returns the number of matched entries or error
|
||||
*/
|
||||
function matches(&$entries, &$results=array()) {
|
||||
$numOfMatches = 0;
|
||||
|
||||
if (!is_array($entries)) {
|
||||
$all_entries = array(&$entries);
|
||||
} else {
|
||||
$all_entries = &$entries;
|
||||
}
|
||||
|
||||
foreach ($all_entries as $entry) {
|
||||
// look at the current entry and see if filter matches
|
||||
|
||||
$entry_matched = false;
|
||||
// if this is not a single component, do calculate all subfilters,
|
||||
// then assert the partial results with the given combination modifier
|
||||
if (!$this->isLeaf()) {
|
||||
|
||||
// get partial results from subfilters
|
||||
$partial_results = array();
|
||||
foreach ($this->_subfilters as $filter) {
|
||||
$partial_results[] = $filter->matches($entry);
|
||||
}
|
||||
|
||||
// evaluate partial results using this filters combination rule
|
||||
switch ($this->_match) {
|
||||
case '!':
|
||||
// result is the neagtive result of the assertion
|
||||
$entry_matched = !$partial_results[0];
|
||||
break;
|
||||
|
||||
case '&':
|
||||
// all partial results have to be boolean-true
|
||||
$entry_matched = !in_array(false, $partial_results);
|
||||
break;
|
||||
|
||||
case '|':
|
||||
// at least one partial result has to be true
|
||||
$entry_matched = in_array(true, $partial_results);
|
||||
break;
|
||||
}
|
||||
|
||||
} else {
|
||||
// Leaf filter: assert given entry
|
||||
// [TODO]: Could be optimized to avoid preg_match especially with "ends", "begins" etc
|
||||
|
||||
// Translate the LDAP-match to some preg_match expression and evaluate it
|
||||
list($attribute, $match, $assertValue) = $this->getComponents();
|
||||
switch ($match) {
|
||||
case '=':
|
||||
$regexp = '/^'.str_replace('*', '.*', $assertValue).'$/i'; // not case sensitive unless specified by schema
|
||||
$entry_matched = $entry->pregMatch($regexp, $attribute);
|
||||
break;
|
||||
|
||||
// -------------------------------------
|
||||
// [TODO]: implement <, >, <=, >= and =~
|
||||
// -------------------------------------
|
||||
|
||||
default:
|
||||
$err = PEAR::raiseError("Net_LDAP2_Filter match error: unsupported match rule '$match'!");
|
||||
return $err;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// process filter matching result
|
||||
if ($entry_matched) {
|
||||
$numOfMatches++;
|
||||
$results[] = $entry;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return $numOfMatches;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve this leaf-filters attribute, match and value component.
|
||||
*
|
||||
* For leaf filters, this returns array(attr, match, value).
|
||||
* Match is be the logical operator, not the text representation,
|
||||
* eg "=" instead of "equals". Note that some operators are really
|
||||
* a combination of operator+value with wildcard, like
|
||||
* "begins": That will return "=" with the value "value*"!
|
||||
*
|
||||
* For non-leaf filters this will drop an error.
|
||||
*
|
||||
* @todo $this->_match is not always available and thus not usable here; it would be great if it would set in the factory methods and constructor.
|
||||
* @return array|Net_LDAP2_Error
|
||||
*/
|
||||
function getComponents() {
|
||||
if ($this->isLeaf()) {
|
||||
$raw_filter = preg_replace('/^\(|\)$/', '', $this->_filter);
|
||||
$parts = Net_LDAP2_Util::split_attribute_string($raw_filter, true, true);
|
||||
if (count($parts) != 3) {
|
||||
return PEAR::raiseError("Net_LDAP2_Filter getComponents() error: invalid filter syntax - unknown matching rule used");
|
||||
} else {
|
||||
return $parts;
|
||||
}
|
||||
} else {
|
||||
return PEAR::raiseError('Net_LDAP2_Filter getComponents() call is invalid for non-leaf filters!');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
?>
|
||||
+925
@@ -0,0 +1,925 @@
|
||||
<?php
|
||||
/* vim: set expandtab tabstop=4 shiftwidth=4: */
|
||||
/**
|
||||
* File containing the Net_LDAP2_LDIF interface class.
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* @category Net
|
||||
* @package Net_LDAP2
|
||||
* @author Benedikt Hallinger <beni@php.net>
|
||||
* @copyright 2009 Benedikt Hallinger
|
||||
* @license http://www.gnu.org/licenses/lgpl-3.0.txt LGPLv3
|
||||
* @version SVN: $Id$
|
||||
* @link http://pear.php.net/package/Net_LDAP2/
|
||||
*/
|
||||
|
||||
/**
|
||||
* Includes
|
||||
*/
|
||||
require_once 'PEAR.php';
|
||||
require_once 'Net/LDAP2.php';
|
||||
require_once 'Net/LDAP2/Entry.php';
|
||||
require_once 'Net/LDAP2/Util.php';
|
||||
|
||||
/**
|
||||
* LDIF capabilitys for Net_LDAP2, closely taken from PERLs Net::LDAP
|
||||
*
|
||||
* It provides a means to convert between Net_LDAP2_Entry objects and LDAP entries
|
||||
* represented in LDIF format files. Reading and writing are supported and may
|
||||
* manipulate single entries or lists of entries.
|
||||
*
|
||||
* Usage example:
|
||||
* <code>
|
||||
* // Read and parse an ldif-file into Net_LDAP2_Entry objects
|
||||
* // and print out the DNs. Store the entries for later use.
|
||||
* require 'Net/LDAP2/LDIF.php';
|
||||
* $options = array(
|
||||
* 'onerror' => 'die'
|
||||
* );
|
||||
* $entries = array();
|
||||
* $ldif = new Net_LDAP2_LDIF('test.ldif', 'r', $options);
|
||||
* do {
|
||||
* $entry = $ldif->read_entry();
|
||||
* $dn = $entry->dn();
|
||||
* echo " done building entry: $dn\n";
|
||||
* array_push($entries, $entry);
|
||||
* } while (!$ldif->eof());
|
||||
* $ldif->done();
|
||||
*
|
||||
*
|
||||
* // write those entries to another file
|
||||
* $ldif = new Net_LDAP2_LDIF('test.out.ldif', 'w', $options);
|
||||
* $ldif->write_entry($entries);
|
||||
* $ldif->done();
|
||||
* </code>
|
||||
*
|
||||
* @category Net
|
||||
* @package Net_LDAP2
|
||||
* @author Benedikt Hallinger <beni@php.net>
|
||||
* @license http://www.gnu.org/copyleft/lesser.html LGPL
|
||||
* @link http://pear.php.net/package/Net_LDAP22/
|
||||
* @see http://www.ietf.org/rfc/rfc2849.txt
|
||||
* @todo Error handling should be PEARified
|
||||
* @todo LDAPv3 controls are not implemented yet
|
||||
*/
|
||||
class Net_LDAP2_LDIF extends PEAR
|
||||
{
|
||||
/**
|
||||
* Options
|
||||
*
|
||||
* @access protected
|
||||
* @var array
|
||||
*/
|
||||
protected $_options = array('encode' => 'base64',
|
||||
'onerror' => null,
|
||||
'change' => 0,
|
||||
'lowercase' => 0,
|
||||
'sort' => 0,
|
||||
'version' => null,
|
||||
'wrap' => 78,
|
||||
'raw' => ''
|
||||
);
|
||||
|
||||
/**
|
||||
* Errorcache
|
||||
*
|
||||
* @access protected
|
||||
* @var array
|
||||
*/
|
||||
protected $_error = array('error' => null,
|
||||
'line' => 0
|
||||
);
|
||||
|
||||
/**
|
||||
* Filehandle for read/write
|
||||
*
|
||||
* @access protected
|
||||
* @var array
|
||||
*/
|
||||
protected $_FH = null;
|
||||
|
||||
/**
|
||||
* Says, if we opened the filehandle ourselves
|
||||
*
|
||||
* @access protected
|
||||
* @var array
|
||||
*/
|
||||
protected $_FH_opened = false;
|
||||
|
||||
/**
|
||||
* Linecounter for input file handle
|
||||
*
|
||||
* @access protected
|
||||
* @var array
|
||||
*/
|
||||
protected $_input_line = 0;
|
||||
|
||||
/**
|
||||
* counter for processed entries
|
||||
*
|
||||
* @access protected
|
||||
* @var int
|
||||
*/
|
||||
protected $_entrynum = 0;
|
||||
|
||||
/**
|
||||
* Mode we are working in
|
||||
*
|
||||
* Either 'r', 'a' or 'w'
|
||||
*
|
||||
* @access protected
|
||||
* @var string
|
||||
*/
|
||||
protected $_mode = false;
|
||||
|
||||
/**
|
||||
* Tells, if the LDIF version string was already written
|
||||
*
|
||||
* @access protected
|
||||
* @var boolean
|
||||
*/
|
||||
protected $_version_written = false;
|
||||
|
||||
/**
|
||||
* Cache for lines that have build the current entry
|
||||
*
|
||||
* @access protected
|
||||
* @var boolean
|
||||
*/
|
||||
protected $_lines_cur = array();
|
||||
|
||||
/**
|
||||
* Cache for lines that will build the next entry
|
||||
*
|
||||
* @access protected
|
||||
* @var boolean
|
||||
*/
|
||||
protected $_lines_next = array();
|
||||
|
||||
/**
|
||||
* Open LDIF file for reading or for writing
|
||||
*
|
||||
* new (FILE):
|
||||
* Open the file read-only. FILE may be the name of a file
|
||||
* or an already open filehandle.
|
||||
* If the file doesn't exist, it will be created if in write mode.
|
||||
*
|
||||
* new (FILE, MODE, OPTIONS):
|
||||
* Open the file with the given MODE (see PHPs fopen()), eg "w" or "a".
|
||||
* FILE may be the name of a file or an already open filehandle.
|
||||
* PERLs Net_LDAP2 "FILE|" mode does not work curently.
|
||||
*
|
||||
* OPTIONS is an associative array and may contain:
|
||||
* encode => 'none' | 'canonical' | 'base64'
|
||||
* Some DN values in LDIF cannot be written verbatim and have to be encoded in some way:
|
||||
* 'none' No encoding.
|
||||
* 'canonical' See "canonical_dn()" in Net::LDAP::Util.
|
||||
* 'base64' Use base64. (default, this differs from the Perl interface.
|
||||
* The perl default is "none"!)
|
||||
*
|
||||
* onerror => 'die' | 'warn' | NULL
|
||||
* Specify what happens when an error is detected.
|
||||
* 'die' Net_LDAP2_LDIF will croak with an appropriate message.
|
||||
* 'warn' Net_LDAP2_LDIF will warn (echo) with an appropriate message.
|
||||
* NULL Net_LDAP2_LDIF will not warn (default), use error().
|
||||
*
|
||||
* change => 1
|
||||
* Write entry changes to the LDIF file instead of the entries itself. I.e. write LDAP
|
||||
* operations acting on the entries to the file instead of the entries contents.
|
||||
* This writes the changes usually carried out by an update() to the LDIF file.
|
||||
*
|
||||
* lowercase => 1
|
||||
* Convert attribute names to lowercase when writing.
|
||||
*
|
||||
* sort => 1
|
||||
* Sort attribute names when writing entries according to the rule:
|
||||
* objectclass first then all other attributes alphabetically sorted by attribute name
|
||||
*
|
||||
* version => '1'
|
||||
* Set the LDIF version to write to the resulting LDIF file.
|
||||
* According to RFC 2849 currently the only legal value for this option is 1.
|
||||
* When this option is set Net_LDAP2_LDIF tries to adhere more strictly to
|
||||
* the LDIF specification in RFC2489 in a few places.
|
||||
* The default is NULL meaning no version information is written to the LDIF file.
|
||||
*
|
||||
* wrap => 78
|
||||
* Number of columns where output line wrapping shall occur.
|
||||
* Default is 78. Setting it to 40 or lower inhibits wrapping.
|
||||
*
|
||||
* raw => REGEX
|
||||
* Use REGEX to denote the names of attributes that are to be
|
||||
* considered binary in search results if writing entries.
|
||||
* Example: raw => "/(?i:^jpegPhoto|;binary)/i"
|
||||
*
|
||||
* @param string|ressource $file Filename or filehandle
|
||||
* @param string $mode Mode to open filename
|
||||
* @param array $options Options like described above
|
||||
*/
|
||||
public function __construct($file, $mode = 'r', $options = array())
|
||||
{
|
||||
parent::__construct('Net_LDAP2_Error'); // default error class
|
||||
|
||||
// First, parse options
|
||||
// todo: maybe implement further checks on possible values
|
||||
foreach ($options as $option => $value) {
|
||||
if (!array_key_exists($option, $this->_options)) {
|
||||
$this->dropError('Net_LDAP2_LDIF error: option '.$option.' not known!');
|
||||
return;
|
||||
} else {
|
||||
$this->_options[$option] = strtolower($value);
|
||||
}
|
||||
}
|
||||
|
||||
// setup LDIF class
|
||||
$this->version($this->_options['version']);
|
||||
|
||||
// setup file mode
|
||||
if (!preg_match('/^[rwa]\+?$/', $mode)) {
|
||||
$this->dropError('Net_LDAP2_LDIF error: file mode '.$mode.' not supported!');
|
||||
} else {
|
||||
$this->_mode = $mode;
|
||||
|
||||
// setup filehandle
|
||||
if (is_resource($file)) {
|
||||
// TODO: checks on mode possible?
|
||||
$this->_FH =& $file;
|
||||
} else {
|
||||
$imode = substr($this->_mode, 0, 1);
|
||||
if ($imode == 'r') {
|
||||
if (!file_exists($file)) {
|
||||
$this->dropError('Unable to open '.$file.' for read: file not found');
|
||||
$this->_mode = false;
|
||||
}
|
||||
if (!is_readable($file)) {
|
||||
$this->dropError('Unable to open '.$file.' for read: permission denied');
|
||||
$this->_mode = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (($imode == 'w' || $imode == 'a')) {
|
||||
if (file_exists($file)) {
|
||||
if (!is_writable($file)) {
|
||||
$this->dropError('Unable to open '.$file.' for write: permission denied');
|
||||
$this->_mode = false;
|
||||
}
|
||||
} else {
|
||||
if (!@touch($file)) {
|
||||
$this->dropError('Unable to create '.$file.' for write: permission denied');
|
||||
$this->_mode = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->_mode) {
|
||||
$this->_FH = @fopen($file, $this->_mode);
|
||||
if (false === $this->_FH) {
|
||||
// Fallback; should never be reached if tests above are good enough!
|
||||
$this->dropError('Net_LDAP2_LDIF error: Could not open file '.$file);
|
||||
} else {
|
||||
$this->_FH_opened = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read one entry from the file and return it as a Net::LDAP::Entry object.
|
||||
*
|
||||
* @return Net_LDAP2_Entry
|
||||
*/
|
||||
public function read_entry()
|
||||
{
|
||||
// read fresh lines, set them as current lines and create the entry
|
||||
$attrs = $this->next_lines(true);
|
||||
if (count($attrs) > 0) {
|
||||
$this->_lines_cur = $attrs;
|
||||
}
|
||||
return $this->current_entry();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true when the end of the file is reached.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function eof()
|
||||
{
|
||||
return feof($this->_FH);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the entry or entries to the LDIF file.
|
||||
*
|
||||
* If you want to build an LDIF file containing several entries AND
|
||||
* you want to call write_entry() several times, you must open the filehandle
|
||||
* in append mode ("a"), otherwise you will always get the last entry only.
|
||||
*
|
||||
* @param Net_LDAP2_Entry|array $entries Entry or array of entries
|
||||
*
|
||||
* @return void
|
||||
* @todo implement operations on whole entries (adding a whole entry)
|
||||
*/
|
||||
public function write_entry($entries)
|
||||
{
|
||||
if (!is_array($entries)) {
|
||||
$entries = array($entries);
|
||||
}
|
||||
|
||||
foreach ($entries as $entry) {
|
||||
$this->_entrynum++;
|
||||
if (!$entry instanceof Net_LDAP2_Entry) {
|
||||
$this->dropError('Net_LDAP2_LDIF error: entry '.$this->_entrynum.' is not an Net_LDAP2_Entry object');
|
||||
} else {
|
||||
if ($this->_options['change']) {
|
||||
// LDIF change mode
|
||||
// fetch change information from entry
|
||||
$entry_attrs_changes = $entry->getChanges();
|
||||
$num_of_changes = count($entry_attrs_changes['add'])
|
||||
+ count($entry_attrs_changes['replace'])
|
||||
+ count($entry_attrs_changes['delete']);
|
||||
|
||||
|
||||
$is_changed = ($num_of_changes > 0 || $entry->willBeDeleted() || $entry->willBeMoved());
|
||||
|
||||
// write version if not done yet
|
||||
// also write DN of entry
|
||||
if ($is_changed) {
|
||||
if (!$this->_version_written) {
|
||||
$this->write_version();
|
||||
}
|
||||
$this->writeDN($entry->currentDN());
|
||||
}
|
||||
|
||||
// process changes
|
||||
// TODO: consider DN add!
|
||||
if ($entry->willBeDeleted()) {
|
||||
$this->writeLine("changetype: delete".PHP_EOL);
|
||||
} elseif ($entry->willBeMoved()) {
|
||||
$this->writeLine("changetype: modrdn".PHP_EOL);
|
||||
$olddn = Net_LDAP2_Util::ldap_explode_dn($entry->currentDN(), array('casefold' => 'none')); // maybe gives a bug if using multivalued RDNs
|
||||
$oldrdn = array_shift($olddn);
|
||||
$oldparent = implode(',', $olddn);
|
||||
$newdn = Net_LDAP2_Util::ldap_explode_dn($entry->dn(), array('casefold' => 'none')); // maybe gives a bug if using multivalued RDNs
|
||||
$rdn = array_shift($newdn);
|
||||
$parent = implode(',', $newdn);
|
||||
$this->writeLine("newrdn: ".$rdn.PHP_EOL);
|
||||
$this->writeLine("deleteoldrdn: 1".PHP_EOL);
|
||||
if ($parent !== $oldparent) {
|
||||
$this->writeLine("newsuperior: ".$parent.PHP_EOL);
|
||||
}
|
||||
// TODO: What if the entry has attribute changes as well?
|
||||
// I think we should check for that and make a dummy
|
||||
// entry with the changes that is written to the LDIF file
|
||||
} elseif ($num_of_changes > 0) {
|
||||
// write attribute change data
|
||||
$this->writeLine("changetype: modify".PHP_EOL);
|
||||
foreach ($entry_attrs_changes as $changetype => $entry_attrs) {
|
||||
foreach ($entry_attrs as $attr_name => $attr_values) {
|
||||
$this->writeLine("$changetype: $attr_name".PHP_EOL);
|
||||
if ($attr_values !== null) $this->writeAttribute($attr_name, $attr_values, $changetype);
|
||||
$this->writeLine("-".PHP_EOL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// finish this entrys data if we had changes
|
||||
if ($is_changed) {
|
||||
$this->finishEntry();
|
||||
}
|
||||
} else {
|
||||
// LDIF-content mode
|
||||
// fetch attributes for further processing
|
||||
$entry_attrs = $entry->getValues();
|
||||
|
||||
// sort and put objectclass-attrs to first position
|
||||
if ($this->_options['sort']) {
|
||||
ksort($entry_attrs);
|
||||
if (array_key_exists('objectclass', $entry_attrs)) {
|
||||
$oc = $entry_attrs['objectclass'];
|
||||
unset($entry_attrs['objectclass']);
|
||||
$entry_attrs = array_merge(array('objectclass' => $oc), $entry_attrs);
|
||||
}
|
||||
}
|
||||
|
||||
// write data
|
||||
if (!$this->_version_written) {
|
||||
$this->write_version();
|
||||
}
|
||||
$this->writeDN($entry->dn());
|
||||
foreach ($entry_attrs as $attr_name => $attr_values) {
|
||||
$this->writeAttribute($attr_name, $attr_values);
|
||||
}
|
||||
$this->finishEntry();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write version to LDIF
|
||||
*
|
||||
* If the object's version is defined, this method allows to explicitely write the version before an entry is written.
|
||||
* If not called explicitely, it gets called automatically when writing the first entry.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function write_version()
|
||||
{
|
||||
$this->_version_written = true;
|
||||
if (!is_null($this->version())) {
|
||||
return $this->writeLine('version: '.$this->version().PHP_EOL, 'Net_LDAP2_LDIF error: unable to write version');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get or set LDIF version
|
||||
*
|
||||
* If called without arguments it returns the version of the LDIF file or NULL if no version has been set.
|
||||
* If called with an argument it sets the LDIF version to VERSION.
|
||||
* According to RFC 2849 currently the only legal value for VERSION is 1.
|
||||
*
|
||||
* @param int $version (optional) LDIF version to set
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function version($version = null)
|
||||
{
|
||||
if ($version !== null) {
|
||||
if ($version != 1) {
|
||||
$this->dropError('Net_LDAP2_LDIF error: illegal LDIF version set');
|
||||
} else {
|
||||
$this->_options['version'] = $version;
|
||||
}
|
||||
}
|
||||
return $this->_options['version'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the file handle the Net_LDAP2_LDIF object reads from or writes to.
|
||||
*
|
||||
* You can, for example, use this to fetch the content of the LDIF file yourself
|
||||
*
|
||||
* @return null|resource
|
||||
*/
|
||||
public function &handle()
|
||||
{
|
||||
if (!is_resource($this->_FH)) {
|
||||
$this->dropError('Net_LDAP2_LDIF error: invalid file resource');
|
||||
$null = null;
|
||||
return $null;
|
||||
} else {
|
||||
return $this->_FH;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean up
|
||||
*
|
||||
* This method signals that the LDIF object is no longer needed.
|
||||
* You can use this to free up some memory and close the file handle.
|
||||
* The file handle is only closed, if it was opened from Net_LDAP2_LDIF.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function done()
|
||||
{
|
||||
// close FH if we opened it
|
||||
if ($this->_FH_opened) {
|
||||
fclose($this->handle());
|
||||
}
|
||||
|
||||
// free variables
|
||||
foreach (get_object_vars($this) as $name => $value) {
|
||||
unset($this->$name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns last error message if error was found.
|
||||
*
|
||||
* Example:
|
||||
* <code>
|
||||
* $ldif->someAction();
|
||||
* if ($ldif->error()) {
|
||||
* echo "Error: ".$ldif->error()." at input line: ".$ldif->error_lines();
|
||||
* }
|
||||
* </code>
|
||||
*
|
||||
* @param boolean $as_string If set to true, only the message is returned
|
||||
*
|
||||
* @return false|Net_LDAP2_Error
|
||||
*/
|
||||
public function error($as_string = false)
|
||||
{
|
||||
if (Net_LDAP2::isError($this->_error['error'])) {
|
||||
return ($as_string)? $this->_error['error']->getMessage() : $this->_error['error'];
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns lines that resulted in error.
|
||||
*
|
||||
* Perl returns an array of faulty lines in list context,
|
||||
* but we always just return an int because of PHPs language.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function error_lines()
|
||||
{
|
||||
return $this->_error['line'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current Net::LDAP::Entry object.
|
||||
*
|
||||
* @return Net_LDAP2_Entry|false
|
||||
*/
|
||||
public function current_entry()
|
||||
{
|
||||
return $this->parseLines($this->current_lines());
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse LDIF lines of one entry into an Net_LDAP2_Entry object
|
||||
*
|
||||
* @param array $lines LDIF lines for one entry
|
||||
*
|
||||
* @return Net_LDAP2_Entry|false Net_LDAP2_Entry object for those lines
|
||||
* @todo what about file inclusions and urls? "jpegphoto:< file:///usr/local/directory/photos/fiona.jpg"
|
||||
*/
|
||||
public function parseLines($lines)
|
||||
{
|
||||
// parse lines into an array of attributes and build the entry
|
||||
$attributes = array();
|
||||
$dn = false;
|
||||
foreach ($lines as $line) {
|
||||
if (preg_match('/^(\w+(;binary)?)(:|::|:<)\s(.+)$/', $line, $matches)) {
|
||||
$attr =& $matches[1] . $matches[2];
|
||||
$delim =& $matches[3];
|
||||
$data =& $matches[4];
|
||||
|
||||
if ($delim == ':') {
|
||||
// normal data
|
||||
$attributes[$attr][] = $data;
|
||||
} elseif ($delim == '::') {
|
||||
// base64 data
|
||||
$attributes[$attr][] = base64_decode($data);
|
||||
} elseif ($delim == ':<') {
|
||||
// file inclusion
|
||||
// TODO: Is this the job of the LDAP-client or the server?
|
||||
$this->dropError('File inclusions are currently not supported');
|
||||
//$attributes[$attr][] = ...;
|
||||
} else {
|
||||
// since the pattern above, the delimeter cannot be something else.
|
||||
$this->dropError('Net_LDAP2_LDIF parsing error: invalid syntax at parsing entry line: '.$line);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (strtolower($attr) == 'dn') {
|
||||
// DN line detected
|
||||
$dn = $attributes[$attr][0]; // save possibly decoded DN
|
||||
unset($attributes[$attr]); // remove wrongly added "dn: " attribute
|
||||
}
|
||||
} else {
|
||||
// line not in "attr: value" format -> ignore
|
||||
// maybe we should rise an error here, but this should be covered by
|
||||
// next_lines() already. A problem arises, if users try to feed data of
|
||||
// several entries to this method - the resulting entry will
|
||||
// get wrong attributes. However, this is already mentioned in the
|
||||
// methods documentation above.
|
||||
}
|
||||
}
|
||||
|
||||
if (false === $dn) {
|
||||
$this->dropError('Net_LDAP2_LDIF parsing error: unable to detect DN for entry');
|
||||
return false;
|
||||
} else {
|
||||
$newentry = Net_LDAP2_Entry::createFresh($dn, $attributes);
|
||||
return $newentry;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the lines that generated the current Net::LDAP::Entry object.
|
||||
*
|
||||
* Note that this returns an empty array if no lines have been read so far.
|
||||
*
|
||||
* @return array Array of lines
|
||||
*/
|
||||
public function current_lines()
|
||||
{
|
||||
return $this->_lines_cur;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the lines that will generate the next Net::LDAP::Entry object.
|
||||
*
|
||||
* If you set $force to TRUE then you can iterate over the lines that build
|
||||
* up entries manually. Otherwise, iterating is done using {@link read_entry()}.
|
||||
* Force will move the file pointer forward, thus returning the next entries lines.
|
||||
*
|
||||
* Wrapped lines will be unwrapped. Comments are stripped.
|
||||
*
|
||||
* @param boolean $force Set this to true if you want to iterate over the lines manually
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function next_lines($force = false)
|
||||
{
|
||||
// if we already have those lines, just return them, otherwise read
|
||||
if (count($this->_lines_next) == 0 || $force) {
|
||||
$this->_lines_next = array(); // empty in case something was left (if used $force)
|
||||
$entry_done = false;
|
||||
$fh = &$this->handle();
|
||||
$commentmode = false; // if we are in an comment, for wrapping purposes
|
||||
$datalines_read = 0; // how many lines with data we have read
|
||||
|
||||
while (!$entry_done && !$this->eof()) {
|
||||
$this->_input_line++;
|
||||
// Read line. Remove line endings, we want only data;
|
||||
// this is okay since ending spaces should be encoded
|
||||
$data = rtrim(fgets($fh));
|
||||
if ($data === false) {
|
||||
// error only, if EOF not reached after fgets() call
|
||||
if (!$this->eof()) {
|
||||
$this->dropError('Net_LDAP2_LDIF error: error reading from file at input line '.$this->_input_line, $this->_input_line);
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
if (count($this->_lines_next) > 0 && preg_match('/^$/', $data)) {
|
||||
// Entry is finished if we have an empty line after we had data
|
||||
$entry_done = true;
|
||||
|
||||
// Look ahead if the next EOF is nearby. Comments and empty
|
||||
// lines at the file end may cause problems otherwise
|
||||
$current_pos = ftell($fh);
|
||||
$data = fgets($fh);
|
||||
while (!feof($fh)) {
|
||||
if (preg_match('/^\s*$/', $data) || preg_match('/^#/', $data)) {
|
||||
// only empty lines or comments, continue to seek
|
||||
// TODO: Known bug: Wrappings for comments are okay but are treaten as
|
||||
// error, since we do not honor comment mode here.
|
||||
// This should be a very theoretically case, however
|
||||
// i am willing to fix this if really necessary.
|
||||
$this->_input_line++;
|
||||
$current_pos = ftell($fh);
|
||||
$data = fgets($fh);
|
||||
} else {
|
||||
// Data found if non emtpy line and not a comment!!
|
||||
// Rewind to position prior last read and stop lookahead
|
||||
fseek($fh, $current_pos);
|
||||
break;
|
||||
}
|
||||
}
|
||||
// now we have either the file pointer at the beginning of
|
||||
// a new data position or at the end of file causing feof() to return true
|
||||
|
||||
} else {
|
||||
// build lines
|
||||
if (preg_match('/^version:\s(.+)$/', $data, $match)) {
|
||||
// version statement, set version
|
||||
$this->version($match[1]);
|
||||
} elseif (preg_match('/^\w+(;binary)?::?\s.+$/', $data)) {
|
||||
// normal attribute: add line
|
||||
$commentmode = false;
|
||||
$this->_lines_next[] = trim($data);
|
||||
$datalines_read++;
|
||||
} elseif (preg_match('/^\s(.+)$/', $data, $matches)) {
|
||||
// wrapped data: unwrap if not in comment mode
|
||||
// note that the \s above is some more liberal than
|
||||
// the RFC requests as it also matches tabs etc.
|
||||
if (!$commentmode) {
|
||||
if ($datalines_read == 0) {
|
||||
// first line of entry: wrapped data is illegal
|
||||
$this->dropError('Net_LDAP2_LDIF error: illegal wrapping at input line '.$this->_input_line, $this->_input_line);
|
||||
} else {
|
||||
$last = array_pop($this->_lines_next);
|
||||
$last = $last.$matches[1];
|
||||
$this->_lines_next[] = $last;
|
||||
$datalines_read++;
|
||||
}
|
||||
}
|
||||
} elseif (preg_match('/^#/', $data)) {
|
||||
// LDIF comments
|
||||
$commentmode = true;
|
||||
} elseif (preg_match('/^\s*$/', $data)) {
|
||||
// empty line but we had no data for this
|
||||
// entry, so just ignore this line
|
||||
$commentmode = false;
|
||||
} else {
|
||||
$this->dropError('Net_LDAP2_LDIF error: invalid syntax at input line '.$this->_input_line, $this->_input_line);
|
||||
continue;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $this->_lines_next;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert an attribute and value to LDIF string representation
|
||||
*
|
||||
* It honors correct encoding of values according to RFC 2849.
|
||||
* Line wrapping will occur at the configured maximum but only if
|
||||
* the value is greater than 40 chars.
|
||||
*
|
||||
* @param string $attr_name Name of the attribute
|
||||
* @param string $attr_value Value of the attribute
|
||||
*
|
||||
* @access protected
|
||||
* @return string LDIF string for that attribute and value
|
||||
*/
|
||||
protected function convertAttribute($attr_name, $attr_value)
|
||||
{
|
||||
// Handle empty attribute or process
|
||||
if (strlen($attr_value) == 0) {
|
||||
$attr_value = " ";
|
||||
} else {
|
||||
$base64 = false;
|
||||
// ASCII-chars that are NOT safe for the
|
||||
// start and for being inside the value.
|
||||
// These are the int values of those chars.
|
||||
$unsafe_init = array(0, 10, 13, 32, 58, 60);
|
||||
$unsafe = array(0, 10, 13);
|
||||
|
||||
// Test for illegal init char
|
||||
$init_ord = ord(substr($attr_value, 0, 1));
|
||||
if ($init_ord > 127 || in_array($init_ord, $unsafe_init)) {
|
||||
$base64 = true;
|
||||
}
|
||||
|
||||
// Test for illegal content char
|
||||
for ($i = 0; $i < strlen($attr_value); $i++) {
|
||||
$char_ord = ord(substr($attr_value, $i, 1));
|
||||
if ($char_ord > 127 || in_array($char_ord, $unsafe)) {
|
||||
$base64 = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Test for ending space
|
||||
if (substr($attr_value, -1) == ' ') {
|
||||
$base64 = true;
|
||||
}
|
||||
|
||||
// If converting is needed, do it
|
||||
// Either we have some special chars or a matching "raw" regex
|
||||
if ($base64 || ($this->_options['raw'] && preg_match($this->_options['raw'], $attr_name))) {
|
||||
$attr_name .= ':';
|
||||
$attr_value = base64_encode($attr_value);
|
||||
}
|
||||
|
||||
// Lowercase attr names if requested
|
||||
if ($this->_options['lowercase']) $attr_name = strtolower($attr_name);
|
||||
|
||||
// Handle line wrapping
|
||||
if ($this->_options['wrap'] > 40 && strlen($attr_value) > $this->_options['wrap']) {
|
||||
$attr_value = wordwrap($attr_value, $this->_options['wrap'], PHP_EOL." ", true);
|
||||
}
|
||||
}
|
||||
|
||||
return $attr_name.': '.$attr_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert an entries DN to LDIF string representation
|
||||
*
|
||||
* It honors correct encoding of values according to RFC 2849.
|
||||
*
|
||||
* @param string $dn UTF8-Encoded DN
|
||||
*
|
||||
* @access protected
|
||||
* @return string LDIF string for that DN
|
||||
* @todo I am not sure, if the UTF8 stuff is correctly handled right now
|
||||
*/
|
||||
protected function convertDN($dn)
|
||||
{
|
||||
$base64 = false;
|
||||
// ASCII-chars that are NOT safe for the
|
||||
// start and for being inside the dn.
|
||||
// These are the int values of those chars.
|
||||
$unsafe_init = array(0, 10, 13, 32, 58, 60);
|
||||
$unsafe = array(0, 10, 13);
|
||||
|
||||
// Test for illegal init char
|
||||
$init_ord = ord(substr($dn, 0, 1));
|
||||
if ($init_ord >= 127 || in_array($init_ord, $unsafe_init)) {
|
||||
$base64 = true;
|
||||
}
|
||||
|
||||
// Test for illegal content char
|
||||
for ($i = 0; $i < strlen($dn); $i++) {
|
||||
$char = substr($dn, $i, 1);
|
||||
if (ord($char) >= 127 || in_array($init_ord, $unsafe)) {
|
||||
$base64 = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Test for ending space
|
||||
if (substr($dn, -1) == ' ') {
|
||||
$base64 = true;
|
||||
}
|
||||
|
||||
// if converting is needed, do it
|
||||
return ($base64)? 'dn:: '.base64_encode($dn) : 'dn: '.$dn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes an attribute to the filehandle
|
||||
*
|
||||
* @param string $attr_name Name of the attribute
|
||||
* @param string|array $attr_values Single attribute value or array with attribute values
|
||||
*
|
||||
* @access protected
|
||||
* @return void
|
||||
*/
|
||||
protected function writeAttribute($attr_name, $attr_values)
|
||||
{
|
||||
// write out attribute content
|
||||
if (!is_array($attr_values)) {
|
||||
$attr_values = array($attr_values);
|
||||
}
|
||||
foreach ($attr_values as $attr_val) {
|
||||
$line = $this->convertAttribute($attr_name, $attr_val).PHP_EOL;
|
||||
$this->writeLine($line, 'Net_LDAP2_LDIF error: unable to write attribute '.$attr_name.' of entry '.$this->_entrynum);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a DN to the filehandle
|
||||
*
|
||||
* @param string $dn DN to write
|
||||
*
|
||||
* @access protected
|
||||
* @return void
|
||||
*/
|
||||
protected function writeDN($dn)
|
||||
{
|
||||
// prepare DN
|
||||
if ($this->_options['encode'] == 'base64') {
|
||||
$dn = $this->convertDN($dn).PHP_EOL;
|
||||
} elseif ($this->_options['encode'] == 'canonical') {
|
||||
$dn = Net_LDAP2_Util::canonical_dn($dn, array('casefold' => 'none')).PHP_EOL;
|
||||
} else {
|
||||
$dn = $dn.PHP_EOL;
|
||||
}
|
||||
$this->writeLine($dn, 'Net_LDAP2_LDIF error: unable to write DN of entry '.$this->_entrynum);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finishes an LDIF entry
|
||||
*
|
||||
* @access protected
|
||||
* @return void
|
||||
*/
|
||||
protected function finishEntry()
|
||||
{
|
||||
$this->writeLine(PHP_EOL, 'Net_LDAP2_LDIF error: unable to close entry '.$this->_entrynum);
|
||||
}
|
||||
|
||||
/**
|
||||
* Just write an arbitary line to the filehandle
|
||||
*
|
||||
* @param string $line Content to write
|
||||
* @param string $error If error occurs, drop this message
|
||||
*
|
||||
* @access protected
|
||||
* @return true|false
|
||||
*/
|
||||
protected function writeLine($line, $error = 'Net_LDAP2_LDIF error: unable to write to filehandle')
|
||||
{
|
||||
if (is_resource($this->handle()) && fwrite($this->handle(), $line, strlen($line)) === false) {
|
||||
$this->dropError($error);
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Optionally raises an error and pushes the error on the error cache
|
||||
*
|
||||
* @param string $msg Errortext
|
||||
* @param int $line Line in the LDIF that caused the error
|
||||
*
|
||||
* @access protected
|
||||
* @return void
|
||||
*/
|
||||
protected function dropError($msg, $line = null)
|
||||
{
|
||||
$this->_error['error'] = new Net_LDAP2_Error($msg);
|
||||
if ($line !== null) $this->_error['line'] = $line;
|
||||
|
||||
if ($this->_options['onerror'] == 'die') {
|
||||
die($msg.PHP_EOL);
|
||||
} elseif ($this->_options['onerror'] == 'warn') {
|
||||
echo $msg.PHP_EOL;
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
||||
+240
@@ -0,0 +1,240 @@
|
||||
<?php
|
||||
/* vim: set expandtab tabstop=4 shiftwidth=4: */
|
||||
/**
|
||||
* File containing the Net_LDAP2_RootDSE interface class.
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* @category Net
|
||||
* @package Net_LDAP2
|
||||
* @author Jan Wagner <wagner@netsols.de>
|
||||
* @copyright 2009 Jan Wagner
|
||||
* @license http://www.gnu.org/licenses/lgpl-3.0.txt LGPLv3
|
||||
* @version SVN: $Id$
|
||||
* @link http://pear.php.net/package/Net_LDAP2/
|
||||
*/
|
||||
|
||||
/**
|
||||
* Includes
|
||||
*/
|
||||
require_once 'PEAR.php';
|
||||
|
||||
/**
|
||||
* Getting the rootDSE entry of a LDAP server
|
||||
*
|
||||
* @category Net
|
||||
* @package Net_LDAP2
|
||||
* @author Jan Wagner <wagner@netsols.de>
|
||||
* @license http://www.gnu.org/copyleft/lesser.html LGPL
|
||||
* @link http://pear.php.net/package/Net_LDAP22/
|
||||
*/
|
||||
class Net_LDAP2_RootDSE extends PEAR
|
||||
{
|
||||
/**
|
||||
* @access protected
|
||||
* @var object Net_LDAP2_Entry
|
||||
**/
|
||||
protected $_entry;
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*
|
||||
* @param Net_LDAP2_Entry &$entry Net_LDAP2_Entry object of the RootDSE
|
||||
*/
|
||||
public function __construct(&$entry)
|
||||
{
|
||||
$this->_entry = $entry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches a RootDSE object from an LDAP connection
|
||||
*
|
||||
* @param Net_LDAP2 $ldap Directory from which the RootDSE should be fetched
|
||||
* @param array $attrs Array of attributes to search for
|
||||
*
|
||||
* @access static
|
||||
* @return Net_LDAP2_RootDSE|Net_LDAP2_Error
|
||||
*/
|
||||
public static function fetch($ldap, $attrs = null)
|
||||
{
|
||||
if (!$ldap instanceof Net_LDAP2) {
|
||||
return PEAR::raiseError("Unable to fetch Schema: Parameter \$ldap must be a Net_LDAP2 object!");
|
||||
}
|
||||
|
||||
if (is_array($attrs) && count($attrs) > 0 ) {
|
||||
$attributes = $attrs;
|
||||
} else {
|
||||
$attributes = array('vendorName',
|
||||
'vendorVersion',
|
||||
'namingContexts',
|
||||
'altServer',
|
||||
'supportedExtension',
|
||||
'supportedControl',
|
||||
'supportedSASLMechanisms',
|
||||
'supportedLDAPVersion',
|
||||
'subschemaSubentry' );
|
||||
}
|
||||
$result = $ldap->search('', '(objectClass=*)', array('attributes' => $attributes, 'scope' => 'base'));
|
||||
if (self::isError($result)) {
|
||||
return $result;
|
||||
}
|
||||
$entry = $result->shiftEntry();
|
||||
if (false === $entry) {
|
||||
return PEAR::raiseError('Could not fetch RootDSE entry');
|
||||
}
|
||||
$ret = new Net_LDAP2_RootDSE($entry);
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the requested attribute value
|
||||
*
|
||||
* Same usuage as {@link Net_LDAP2_Entry::getValue()}
|
||||
*
|
||||
* @param string $attr Attribute name
|
||||
* @param array $options Array of options
|
||||
*
|
||||
* @access public
|
||||
* @return mixed Net_LDAP2_Error object or attribute values
|
||||
* @see Net_LDAP2_Entry::get_value()
|
||||
*/
|
||||
public function getValue($attr = '', $options = '')
|
||||
{
|
||||
return $this->_entry->get_value($attr, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias function of getValue() for perl-ldap interface
|
||||
*
|
||||
* @see getValue()
|
||||
* @return mixed
|
||||
*/
|
||||
public function get_value()
|
||||
{
|
||||
$args = func_get_args();
|
||||
return call_user_func_array(array( &$this, 'getValue' ), $args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the extension is supported
|
||||
*
|
||||
* @param array $oids Array of oids to check
|
||||
*
|
||||
* @access public
|
||||
* @return boolean
|
||||
*/
|
||||
public function supportedExtension($oids)
|
||||
{
|
||||
return $this->checkAttr($oids, 'supportedExtension');
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias function of supportedExtension() for perl-ldap interface
|
||||
*
|
||||
* @see supportedExtension()
|
||||
* @return boolean
|
||||
*/
|
||||
public function supported_extension()
|
||||
{
|
||||
$args = func_get_args();
|
||||
return call_user_func_array(array( &$this, 'supportedExtension'), $args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the version is supported
|
||||
*
|
||||
* @param array $versions Versions to check
|
||||
*
|
||||
* @access public
|
||||
* @return boolean
|
||||
*/
|
||||
public function supportedVersion($versions)
|
||||
{
|
||||
return $this->checkAttr($versions, 'supportedLDAPVersion');
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias function of supportedVersion() for perl-ldap interface
|
||||
*
|
||||
* @see supportedVersion()
|
||||
* @return boolean
|
||||
*/
|
||||
public function supported_version()
|
||||
{
|
||||
$args = func_get_args();
|
||||
return call_user_func_array(array(&$this, 'supportedVersion'), $args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the control is supported
|
||||
*
|
||||
* @param array $oids Control oids to check
|
||||
*
|
||||
* @access public
|
||||
* @return boolean
|
||||
*/
|
||||
public function supportedControl($oids)
|
||||
{
|
||||
return $this->checkAttr($oids, 'supportedControl');
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias function of supportedControl() for perl-ldap interface
|
||||
*
|
||||
* @see supportedControl()
|
||||
* @return boolean
|
||||
*/
|
||||
public function supported_control()
|
||||
{
|
||||
$args = func_get_args();
|
||||
return call_user_func_array(array(&$this, 'supportedControl' ), $args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the sasl mechanism is supported
|
||||
*
|
||||
* @param array $mechlist SASL mechanisms to check
|
||||
*
|
||||
* @access public
|
||||
* @return boolean
|
||||
*/
|
||||
public function supportedSASLMechanism($mechlist)
|
||||
{
|
||||
return $this->checkAttr($mechlist, 'supportedSASLMechanisms');
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias function of supportedSASLMechanism() for perl-ldap interface
|
||||
*
|
||||
* @see supportedSASLMechanism()
|
||||
* @return boolean
|
||||
*/
|
||||
public function supported_sasl_mechanism()
|
||||
{
|
||||
$args = func_get_args();
|
||||
return call_user_func_array(array(&$this, 'supportedSASLMechanism'), $args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks for existance of value in attribute
|
||||
*
|
||||
* @param array $values values to check
|
||||
* @param string $attr attribute name
|
||||
*
|
||||
* @access protected
|
||||
* @return boolean
|
||||
*/
|
||||
protected function checkAttr($values, $attr)
|
||||
{
|
||||
if (!is_array($values)) $values = array($values);
|
||||
|
||||
foreach ($values as $value) {
|
||||
if (!@in_array($value, $this->get_value($attr, 'all'))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
+622
@@ -0,0 +1,622 @@
|
||||
<?php
|
||||
/* vim: set expandtab tabstop=4 shiftwidth=4: */
|
||||
/**
|
||||
* File containing the Net_LDAP2_Schema interface class.
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* @category Net
|
||||
* @package Net_LDAP2
|
||||
* @author Jan Wagner <wagner@netsols.de>
|
||||
* @author Benedikt Hallinger <beni@php.net>
|
||||
* @copyright 2009 Jan Wagner, Benedikt Hallinger
|
||||
* @license http://www.gnu.org/licenses/lgpl-3.0.txt LGPLv3
|
||||
* @version SVN: $Id$
|
||||
* @link http://pear.php.net/package/Net_LDAP2/
|
||||
* @todo see the comment at the end of the file
|
||||
*/
|
||||
|
||||
/**
|
||||
* Includes
|
||||
*/
|
||||
require_once 'PEAR.php';
|
||||
|
||||
/**
|
||||
* Syntax definitions
|
||||
*
|
||||
* Please don't forget to add binary attributes to isBinary() below
|
||||
* to support proper value fetching from Net_LDAP2_Entry
|
||||
*/
|
||||
define('NET_LDAP2_SYNTAX_BOOLEAN', '1.3.6.1.4.1.1466.115.121.1.7');
|
||||
define('NET_LDAP2_SYNTAX_DIRECTORY_STRING', '1.3.6.1.4.1.1466.115.121.1.15');
|
||||
define('NET_LDAP2_SYNTAX_DISTINGUISHED_NAME', '1.3.6.1.4.1.1466.115.121.1.12');
|
||||
define('NET_LDAP2_SYNTAX_INTEGER', '1.3.6.1.4.1.1466.115.121.1.27');
|
||||
define('NET_LDAP2_SYNTAX_JPEG', '1.3.6.1.4.1.1466.115.121.1.28');
|
||||
define('NET_LDAP2_SYNTAX_NUMERIC_STRING', '1.3.6.1.4.1.1466.115.121.1.36');
|
||||
define('NET_LDAP2_SYNTAX_OID', '1.3.6.1.4.1.1466.115.121.1.38');
|
||||
define('NET_LDAP2_SYNTAX_OCTET_STRING', '1.3.6.1.4.1.1466.115.121.1.40');
|
||||
|
||||
/**
|
||||
* Load an LDAP Schema and provide information
|
||||
*
|
||||
* This class takes a Subschema entry, parses this information
|
||||
* and makes it available in an array. Most of the code has been
|
||||
* inspired by perl-ldap( http://perl-ldap.sourceforge.net).
|
||||
* You will find portions of their implementation in here.
|
||||
*
|
||||
* @category Net
|
||||
* @package Net_LDAP2
|
||||
* @author Jan Wagner <wagner@netsols.de>
|
||||
* @author Benedikt Hallinger <beni@php.net>
|
||||
* @license http://www.gnu.org/copyleft/lesser.html LGPL
|
||||
* @link http://pear.php.net/package/Net_LDAP22/
|
||||
*/
|
||||
class Net_LDAP2_Schema extends PEAR
|
||||
{
|
||||
/**
|
||||
* Map of entry types to ldap attributes of subschema entry
|
||||
*
|
||||
* @access public
|
||||
* @var array
|
||||
*/
|
||||
public $types = array(
|
||||
'attribute' => 'attributeTypes',
|
||||
'ditcontentrule' => 'dITContentRules',
|
||||
'ditstructurerule' => 'dITStructureRules',
|
||||
'matchingrule' => 'matchingRules',
|
||||
'matchingruleuse' => 'matchingRuleUse',
|
||||
'nameform' => 'nameForms',
|
||||
'objectclass' => 'objectClasses',
|
||||
'syntax' => 'ldapSyntaxes'
|
||||
);
|
||||
|
||||
/**
|
||||
* Array of entries belonging to this type
|
||||
*
|
||||
* @access protected
|
||||
* @var array
|
||||
*/
|
||||
protected $_attributeTypes = array();
|
||||
protected $_matchingRules = array();
|
||||
protected $_matchingRuleUse = array();
|
||||
protected $_ldapSyntaxes = array();
|
||||
protected $_objectClasses = array();
|
||||
protected $_dITContentRules = array();
|
||||
protected $_dITStructureRules = array();
|
||||
protected $_nameForms = array();
|
||||
|
||||
|
||||
/**
|
||||
* hash of all fetched oids
|
||||
*
|
||||
* @access protected
|
||||
* @var array
|
||||
*/
|
||||
protected $_oids = array();
|
||||
|
||||
/**
|
||||
* Tells if the schema is initialized
|
||||
*
|
||||
* @access protected
|
||||
* @var boolean
|
||||
* @see parse(), get()
|
||||
*/
|
||||
protected $_initialized = false;
|
||||
|
||||
|
||||
/**
|
||||
* Constructor of the class
|
||||
*
|
||||
* @access protected
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct('Net_LDAP2_Error'); // default error class
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the Schema from an LDAP connection
|
||||
*
|
||||
* @param Net_LDAP2 $ldap LDAP connection
|
||||
* @param string $dn (optional) Subschema entry dn
|
||||
*
|
||||
* @access public
|
||||
* @return Net_LDAP2_Schema|NET_LDAP2_Error
|
||||
*/
|
||||
public static function fetch($ldap, $dn = null)
|
||||
{
|
||||
if (!$ldap instanceof Net_LDAP2) {
|
||||
return PEAR::raiseError("Unable to fetch Schema: Parameter \$ldap must be a Net_LDAP2 object!");
|
||||
}
|
||||
|
||||
$schema_o = new Net_LDAP2_Schema();
|
||||
|
||||
if (is_null($dn)) {
|
||||
// get the subschema entry via root dse
|
||||
$dse = $ldap->rootDSE(array('subschemaSubentry'));
|
||||
if (false == Net_LDAP2::isError($dse)) {
|
||||
$base = $dse->getValue('subschemaSubentry', 'single');
|
||||
if (!Net_LDAP2::isError($base)) {
|
||||
$dn = $base;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Support for buggy LDAP servers (e.g. Siemens DirX 6.x) that incorrectly
|
||||
// call this entry subSchemaSubentry instead of subschemaSubentry.
|
||||
// Note the correct case/spelling as per RFC 2251.
|
||||
if (is_null($dn)) {
|
||||
// get the subschema entry via root dse
|
||||
$dse = $ldap->rootDSE(array('subSchemaSubentry'));
|
||||
if (false == Net_LDAP2::isError($dse)) {
|
||||
$base = $dse->getValue('subSchemaSubentry', 'single');
|
||||
if (!Net_LDAP2::isError($base)) {
|
||||
$dn = $base;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Final fallback case where there is no subschemaSubentry attribute
|
||||
// in the root DSE (this is a bug for an LDAP v3 server so report this
|
||||
// to your LDAP vendor if you get this far).
|
||||
if (is_null($dn)) {
|
||||
$dn = 'cn=Subschema';
|
||||
}
|
||||
|
||||
// fetch the subschema entry
|
||||
$result = $ldap->search($dn, '(objectClass=*)',
|
||||
array('attributes' => array_values($schema_o->types),
|
||||
'scope' => 'base'));
|
||||
if (Net_LDAP2::isError($result)) {
|
||||
return PEAR::raiseError('Could not fetch Subschema entry: '.$result->getMessage());
|
||||
}
|
||||
|
||||
$entry = $result->shiftEntry();
|
||||
if (!$entry instanceof Net_LDAP2_Entry) {
|
||||
if ($entry instanceof Net_LDAP2_Error) {
|
||||
return PEAR::raiseError('Could not fetch Subschema entry: '.$entry->getMessage());
|
||||
} else {
|
||||
return PEAR::raiseError('Could not fetch Subschema entry (search returned '.$result->count().' entries. Check parameter \'basedn\')');
|
||||
}
|
||||
}
|
||||
|
||||
$schema_o->parse($entry);
|
||||
return $schema_o;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a hash of entries for the given type
|
||||
*
|
||||
* Returns a hash of entry for the givene type. Types may be:
|
||||
* objectclasses, attributes, ditcontentrules, ditstructurerules, matchingrules,
|
||||
* matchingruleuses, nameforms, syntaxes
|
||||
*
|
||||
* @param string $type Type to fetch
|
||||
*
|
||||
* @access public
|
||||
* @return array|Net_LDAP2_Error Array or Net_LDAP2_Error
|
||||
*/
|
||||
public function &getAll($type)
|
||||
{
|
||||
$map = array('objectclasses' => &$this->_objectClasses,
|
||||
'attributes' => &$this->_attributeTypes,
|
||||
'ditcontentrules' => &$this->_dITContentRules,
|
||||
'ditstructurerules' => &$this->_dITStructureRules,
|
||||
'matchingrules' => &$this->_matchingRules,
|
||||
'matchingruleuses' => &$this->_matchingRuleUse,
|
||||
'nameforms' => &$this->_nameForms,
|
||||
'syntaxes' => &$this->_ldapSyntaxes );
|
||||
|
||||
$key = strtolower($type);
|
||||
$ret = ((key_exists($key, $map)) ? $map[$key] : PEAR::raiseError("Unknown type $type"));
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a specific entry
|
||||
*
|
||||
* @param string $type Type of name
|
||||
* @param string $name Name or OID to fetch
|
||||
*
|
||||
* @access public
|
||||
* @return mixed Entry or Net_LDAP2_Error
|
||||
*/
|
||||
public function &get($type, $name)
|
||||
{
|
||||
if ($this->_initialized) {
|
||||
$type = strtolower($type);
|
||||
if (false == key_exists($type, $this->types)) {
|
||||
return PEAR::raiseError("No such type $type");
|
||||
}
|
||||
|
||||
$name = strtolower($name);
|
||||
$type_var = &$this->{'_' . $this->types[$type]};
|
||||
|
||||
if (key_exists($name, $type_var)) {
|
||||
return $type_var[$name];
|
||||
} elseif (key_exists($name, $this->_oids) && $this->_oids[$name]['type'] == $type) {
|
||||
return $this->_oids[$name];
|
||||
} else {
|
||||
return PEAR::raiseError("Could not find $type $name");
|
||||
}
|
||||
} else {
|
||||
$return = null;
|
||||
return $return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Fetches attributes that MAY be present in the given objectclass
|
||||
*
|
||||
* @param string $oc Name or OID of objectclass
|
||||
*
|
||||
* @access public
|
||||
* @return array|Net_LDAP2_Error Array with attributes or Net_LDAP2_Error
|
||||
*/
|
||||
public function may($oc)
|
||||
{
|
||||
return $this->_getAttr($oc, 'may');
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches attributes that MUST be present in the given objectclass
|
||||
*
|
||||
* @param string $oc Name or OID of objectclass
|
||||
*
|
||||
* @access public
|
||||
* @return array|Net_LDAP2_Error Array with attributes or Net_LDAP2_Error
|
||||
*/
|
||||
public function must($oc)
|
||||
{
|
||||
return $this->_getAttr($oc, 'must');
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the given attribute from the given objectclass
|
||||
*
|
||||
* @param string $oc Name or OID of objectclass
|
||||
* @param string $attr Name of attribute to fetch
|
||||
*
|
||||
* @access protected
|
||||
* @return array|Net_LDAP2_Error The attribute or Net_LDAP2_Error
|
||||
*/
|
||||
protected function _getAttr($oc, $attr)
|
||||
{
|
||||
$oc = strtolower($oc);
|
||||
if (key_exists($oc, $this->_objectClasses) && key_exists($attr, $this->_objectClasses[$oc])) {
|
||||
return $this->_objectClasses[$oc][$attr];
|
||||
} elseif (key_exists($oc, $this->_oids) &&
|
||||
$this->_oids[$oc]['type'] == 'objectclass' &&
|
||||
key_exists($attr, $this->_oids[$oc])) {
|
||||
return $this->_oids[$oc][$attr];
|
||||
} else {
|
||||
return PEAR::raiseError("Could not find $attr attributes for $oc ");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name(s) of the immediate superclass(es)
|
||||
*
|
||||
* @param string $oc Name or OID of objectclass
|
||||
*
|
||||
* @access public
|
||||
* @return array|Net_LDAP2_Error Array of names or Net_LDAP2_Error
|
||||
*/
|
||||
public function superclass($oc)
|
||||
{
|
||||
$o = $this->get('objectclass', $oc);
|
||||
if (Net_LDAP2::isError($o)) {
|
||||
return $o;
|
||||
}
|
||||
return (key_exists('sup', $o) ? $o['sup'] : array());
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the schema of the given Subschema entry
|
||||
*
|
||||
* @param Net_LDAP2_Entry &$entry Subschema entry
|
||||
*
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function parse(&$entry)
|
||||
{
|
||||
foreach ($this->types as $type => $attr) {
|
||||
// initialize map type to entry
|
||||
$type_var = '_' . $attr;
|
||||
$this->{$type_var} = array();
|
||||
|
||||
// get values for this type
|
||||
if ($entry->exists($attr)) {
|
||||
$values = $entry->getValue($attr);
|
||||
if (is_array($values)) {
|
||||
foreach ($values as $value) {
|
||||
|
||||
unset($schema_entry); // this was a real mess without it
|
||||
|
||||
// get the schema entry
|
||||
$schema_entry = $this->_parse_entry($value);
|
||||
|
||||
// set the type
|
||||
$schema_entry['type'] = $type;
|
||||
|
||||
// save a ref in $_oids
|
||||
$this->_oids[$schema_entry['oid']] = &$schema_entry;
|
||||
|
||||
// save refs for all names in type map
|
||||
$names = $schema_entry['aliases'];
|
||||
array_push($names, $schema_entry['name']);
|
||||
foreach ($names as $name) {
|
||||
$this->{$type_var}[strtolower($name)] = &$schema_entry;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->_initialized = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses an attribute value into a schema entry
|
||||
*
|
||||
* @param string $value Attribute value
|
||||
*
|
||||
* @access protected
|
||||
* @return array|false Schema entry array or false
|
||||
*/
|
||||
protected function &_parse_entry($value)
|
||||
{
|
||||
// tokens that have no value associated
|
||||
$noValue = array('single-value',
|
||||
'obsolete',
|
||||
'collective',
|
||||
'no-user-modification',
|
||||
'abstract',
|
||||
'structural',
|
||||
'auxiliary');
|
||||
|
||||
// tokens that can have multiple values
|
||||
$multiValue = array('must', 'may', 'sup');
|
||||
|
||||
$schema_entry = array('aliases' => array()); // initilization
|
||||
|
||||
$tokens = $this->_tokenize($value); // get an array of tokens
|
||||
|
||||
// remove surrounding brackets
|
||||
if ($tokens[0] == '(') array_shift($tokens);
|
||||
if ($tokens[count($tokens) - 1] == ')') array_pop($tokens); // -1 doesnt work on arrays :-(
|
||||
|
||||
$schema_entry['oid'] = array_shift($tokens); // first token is the oid
|
||||
|
||||
// cycle over the tokens until none are left
|
||||
while (count($tokens) > 0) {
|
||||
$token = strtolower(array_shift($tokens));
|
||||
if (in_array($token, $noValue)) {
|
||||
$schema_entry[$token] = 1; // single value token
|
||||
} else {
|
||||
// this one follows a string or a list if it is multivalued
|
||||
if (($schema_entry[$token] = array_shift($tokens)) == '(') {
|
||||
// this creates the list of values and cycles through the tokens
|
||||
// until the end of the list is reached ')'
|
||||
$schema_entry[$token] = array();
|
||||
while ($tmp = array_shift($tokens)) {
|
||||
if ($tmp == ')') break;
|
||||
if ($tmp != '$') array_push($schema_entry[$token], $tmp);
|
||||
}
|
||||
}
|
||||
// create a array if the value should be multivalued but was not
|
||||
if (in_array($token, $multiValue) && !is_array($schema_entry[$token])) {
|
||||
$schema_entry[$token] = array($schema_entry[$token]);
|
||||
}
|
||||
}
|
||||
}
|
||||
// get max length from syntax
|
||||
if (key_exists('syntax', $schema_entry)) {
|
||||
if (preg_match('/{(\d+)}/', $schema_entry['syntax'], $matches)) {
|
||||
$schema_entry['max_length'] = $matches[1];
|
||||
}
|
||||
}
|
||||
// force a name
|
||||
if (empty($schema_entry['name'])) {
|
||||
$schema_entry['name'] = $schema_entry['oid'];
|
||||
}
|
||||
// make one name the default and put the other ones into aliases
|
||||
if (is_array($schema_entry['name'])) {
|
||||
$aliases = $schema_entry['name'];
|
||||
$schema_entry['name'] = array_shift($aliases);
|
||||
$schema_entry['aliases'] = $aliases;
|
||||
}
|
||||
return $schema_entry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tokenizes the given value into an array of tokens
|
||||
*
|
||||
* @param string $value String to parse
|
||||
*
|
||||
* @access protected
|
||||
* @return array Array of tokens
|
||||
*/
|
||||
protected function _tokenize($value)
|
||||
{
|
||||
$tokens = array(); // array of tokens
|
||||
$matches = array(); // matches[0] full pattern match, [1,2,3] subpatterns
|
||||
|
||||
// this one is taken from perl-ldap, modified for php
|
||||
$pattern = "/\s* (?:([()]) | ([^'\s()]+) | '((?:[^']+|'[^\s)])*)') \s*/x";
|
||||
|
||||
/**
|
||||
* This one matches one big pattern wherin only one of the three subpatterns matched
|
||||
* We are interested in the subpatterns that matched. If it matched its value will be
|
||||
* non-empty and so it is a token. Tokens may be round brackets, a string, or a string
|
||||
* enclosed by '
|
||||
*/
|
||||
preg_match_all($pattern, $value, $matches);
|
||||
|
||||
for ($i = 0; $i < count($matches[0]); $i++) { // number of tokens (full pattern match)
|
||||
for ($j = 1; $j < 4; $j++) { // each subpattern
|
||||
if (null != trim($matches[$j][$i])) { // pattern match in this subpattern
|
||||
$tokens[$i] = trim($matches[$j][$i]); // this is the token
|
||||
}
|
||||
}
|
||||
}
|
||||
return $tokens;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns wether a attribute syntax is binary or not
|
||||
*
|
||||
* This method gets used by Net_LDAP2_Entry to decide which
|
||||
* PHP function needs to be used to fetch the value in the
|
||||
* proper format (e.g. binary or string)
|
||||
*
|
||||
* @param string $attribute The name of the attribute (eg.: 'sn')
|
||||
*
|
||||
* @access public
|
||||
* @return boolean
|
||||
*/
|
||||
public function isBinary($attribute)
|
||||
{
|
||||
$return = false; // default to false
|
||||
|
||||
// This list contains all syntax that should be treaten as
|
||||
// containing binary values
|
||||
// The Syntax Definitons go into constants at the top of this page
|
||||
$syntax_binary = array(
|
||||
NET_LDAP2_SYNTAX_OCTET_STRING,
|
||||
NET_LDAP2_SYNTAX_JPEG
|
||||
);
|
||||
|
||||
// Check Syntax
|
||||
$attr_s = $this->get('attribute', $attribute);
|
||||
if (Net_LDAP2::isError($attr_s)) {
|
||||
// Attribute not found in schema
|
||||
$return = false; // consider attr not binary
|
||||
} elseif (isset($attr_s['syntax']) && in_array($attr_s['syntax'], $syntax_binary)) {
|
||||
// Syntax is defined as binary in schema
|
||||
$return = true;
|
||||
} else {
|
||||
// Syntax not defined as binary, or not found
|
||||
// if attribute is a subtype, check superior attribute syntaxes
|
||||
if (isset($attr_s['sup'])) {
|
||||
foreach ($attr_s['sup'] as $superattr) {
|
||||
$return = $this->isBinary($superattr);
|
||||
if ($return) {
|
||||
break; // stop checking parents since we are binary
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* See if an schema element exists
|
||||
*
|
||||
* @param string $type Type of name, see get()
|
||||
* @param string $name Name or OID
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function exists($type, $name)
|
||||
{
|
||||
$entry = $this->get($type, $name);
|
||||
if ($entry instanceof Net_LDAP2_ERROR) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* See if an attribute is defined in the schema
|
||||
*
|
||||
* @param string $attribute Name or OID of the attribute
|
||||
* @return boolean
|
||||
*/
|
||||
public function attributeExists($attribute)
|
||||
{
|
||||
return $this->exists('attribute', $attribute);
|
||||
}
|
||||
|
||||
/**
|
||||
* See if an objectClass is defined in the schema
|
||||
*
|
||||
* @param string $ocl Name or OID of the objectClass
|
||||
* @return boolean
|
||||
*/
|
||||
public function objectClassExists($ocl)
|
||||
{
|
||||
return $this->exists('objectclass', $ocl);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* See to which ObjectClasses an attribute is assigned
|
||||
*
|
||||
* The objectclasses are sorted into the keys 'may' and 'must'.
|
||||
*
|
||||
* @param string $attribute Name or OID of the attribute
|
||||
*
|
||||
* @return array|Net_LDAP2_Error Associative array with OCL names or Error
|
||||
*/
|
||||
public function getAssignedOCLs($attribute)
|
||||
{
|
||||
$may = array();
|
||||
$must = array();
|
||||
|
||||
// Test if the attribute type is defined in the schema,
|
||||
// if so, retrieve real name for lookups
|
||||
$attr_entry = $this->get('attribute', $attribute);
|
||||
if ($attr_entry instanceof Net_LDAP2_ERROR) {
|
||||
return PEAR::raiseError("Attribute $attribute not defined in schema: ".$attr_entry->getMessage());
|
||||
} else {
|
||||
$attribute = $attr_entry['name'];
|
||||
}
|
||||
|
||||
|
||||
// We need to get all defined OCLs for this.
|
||||
$ocls = $this->getAll('objectclasses');
|
||||
foreach ($ocls as $ocl => $ocl_data) {
|
||||
// Fetch the may and must attrs and see if our searched attr is contained.
|
||||
// If so, record it in the corresponding array.
|
||||
$ocl_may_attrs = $this->may($ocl);
|
||||
$ocl_must_attrs = $this->must($ocl);
|
||||
if (is_array($ocl_may_attrs) && in_array($attribute, $ocl_may_attrs)) {
|
||||
array_push($may, $ocl_data['name']);
|
||||
}
|
||||
if (is_array($ocl_must_attrs) && in_array($attribute, $ocl_must_attrs)) {
|
||||
array_push($must, $ocl_data['name']);
|
||||
}
|
||||
}
|
||||
|
||||
return array('may' => $may, 'must' => $must);
|
||||
}
|
||||
|
||||
/**
|
||||
* See if an attribute is available in a set of objectClasses
|
||||
*
|
||||
* @param string $attribute Attribute name or OID
|
||||
* @param array $ocls Names of OCLs to check for
|
||||
*
|
||||
* @return boolean TRUE, if the attribute is defined for at least one of the OCLs
|
||||
*/
|
||||
public function checkAttribute($attribute, $ocls)
|
||||
{
|
||||
foreach ($ocls as $ocl) {
|
||||
$ocl_entry = $this->get('objectclass', $ocl);
|
||||
$ocl_may_attrs = $this->may($ocl);
|
||||
$ocl_must_attrs = $this->must($ocl);
|
||||
if (is_array($ocl_may_attrs) && in_array($attribute, $ocl_may_attrs)) {
|
||||
return true;
|
||||
}
|
||||
if (is_array($ocl_must_attrs) && in_array($attribute, $ocl_must_attrs)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false; // no ocl for the ocls found.
|
||||
}
|
||||
}
|
||||
?>
|
||||
Generated
Vendored
+59
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
/* vim: set expandtab tabstop=4 shiftwidth=4: */
|
||||
/**
|
||||
* File containing the Net_LDAP2_SchemaCache interface class.
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* @category Net
|
||||
* @package Net_LDAP2
|
||||
* @author Benedikt Hallinger <beni@php.net>
|
||||
* @copyright 2009 Benedikt Hallinger
|
||||
* @license http://www.gnu.org/licenses/lgpl-3.0.txt LGPLv3
|
||||
* @version SVN: $Id$
|
||||
* @link http://pear.php.net/package/Net_LDAP2/
|
||||
*/
|
||||
|
||||
/**
|
||||
* Interface describing a custom schema cache object
|
||||
*
|
||||
* To implement a custom schema cache, one must implement this interface and
|
||||
* pass the instanciated object to Net_LDAP2s registerSchemaCache() method.
|
||||
*/
|
||||
interface Net_LDAP2_SchemaCache
|
||||
{
|
||||
/**
|
||||
* Return the schema object from the cache
|
||||
*
|
||||
* Net_LDAP2 will consider anything returned invalid, except
|
||||
* a valid Net_LDAP2_Schema object.
|
||||
* In case you return a Net_LDAP2_Error, this error will be routed
|
||||
* to the return of the $ldap->schema() call.
|
||||
* If you return something else, Net_LDAP2 will
|
||||
* fetch a fresh Schema object from the LDAP server.
|
||||
*
|
||||
* You may want to implement a cache aging mechanism here too.
|
||||
*
|
||||
* @return Net_LDAP2_Schema|Net_LDAP2_Error|false
|
||||
*/
|
||||
public function loadSchema();
|
||||
|
||||
/**
|
||||
* Store a schema object in the cache
|
||||
*
|
||||
* This method will be called, if Net_LDAP2 has fetched a fresh
|
||||
* schema object from LDAP and wants to init or refresh the cache.
|
||||
*
|
||||
* In case of errors you may return a Net_LDAP2_Error which will
|
||||
* be routet to the client.
|
||||
* Note that doing this prevents, that the schema object fetched from LDAP
|
||||
* will be given back to the client, so only return errors if storing
|
||||
* of the cache is something crucial (e.g. for doing something else with it).
|
||||
* Normaly you dont want to give back errors in which case Net_LDAP2 needs to
|
||||
* fetch the schema once per script run and instead use the error
|
||||
* returned from loadSchema().
|
||||
*
|
||||
* @return true|Net_LDAP2_Error
|
||||
*/
|
||||
public function storeSchema($schema);
|
||||
}
|
||||
+631
@@ -0,0 +1,631 @@
|
||||
<?php
|
||||
/* vim: set expandtab tabstop=4 shiftwidth=4: */
|
||||
/**
|
||||
* File containing the Net_LDAP2_Search interface class.
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* @category Net
|
||||
* @package Net_LDAP2
|
||||
* @author Tarjej Huse <tarjei@bergfald.no>
|
||||
* @author Benedikt Hallinger <beni@php.net>
|
||||
* @copyright 2009 Tarjej Huse, Benedikt Hallinger
|
||||
* @license http://www.gnu.org/licenses/lgpl-3.0.txt LGPLv3
|
||||
* @version SVN: $Id$
|
||||
* @link http://pear.php.net/package/Net_LDAP2/
|
||||
*/
|
||||
|
||||
/**
|
||||
* Includes
|
||||
*/
|
||||
require_once 'PEAR.php';
|
||||
|
||||
/**
|
||||
* Result set of an LDAP search
|
||||
*
|
||||
* @category Net
|
||||
* @package Net_LDAP2
|
||||
* @author Tarjej Huse <tarjei@bergfald.no>
|
||||
* @author Benedikt Hallinger <beni@php.net>
|
||||
* @license http://www.gnu.org/copyleft/lesser.html LGPL
|
||||
* @link http://pear.php.net/package/Net_LDAP22/
|
||||
*/
|
||||
class Net_LDAP2_Search extends PEAR implements Iterator
|
||||
{
|
||||
/**
|
||||
* Search result identifier
|
||||
*
|
||||
* @access protected
|
||||
* @var resource
|
||||
*/
|
||||
protected $_search;
|
||||
|
||||
/**
|
||||
* LDAP resource link
|
||||
*
|
||||
* @access protected
|
||||
* @var resource
|
||||
*/
|
||||
protected $_link;
|
||||
|
||||
/**
|
||||
* Net_LDAP2 object
|
||||
*
|
||||
* A reference of the Net_LDAP2 object for passing to Net_LDAP2_Entry
|
||||
*
|
||||
* @access protected
|
||||
* @var object Net_LDAP2
|
||||
*/
|
||||
protected $_ldap;
|
||||
|
||||
/**
|
||||
* Result entry identifier
|
||||
*
|
||||
* @access protected
|
||||
* @var resource
|
||||
*/
|
||||
protected $_entry = null;
|
||||
|
||||
/**
|
||||
* The errorcode the search got
|
||||
*
|
||||
* Some errorcodes might be of interest, but might not be best handled as errors.
|
||||
* examples: 4 - LDAP_SIZELIMIT_EXCEEDED - indicates a huge search.
|
||||
* Incomplete results are returned. If you just want to check if there's anything in the search.
|
||||
* than this is a point to handle.
|
||||
* 32 - no such object - search here returns a count of 0.
|
||||
*
|
||||
* @access protected
|
||||
* @var int
|
||||
*/
|
||||
protected $_errorCode = 0; // if not set - sucess!
|
||||
|
||||
/**
|
||||
* Cache for all entries already fetched from iterator interface
|
||||
*
|
||||
* @access protected
|
||||
* @var array
|
||||
*/
|
||||
protected $_iteratorCache = array();
|
||||
|
||||
/**
|
||||
* What attributes we searched for
|
||||
*
|
||||
* The $attributes array contains the names of the searched attributes and gets
|
||||
* passed from $Net_LDAP2->search() so the Net_LDAP2_Search object can tell
|
||||
* what attributes was searched for ({@link searchedAttrs())
|
||||
*
|
||||
* This variable gets set from the constructor and returned
|
||||
* from {@link searchedAttrs()}
|
||||
*
|
||||
* @access protected
|
||||
* @var array
|
||||
*/
|
||||
protected $_searchedAttrs = array();
|
||||
|
||||
/**
|
||||
* Cache variable for storing entries fetched internally
|
||||
*
|
||||
* This currently is not used by all functions and need consolidation.
|
||||
*
|
||||
* @access protected
|
||||
* @var array
|
||||
*/
|
||||
protected $_entry_cache = false;
|
||||
|
||||
/**
|
||||
* Cache variable for count()
|
||||
*
|
||||
* @see count()
|
||||
* @access protected
|
||||
* @var int
|
||||
*/
|
||||
protected $_count_cache = null;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param resource $search Search result identifier
|
||||
* @param Net_LDAP2|resource $ldap Net_LDAP2 object or just a LDAP-Link resource
|
||||
* @param array $attributes (optional) Array with searched attribute names. (see {@link $_searchedAttrs})
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function __construct($search, $ldap, $attributes = array())
|
||||
{
|
||||
parent::__construct('Net_LDAP2_Error');
|
||||
|
||||
$this->setSearch($search);
|
||||
|
||||
if ($ldap instanceof Net_LDAP2) {
|
||||
$this->_ldap = $ldap;
|
||||
$this->setLink($this->_ldap->getLink());
|
||||
} else {
|
||||
$this->setLink($ldap);
|
||||
}
|
||||
|
||||
$this->_errorCode = @ldap_errno($this->_link);
|
||||
|
||||
if (is_array($attributes) && !empty($attributes)) {
|
||||
$this->_searchedAttrs = $attributes;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of entry objects.
|
||||
*
|
||||
* @return array Array of entry objects.
|
||||
*/
|
||||
public function entries()
|
||||
{
|
||||
$entries = array();
|
||||
|
||||
if (false === $this->_entry_cache) {
|
||||
// cache is empty: fetch from LDAP
|
||||
while ($entry = $this->shiftEntry()) {
|
||||
$entries[] = $entry;
|
||||
}
|
||||
$this->_entry_cache = $entries; // store result in cache
|
||||
}
|
||||
|
||||
return $this->_entry_cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the next entry in the searchresult from LDAP server.
|
||||
*
|
||||
* This will return a valid Net_LDAP2_Entry object or false, so
|
||||
* you can use this method to easily iterate over the entries inside
|
||||
* a while loop.
|
||||
*
|
||||
* @return Net_LDAP2_Entry|false Reference to Net_LDAP2_Entry object or false
|
||||
*/
|
||||
public function shiftEntry()
|
||||
{
|
||||
if (is_null($this->_entry)) {
|
||||
if(!$this->_entry = @ldap_first_entry($this->_link, $this->_search)) {
|
||||
$false = false;
|
||||
return $false;
|
||||
}
|
||||
$entry = Net_LDAP2_Entry::createConnected($this->_ldap, $this->_entry);
|
||||
if ($entry instanceof PEAR_Error) $entry = false;
|
||||
} else {
|
||||
if (!$this->_entry = @ldap_next_entry($this->_link, $this->_entry)) {
|
||||
$false = false;
|
||||
return $false;
|
||||
}
|
||||
$entry = Net_LDAP2_Entry::createConnected($this->_ldap, $this->_entry);
|
||||
if ($entry instanceof PEAR_Error) $entry = false;
|
||||
}
|
||||
return $entry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias function of shiftEntry() for perl-ldap interface
|
||||
*
|
||||
* @see shiftEntry()
|
||||
* @return Net_LDAP2_Entry|false
|
||||
*/
|
||||
public function shift_entry()
|
||||
{
|
||||
$args = func_get_args();
|
||||
return call_user_func_array(array( $this, 'shiftEntry' ), $args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the next entry in the searchresult, but starting from last entry
|
||||
*
|
||||
* This is the opposite to {@link shiftEntry()} and is also very useful
|
||||
* to be used inside a while loop.
|
||||
*
|
||||
* @return Net_LDAP2_Entry|false
|
||||
*/
|
||||
public function popEntry()
|
||||
{
|
||||
if (false === $this->_entry_cache) {
|
||||
// fetch entries into cache if not done so far
|
||||
$this->_entry_cache = $this->entries();
|
||||
}
|
||||
|
||||
$return = array_pop($this->_entry_cache);
|
||||
return (null === $return)? false : $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias function of popEntry() for perl-ldap interface
|
||||
*
|
||||
* @see popEntry()
|
||||
* @return Net_LDAP2_Entry|false
|
||||
*/
|
||||
public function pop_entry()
|
||||
{
|
||||
$args = func_get_args();
|
||||
return call_user_func_array(array( $this, 'popEntry' ), $args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return entries sorted as array
|
||||
*
|
||||
* This returns a array with sorted entries and the values.
|
||||
* Sorting is done with PHPs {@link array_multisort()}.
|
||||
* This method relies on {@link as_struct()} to fetch the raw data of the entries.
|
||||
*
|
||||
* Please note that attribute names are case sensitive!
|
||||
*
|
||||
* Usage example:
|
||||
* <code>
|
||||
* // to sort entries first by location, then by surename, but descending:
|
||||
* $entries = $search->sorted_as_struct(array('locality','sn'), SORT_DESC);
|
||||
* </code>
|
||||
*
|
||||
* @param array $attrs Array of attribute names to sort; order from left to right.
|
||||
* @param int $order Ordering direction, either constant SORT_ASC or SORT_DESC
|
||||
*
|
||||
* @return array|Net_LDAP2_Error Array with sorted entries or error
|
||||
* @todo what about server side sorting as specified in http://www.ietf.org/rfc/rfc2891.txt?
|
||||
*/
|
||||
public function sorted_as_struct($attrs = array('cn'), $order = SORT_ASC)
|
||||
{
|
||||
/*
|
||||
* Old Code, suitable and fast for single valued sorting
|
||||
* This code should be used if we know that single valued sorting is desired,
|
||||
* but we need some method to get that knowledge...
|
||||
*/
|
||||
/*
|
||||
$attrs = array_reverse($attrs);
|
||||
foreach ($attrs as $attribute) {
|
||||
if (!ldap_sort($this->_link, $this->_search, $attribute)){
|
||||
$this->raiseError("Sorting failed for Attribute " . $attribute);
|
||||
}
|
||||
}
|
||||
|
||||
$results = ldap_get_entries($this->_link, $this->_search);
|
||||
|
||||
unset($results['count']); //for tidier output
|
||||
if ($order) {
|
||||
return array_reverse($results);
|
||||
} else {
|
||||
return $results;
|
||||
}*/
|
||||
|
||||
/*
|
||||
* New code: complete "client side" sorting
|
||||
*/
|
||||
// first some parameterchecks
|
||||
if (!is_array($attrs)) {
|
||||
return PEAR::raiseError("Sorting failed: Parameterlist must be an array!");
|
||||
}
|
||||
if ($order != SORT_ASC && $order != SORT_DESC) {
|
||||
return PEAR::raiseError("Sorting failed: sorting direction not understood! (neither constant SORT_ASC nor SORT_DESC)");
|
||||
}
|
||||
|
||||
// fetch the entries data
|
||||
$entries = $this->as_struct();
|
||||
|
||||
// now sort each entries attribute values
|
||||
// this is neccessary because later we can only sort by one value,
|
||||
// so we need the highest or lowest attribute now, depending on the
|
||||
// selected ordering for that specific attribute
|
||||
foreach ($entries as $dn => $entry) {
|
||||
foreach ($entry as $attr_name => $attr_values) {
|
||||
sort($entries[$dn][$attr_name]);
|
||||
if ($order == SORT_DESC) {
|
||||
array_reverse($entries[$dn][$attr_name]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// reformat entrys array for later use with array_multisort()
|
||||
$to_sort = array(); // <- will be a numeric array similar to ldap_get_entries
|
||||
foreach ($entries as $dn => $entry_attr) {
|
||||
$row = array();
|
||||
$row['dn'] = $dn;
|
||||
foreach ($entry_attr as $attr_name => $attr_values) {
|
||||
$row[$attr_name] = $attr_values;
|
||||
}
|
||||
$to_sort[] = $row;
|
||||
}
|
||||
|
||||
// Build columns for array_multisort()
|
||||
// each requested attribute is one row
|
||||
$columns = array();
|
||||
foreach ($attrs as $attr_name) {
|
||||
foreach ($to_sort as $key => $row) {
|
||||
$columns[$attr_name][$key] =& $to_sort[$key][$attr_name][0];
|
||||
}
|
||||
}
|
||||
|
||||
// sort the colums with array_multisort, if there is something
|
||||
// to sort and if we have requested sort columns
|
||||
if (!empty($to_sort) && !empty($columns)) {
|
||||
$sort_params = '';
|
||||
foreach ($attrs as $attr_name) {
|
||||
$sort_params .= '$columns[\''.$attr_name.'\'], '.$order.', ';
|
||||
}
|
||||
eval("array_multisort($sort_params \$to_sort);"); // perform sorting
|
||||
}
|
||||
|
||||
return $to_sort;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return entries sorted as objects
|
||||
*
|
||||
* This returns a array with sorted Net_LDAP2_Entry objects.
|
||||
* The sorting is actually done with {@link sorted_as_struct()}.
|
||||
*
|
||||
* Please note that attribute names are case sensitive!
|
||||
* Also note, that it is (depending on server capabilitys) possible to let
|
||||
* the server sort your results. This happens through search controls
|
||||
* and is described in detail at {@link http://www.ietf.org/rfc/rfc2891.txt}
|
||||
*
|
||||
* Usage example:
|
||||
* <code>
|
||||
* // to sort entries first by location, then by surename, but descending:
|
||||
* $entries = $search->sorted(array('locality','sn'), SORT_DESC);
|
||||
* </code>
|
||||
*
|
||||
* @param array $attrs Array of sort attributes to sort; order from left to right.
|
||||
* @param int $order Ordering direction, either constant SORT_ASC or SORT_DESC
|
||||
*
|
||||
* @return array|Net_LDAP2_Error Array with sorted Net_LDAP2_Entries or error
|
||||
* @todo Entry object construction could be faster. Maybe we could use one of the factorys instead of fetching the entry again
|
||||
*/
|
||||
public function sorted($attrs = array('cn'), $order = SORT_ASC)
|
||||
{
|
||||
$return = array();
|
||||
$sorted = $this->sorted_as_struct($attrs, $order);
|
||||
if (PEAR::isError($sorted)) {
|
||||
return $sorted;
|
||||
}
|
||||
foreach ($sorted as $key => $row) {
|
||||
$entry = $this->_ldap->getEntry($row['dn'], $this->searchedAttrs());
|
||||
if (!PEAR::isError($entry)) {
|
||||
array_push($return, $entry);
|
||||
} else {
|
||||
return $entry;
|
||||
}
|
||||
}
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return entries as array
|
||||
*
|
||||
* This method returns the entries and the selected attributes values as
|
||||
* array.
|
||||
* The first array level contains all found entries where the keys are the
|
||||
* DNs of the entries. The second level arrays contian the entries attributes
|
||||
* such that the keys is the lowercased name of the attribute and the values
|
||||
* are stored in another indexed array. Note that the attribute values are stored
|
||||
* in an array even if there is no or just one value.
|
||||
*
|
||||
* The array has the following structure:
|
||||
* <code>
|
||||
* $return = array(
|
||||
* 'cn=foo,dc=example,dc=com' => array(
|
||||
* 'sn' => array('foo'),
|
||||
* 'multival' => array('val1', 'val2', 'valN')
|
||||
* )
|
||||
* 'cn=bar,dc=example,dc=com' => array(
|
||||
* 'sn' => array('bar'),
|
||||
* 'multival' => array('val1', 'valN')
|
||||
* )
|
||||
* )
|
||||
* </code>
|
||||
*
|
||||
* @return array associative result array as described above
|
||||
*/
|
||||
public function as_struct()
|
||||
{
|
||||
$return = array();
|
||||
$entries = $this->entries();
|
||||
foreach ($entries as $entry) {
|
||||
$attrs = array();
|
||||
$entry_attributes = $entry->attributes();
|
||||
foreach ($entry_attributes as $attr_name) {
|
||||
$attr_values = $entry->getValue($attr_name, 'all');
|
||||
if (!is_array($attr_values)) {
|
||||
$attr_values = array($attr_values);
|
||||
}
|
||||
$attrs[$attr_name] = $attr_values;
|
||||
}
|
||||
$return[$entry->dn()] = $attrs;
|
||||
}
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the search objects resource link
|
||||
*
|
||||
* @param resource $search Search result identifier
|
||||
*
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function setSearch($search)
|
||||
{
|
||||
$this->_search = $search;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the ldap ressource link
|
||||
*
|
||||
* @param resource $link Link identifier
|
||||
*
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function setLink($link)
|
||||
{
|
||||
$this->_link = $link;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of entries in the searchresult
|
||||
*
|
||||
* @return int Number of entries in search.
|
||||
*/
|
||||
public function count()
|
||||
{
|
||||
// this catches the situation where OL returned errno 32 = no such object!
|
||||
if (!$this->_search) {
|
||||
return 0;
|
||||
}
|
||||
// ldap_count_entries is slow (see pear bug #18752) with large results,
|
||||
// so we cache the result internally.
|
||||
if ($this->_count_cache === null) {
|
||||
$this->_count_cache = @ldap_count_entries($this->_link, $this->_search);
|
||||
}
|
||||
|
||||
return $this->_count_cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the errorcode the object got in its search.
|
||||
*
|
||||
* @return int The ldap error number.
|
||||
*/
|
||||
public function getErrorCode()
|
||||
{
|
||||
return $this->_errorCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*
|
||||
* @access protected
|
||||
*/
|
||||
public function _Net_LDAP2_Search()
|
||||
{
|
||||
@ldap_free_result($this->_search);
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes search result
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function done()
|
||||
{
|
||||
$this->_Net_LDAP2_Search();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the attribute names this search selected
|
||||
*
|
||||
* @return array
|
||||
* @see $_searchedAttrs
|
||||
* @access protected
|
||||
*/
|
||||
protected function searchedAttrs()
|
||||
{
|
||||
return $this->_searchedAttrs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells if this search exceeds a sizelimit
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function sizeLimitExceeded()
|
||||
{
|
||||
return ($this->getErrorCode() == 4);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* SPL Iterator interface methods.
|
||||
* This interface allows to use Net_LDAP2_Search
|
||||
* objects directly inside a foreach loop!
|
||||
*/
|
||||
/**
|
||||
* SPL Iterator interface: Return the current element.
|
||||
*
|
||||
* The SPL Iterator interface allows you to fetch entries inside
|
||||
* a foreach() loop: <code>foreach ($search as $dn => $entry) { ...</code>
|
||||
*
|
||||
* Of course, you may call {@link current()}, {@link key()}, {@link next()},
|
||||
* {@link rewind()} and {@link valid()} yourself.
|
||||
*
|
||||
* If the search throwed an error, it returns false.
|
||||
* False is also returned, if the end is reached
|
||||
* In case no call to next() was made, we will issue one,
|
||||
* thus returning the first entry.
|
||||
*
|
||||
* @return Net_LDAP2_Entry|false
|
||||
*/
|
||||
public function current()
|
||||
{
|
||||
if (count($this->_iteratorCache) == 0) {
|
||||
$this->next();
|
||||
reset($this->_iteratorCache);
|
||||
}
|
||||
$entry = current($this->_iteratorCache);
|
||||
return ($entry instanceof Net_LDAP2_Entry)? $entry : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* SPL Iterator interface: Return the identifying key (DN) of the current entry.
|
||||
*
|
||||
* @see current()
|
||||
* @return string|false DN of the current entry; false in case no entry is returned by current()
|
||||
*/
|
||||
public function key()
|
||||
{
|
||||
$entry = $this->current();
|
||||
return ($entry instanceof Net_LDAP2_Entry)? $entry->dn() :false;
|
||||
}
|
||||
|
||||
/**
|
||||
* SPL Iterator interface: Move forward to next entry.
|
||||
*
|
||||
* After a call to {@link next()}, {@link current()} will return
|
||||
* the next entry in the result set.
|
||||
*
|
||||
* @see current()
|
||||
* @return void
|
||||
*/
|
||||
public function next()
|
||||
{
|
||||
// fetch next entry.
|
||||
// if we have no entrys anymore, we add false (which is
|
||||
// returned by shiftEntry()) so current() will complain.
|
||||
if (count($this->_iteratorCache) - 1 <= $this->count()) {
|
||||
$this->_iteratorCache[] = $this->shiftEntry();
|
||||
}
|
||||
|
||||
// move on array pointer to current element.
|
||||
// even if we have added all entries, this will
|
||||
// ensure proper operation in case we rewind()
|
||||
next($this->_iteratorCache);
|
||||
}
|
||||
|
||||
/**
|
||||
* SPL Iterator interface: Check if there is a current element after calls to {@link rewind()} or {@link next()}.
|
||||
*
|
||||
* Used to check if we've iterated to the end of the collection.
|
||||
*
|
||||
* @see current()
|
||||
* @return boolean FALSE if there's nothing more to iterate over
|
||||
*/
|
||||
public function valid()
|
||||
{
|
||||
return ($this->current() instanceof Net_LDAP2_Entry);
|
||||
}
|
||||
|
||||
/**
|
||||
* SPL Iterator interface: Rewind the Iterator to the first element.
|
||||
*
|
||||
* After rewinding, {@link current()} will return the first entry in the result set.
|
||||
*
|
||||
* @see current()
|
||||
* @return void
|
||||
*/
|
||||
public function rewind()
|
||||
{
|
||||
reset($this->_iteratorCache);
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
Generated
Vendored
+97
@@ -0,0 +1,97 @@
|
||||
<?php
|
||||
/* vim: set expandtab tabstop=4 shiftwidth=4: */
|
||||
/**
|
||||
* File containing the example simple file based Schema Caching class.
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* @category Net
|
||||
* @package Net_LDAP2
|
||||
* @author Benedikt Hallinger <beni@php.net>
|
||||
* @copyright 2009 Benedikt Hallinger
|
||||
* @license http://www.gnu.org/licenses/lgpl-3.0.txt LGPLv3
|
||||
* @version SVN: $Id$
|
||||
* @link http://pear.php.net/package/Net_LDAP2/
|
||||
*/
|
||||
|
||||
/**
|
||||
* A simple file based schema cacher with cache aging.
|
||||
*
|
||||
* Once the cache is too old, the loadSchema() method will return false, so
|
||||
* Net_LDAP2 will fetch a fresh object from the LDAP server that will
|
||||
* overwrite the current (outdated) old cache.
|
||||
*/
|
||||
class Net_LDAP2_SimpleFileSchemaCache implements Net_LDAP2_SchemaCache
|
||||
{
|
||||
/**
|
||||
* Internal config of this cache
|
||||
*
|
||||
* @see Net_LDAP2_SimpleFileSchemaCache()
|
||||
* @var array
|
||||
*/
|
||||
protected $config = array(
|
||||
'path' => '/tmp/Net_LDAP_Schema.cache',
|
||||
'max_age' => 1200
|
||||
);
|
||||
|
||||
/**
|
||||
* Initialize the simple cache
|
||||
*
|
||||
* Config is as following:
|
||||
* path Complete path to the cache file.
|
||||
* max_age Maximum age of cache in seconds, 0 means "endlessly".
|
||||
*
|
||||
* @param array $cfg Config array
|
||||
*/
|
||||
public function __construct($cfg)
|
||||
{
|
||||
foreach ($cfg as $key => $value) {
|
||||
if (array_key_exists($key, $this->config)) {
|
||||
if (gettype($this->config[$key]) != gettype($value)) {
|
||||
$this->getCore()->dropFatalError(__CLASS__.": Could not set config! Key $key does not match type ".gettype($this->config[$key])."!");
|
||||
}
|
||||
$this->config[$key] = $value;
|
||||
} else {
|
||||
$this->getCore()->dropFatalError(__CLASS__.": Could not set config! Key $key is not defined!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the schema object from the cache
|
||||
*
|
||||
* If file is existent and cache has not expired yet,
|
||||
* then the cache is deserialized and returned.
|
||||
*
|
||||
* @return Net_LDAP2_Schema|Net_LDAP2_Error|false
|
||||
*/
|
||||
public function loadSchema()
|
||||
{
|
||||
$return = false; // Net_LDAP2 will load schema from LDAP
|
||||
if (file_exists($this->config['path'])) {
|
||||
$cache_maxage = filemtime($this->config['path']) + $this->config['max_age'];
|
||||
if (time() <= $cache_maxage || $this->config['max_age'] == 0) {
|
||||
$return = unserialize(file_get_contents($this->config['path']));
|
||||
}
|
||||
}
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a schema object in the cache
|
||||
*
|
||||
* This method will be called, if Net_LDAP2 has fetched a fresh
|
||||
* schema object from LDAP and wants to init or refresh the cache.
|
||||
*
|
||||
* To invalidate the cache and cause Net_LDAP2 to refresh the cache,
|
||||
* you can call this method with null or false as value.
|
||||
* The next call to $ldap->schema() will then refresh the caches object.
|
||||
*
|
||||
* @param mixed $schema The object that should be cached
|
||||
* @return true|Net_LDAP2_Error|false
|
||||
*/
|
||||
public function storeSchema($schema) {
|
||||
file_put_contents($this->config['path'], serialize($schema));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
+620
@@ -0,0 +1,620 @@
|
||||
<?php
|
||||
/* vim: set expandtab tabstop=4 shiftwidth=4: */
|
||||
/**
|
||||
* File containing the Net_LDAP2_Util interface class.
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* @category Net
|
||||
* @package Net_LDAP2
|
||||
* @author Benedikt Hallinger <beni@php.net>
|
||||
* @copyright 2009 Benedikt Hallinger
|
||||
* @license http://www.gnu.org/licenses/lgpl-3.0.txt LGPLv3
|
||||
* @version SVN: $Id$
|
||||
* @link http://pear.php.net/package/Net_LDAP2/
|
||||
*/
|
||||
|
||||
/**
|
||||
* Includes
|
||||
*/
|
||||
require_once 'PEAR.php';
|
||||
|
||||
/**
|
||||
* Utility Class for Net_LDAP2
|
||||
*
|
||||
* This class servers some functionality to the other classes of Net_LDAP2 but most of
|
||||
* the methods can be used separately as well.
|
||||
*
|
||||
* @category Net
|
||||
* @package Net_LDAP2
|
||||
* @author Benedikt Hallinger <beni@php.net>
|
||||
* @license http://www.gnu.org/copyleft/lesser.html LGPL
|
||||
* @link http://pear.php.net/package/Net_LDAP22/
|
||||
*/
|
||||
class Net_LDAP2_Util extends PEAR
|
||||
{
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
// We do nothing here, since all methods can be called statically.
|
||||
// In Net_LDAP <= 0.7, we needed a instance of Util, because
|
||||
// it was possible to do utf8 encoding and decoding, but this
|
||||
// has been moved to the LDAP class. The constructor remains only
|
||||
// here to document the downward compatibility of creating an instance.
|
||||
}
|
||||
|
||||
/**
|
||||
* Explodes the given DN into its elements
|
||||
*
|
||||
* {@link http://www.ietf.org/rfc/rfc2253.txt RFC 2253} says, a Distinguished Name is a sequence
|
||||
* of Relative Distinguished Names (RDNs), which themselves
|
||||
* are sets of Attributes. For each RDN a array is constructed where the RDN part is stored.
|
||||
*
|
||||
* For example, the DN 'OU=Sales+CN=J. Smith,DC=example,DC=net' is exploded to:
|
||||
* <kbd>array( [0] => array([0] => 'OU=Sales', [1] => 'CN=J. Smith'), [2] => 'DC=example', [3] => 'DC=net' )</kbd>
|
||||
*
|
||||
* [NOT IMPLEMENTED] DNs might also contain values, which are the bytes of the BER encoding of
|
||||
* the X.500 AttributeValue rather than some LDAP string syntax. These values are hex-encoded
|
||||
* and prefixed with a #. To distinguish such BER values, ldap_explode_dn uses references to
|
||||
* the actual values, e.g. '1.3.6.1.4.1.1466.0=#04024869,DC=example,DC=com' is exploded to:
|
||||
* [ { '1.3.6.1.4.1.1466.0' => "\004\002Hi" }, { 'DC' => 'example' }, { 'DC' => 'com' } ];
|
||||
* See {@link http://www.vijaymukhi.com/vmis/berldap.htm} for more information on BER.
|
||||
*
|
||||
* It also performs the following operations on the given DN:
|
||||
* - Unescape "\" followed by ",", "+", """, "\", "<", ">", ";", "#", "=", " ", or a hexpair
|
||||
* and strings beginning with "#".
|
||||
* - Removes the leading 'OID.' characters if the type is an OID instead of a name.
|
||||
* - If an RDN contains multiple parts, the parts are re-ordered so that the attribute type names are in alphabetical order.
|
||||
*
|
||||
* OPTIONS is a list of name/value pairs, valid options are:
|
||||
* casefold Controls case folding of attribute types names.
|
||||
* Attribute values are not affected by this option.
|
||||
* The default is to uppercase. Valid values are:
|
||||
* lower Lowercase attribute types names.
|
||||
* upper Uppercase attribute type names. This is the default.
|
||||
* none Do not change attribute type names.
|
||||
* reverse If TRUE, the RDN sequence is reversed.
|
||||
* onlyvalues If TRUE, then only attributes values are returned ('foo' instead of 'cn=foo')
|
||||
*
|
||||
|
||||
* @param string $dn The DN that should be exploded
|
||||
* @param array $options Options to use
|
||||
*
|
||||
* @static
|
||||
* @return array Parts of the exploded DN
|
||||
* @todo implement BER
|
||||
*/
|
||||
public static function ldap_explode_dn($dn, $options = array('casefold' => 'upper'))
|
||||
{
|
||||
if (!isset($options['onlyvalues'])) $options['onlyvalues'] = false;
|
||||
if (!isset($options['reverse'])) $options['reverse'] = false;
|
||||
if (!isset($options['casefold'])) $options['casefold'] = 'upper';
|
||||
|
||||
// Escaping of DN and stripping of "OID."
|
||||
$dn = self::canonical_dn($dn, array('casefold' => $options['casefold']));
|
||||
|
||||
// splitting the DN
|
||||
$dn_array = preg_split('/(?<=[^\\\\]),/', $dn);
|
||||
|
||||
// clear wrong splitting (possibly we have split too much)
|
||||
// /!\ Not clear, if this is neccessary here
|
||||
//$dn_array = self::correct_dn_splitting($dn_array, ',');
|
||||
|
||||
// construct subarrays for multivalued RDNs and unescape DN value
|
||||
// also convert to output format and apply casefolding
|
||||
foreach ($dn_array as $key => $value) {
|
||||
$value_u = self::unescape_dn_value($value);
|
||||
$rdns = self::split_rdn_multival($value_u[0]);
|
||||
if (count($rdns) > 1) {
|
||||
// MV RDN!
|
||||
foreach ($rdns as $subrdn_k => $subrdn_v) {
|
||||
// Casefolding
|
||||
if ($options['casefold'] == 'upper') {
|
||||
$subrdn_v = preg_replace_callback(
|
||||
"/^\w+=/",
|
||||
function ($matches) {
|
||||
return strtoupper($matches[0]);
|
||||
},
|
||||
$subrdn_v
|
||||
);
|
||||
} else if ($options['casefold'] == 'lower') {
|
||||
$subrdn_v = preg_replace_callback(
|
||||
"/^\w+=/",
|
||||
function ($matches) {
|
||||
return strtolower($matches[0]);
|
||||
},
|
||||
$subrdn_v
|
||||
);
|
||||
}
|
||||
|
||||
if ($options['onlyvalues']) {
|
||||
preg_match('/(.+?)(?<!\\\\)=(.+)/', $subrdn_v, $matches);
|
||||
$rdn_ocl = $matches[1];
|
||||
$rdn_val = $matches[2];
|
||||
$unescaped = self::unescape_dn_value($rdn_val);
|
||||
$rdns[$subrdn_k] = $unescaped[0];
|
||||
} else {
|
||||
$unescaped = self::unescape_dn_value($subrdn_v);
|
||||
$rdns[$subrdn_k] = $unescaped[0];
|
||||
}
|
||||
}
|
||||
|
||||
$dn_array[$key] = $rdns;
|
||||
} else {
|
||||
// normal RDN
|
||||
|
||||
// Casefolding
|
||||
if ($options['casefold'] == 'upper') {
|
||||
$value = preg_replace_callback(
|
||||
"/^\w+=/",
|
||||
function ($matches) {
|
||||
return strtoupper($matches[0]);
|
||||
},
|
||||
$value
|
||||
);
|
||||
} else if ($options['casefold'] == 'lower') {
|
||||
$value = preg_replace_callback(
|
||||
"/^\w+=/",
|
||||
function ($matches) {
|
||||
return strtolower($matches[0]);
|
||||
},
|
||||
$value
|
||||
);
|
||||
}
|
||||
|
||||
if ($options['onlyvalues']) {
|
||||
preg_match('/(.+?)(?<!\\\\)=(.+)/', $value, $matches);
|
||||
$dn_ocl = $matches[1];
|
||||
$dn_val = $matches[2];
|
||||
$unescaped = self::unescape_dn_value($dn_val);
|
||||
$dn_array[$key] = $unescaped[0];
|
||||
} else {
|
||||
$unescaped = self::unescape_dn_value($value);
|
||||
$dn_array[$key] = $unescaped[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($options['reverse']) {
|
||||
return array_reverse($dn_array);
|
||||
} else {
|
||||
return $dn_array;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Escapes a DN value according to RFC 2253
|
||||
*
|
||||
* Escapes the given VALUES according to RFC 2253 so that they can be safely used in LDAP DNs.
|
||||
* The characters ",", "+", """, "\", "<", ">", ";", "#", "=" with a special meaning in RFC 2252
|
||||
* are preceeded by ba backslash. Control characters with an ASCII code < 32 are represented as \hexpair.
|
||||
* Finally all leading and trailing spaces are converted to sequences of \20.
|
||||
*
|
||||
* @param array $values An array containing the DN values that should be escaped
|
||||
*
|
||||
* @static
|
||||
* @return array The array $values, but escaped
|
||||
*/
|
||||
public static function escape_dn_value($values = array())
|
||||
{
|
||||
// Parameter validation
|
||||
if (!is_array($values)) {
|
||||
$values = array($values);
|
||||
}
|
||||
|
||||
foreach ($values as $key => $val) {
|
||||
// Escaping of filter meta characters
|
||||
$val = str_replace('\\', '\\\\', $val);
|
||||
$val = str_replace(',', '\,', $val);
|
||||
$val = str_replace('+', '\+', $val);
|
||||
$val = str_replace('"', '\"', $val);
|
||||
$val = str_replace('<', '\<', $val);
|
||||
$val = str_replace('>', '\>', $val);
|
||||
$val = str_replace(';', '\;', $val);
|
||||
$val = str_replace('#', '\#', $val);
|
||||
$val = str_replace('=', '\=', $val);
|
||||
|
||||
// ASCII < 32 escaping
|
||||
$val = self::asc2hex32($val);
|
||||
|
||||
// Convert all leading and trailing spaces to sequences of \20.
|
||||
if (preg_match('/^(\s*)(.+?)(\s*)$/', $val, $matches)) {
|
||||
$val = $matches[2];
|
||||
for ($i = 0; $i < strlen($matches[1]); $i++) {
|
||||
$val = '\20'.$val;
|
||||
}
|
||||
for ($i = 0; $i < strlen($matches[3]); $i++) {
|
||||
$val = $val.'\20';
|
||||
}
|
||||
}
|
||||
|
||||
if (null === $val) $val = '\0'; // apply escaped "null" if string is empty
|
||||
|
||||
$values[$key] = $val;
|
||||
}
|
||||
|
||||
return $values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Undoes the conversion done by escape_dn_value().
|
||||
*
|
||||
* Any escape sequence starting with a baskslash - hexpair or special character -
|
||||
* will be transformed back to the corresponding character.
|
||||
*
|
||||
* @param array $values Array of DN Values
|
||||
*
|
||||
* @return array Same as $values, but unescaped
|
||||
* @static
|
||||
*/
|
||||
public static function unescape_dn_value($values = array())
|
||||
{
|
||||
// Parameter validation
|
||||
if (!is_array($values)) {
|
||||
$values = array($values);
|
||||
}
|
||||
|
||||
foreach ($values as $key => $val) {
|
||||
// strip slashes from special chars
|
||||
$val = str_replace('\\\\', '\\', $val);
|
||||
$val = str_replace('\,', ',', $val);
|
||||
$val = str_replace('\+', '+', $val);
|
||||
$val = str_replace('\"', '"', $val);
|
||||
$val = str_replace('\<', '<', $val);
|
||||
$val = str_replace('\>', '>', $val);
|
||||
$val = str_replace('\;', ';', $val);
|
||||
$val = str_replace('\#', '#', $val);
|
||||
$val = str_replace('\=', '=', $val);
|
||||
|
||||
// Translate hex code into ascii
|
||||
$values[$key] = self::hex2asc($val);
|
||||
}
|
||||
|
||||
return $values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the given DN in a canonical form
|
||||
*
|
||||
* Returns false if DN is not a valid Distinguished Name.
|
||||
* DN can either be a string or an array
|
||||
* as returned by ldap_explode_dn, which is useful when constructing a DN.
|
||||
* The DN array may have be indexed (each array value is a OCL=VALUE pair)
|
||||
* or associative (array key is OCL and value is VALUE).
|
||||
*
|
||||
* It performs the following operations on the given DN:
|
||||
* - Removes the leading 'OID.' characters if the type is an OID instead of a name.
|
||||
* - Escapes all RFC 2253 special characters (",", "+", """, "\", "<", ">", ";", "#", "="), slashes ("/"), and any other character where the ASCII code is < 32 as \hexpair.
|
||||
* - Converts all leading and trailing spaces in values to be \20.
|
||||
* - If an RDN contains multiple parts, the parts are re-ordered so that the attribute type names are in alphabetical order.
|
||||
*
|
||||
* OPTIONS is a list of name/value pairs, valid options are:
|
||||
* casefold Controls case folding of attribute type names.
|
||||
* Attribute values are not affected by this option. The default is to uppercase.
|
||||
* Valid values are:
|
||||
* lower Lowercase attribute type names.
|
||||
* upper Uppercase attribute type names. This is the default.
|
||||
* none Do not change attribute type names.
|
||||
* [NOT IMPLEMENTED] mbcescape If TRUE, characters that are encoded as a multi-octet UTF-8 sequence will be escaped as \(hexpair){2,*}.
|
||||
* reverse If TRUE, the RDN sequence is reversed.
|
||||
* separator Separator to use between RDNs. Defaults to comma (',').
|
||||
*
|
||||
* Note: The empty string "" is a valid DN, so be sure not to do a "$can_dn == false" test,
|
||||
* because an empty string evaluates to false. Use the "===" operator instead.
|
||||
*
|
||||
* @param array|string $dn The DN
|
||||
* @param array $options Options to use
|
||||
*
|
||||
* @static
|
||||
* @return false|string The canonical DN or FALSE
|
||||
* @todo implement option mbcescape
|
||||
*/
|
||||
public static function canonical_dn($dn, $options = array('casefold' => 'upper', 'separator' => ','))
|
||||
{
|
||||
if ($dn === '') return $dn; // empty DN is valid!
|
||||
|
||||
// options check
|
||||
if (!isset($options['reverse'])) {
|
||||
$options['reverse'] = false;
|
||||
} else {
|
||||
$options['reverse'] = true;
|
||||
}
|
||||
if (!isset($options['casefold'])) $options['casefold'] = 'upper';
|
||||
if (!isset($options['separator'])) $options['separator'] = ',';
|
||||
|
||||
|
||||
if (!is_array($dn)) {
|
||||
// It is not clear to me if the perl implementation splits by the user defined
|
||||
// separator or if it just uses this separator to construct the new DN
|
||||
$dn = preg_split('/(?<=[^\\\\])'.$options['separator'].'/', $dn);
|
||||
|
||||
// clear wrong splitting (possibly we have split too much)
|
||||
$dn = self::correct_dn_splitting($dn, $options['separator']);
|
||||
} else {
|
||||
// Is array, check, if the array is indexed or associative
|
||||
$assoc = false;
|
||||
foreach ($dn as $dn_key => $dn_part) {
|
||||
if (!is_int($dn_key)) {
|
||||
$assoc = true;
|
||||
}
|
||||
}
|
||||
// convert to indexed, if associative array detected
|
||||
if ($assoc) {
|
||||
$newdn = array();
|
||||
foreach ($dn as $dn_key => $dn_part) {
|
||||
if (is_array($dn_part)) {
|
||||
ksort($dn_part, SORT_STRING); // we assume here, that the rdn parts are also associative
|
||||
$newdn[] = $dn_part; // copy array as-is, so we can resolve it later
|
||||
} else {
|
||||
$newdn[] = $dn_key.'='.$dn_part;
|
||||
}
|
||||
}
|
||||
$dn =& $newdn;
|
||||
}
|
||||
}
|
||||
|
||||
// Escaping and casefolding
|
||||
foreach ($dn as $pos => $dnval) {
|
||||
if (is_array($dnval)) {
|
||||
// subarray detected, this means very surely, that we had
|
||||
// a multivalued dn part, which must be resolved
|
||||
$dnval_new = '';
|
||||
foreach ($dnval as $subkey => $subval) {
|
||||
// build RDN part
|
||||
if (!is_int($subkey)) {
|
||||
$subval = $subkey.'='.$subval;
|
||||
}
|
||||
$subval_processed = self::canonical_dn($subval);
|
||||
if (false === $subval_processed) return false;
|
||||
$dnval_new .= $subval_processed.'+';
|
||||
}
|
||||
$dn[$pos] = substr($dnval_new, 0, -1); // store RDN part, strip last plus
|
||||
} else {
|
||||
// try to split multivalued RDNS into array
|
||||
$rdns = self::split_rdn_multival($dnval);
|
||||
if (count($rdns) > 1) {
|
||||
// Multivalued RDN was detected!
|
||||
// The RDN value is expected to be correctly split by split_rdn_multival().
|
||||
// It's time to sort the RDN and build the DN!
|
||||
$rdn_string = '';
|
||||
sort($rdns, SORT_STRING); // Sort RDN keys alphabetically
|
||||
foreach ($rdns as $rdn) {
|
||||
$subval_processed = self::canonical_dn($rdn);
|
||||
if (false === $subval_processed) return false;
|
||||
$rdn_string .= $subval_processed.'+';
|
||||
}
|
||||
|
||||
$dn[$pos] = substr($rdn_string, 0, -1); // store RDN part, strip last plus
|
||||
|
||||
} else {
|
||||
// no multivalued RDN!
|
||||
// split at first unescaped "="
|
||||
$dn_comp = preg_split('/(?<=[^\\\\])=/', $rdns[0], 2);
|
||||
$ocl = ltrim($dn_comp[0]); // trim left whitespaces 'cause of "cn=foo, l=bar" syntax (whitespace after comma)
|
||||
$val = $dn_comp[1];
|
||||
|
||||
// strip 'OID.', otherwise apply casefolding and escaping
|
||||
if (substr(strtolower($ocl), 0, 4) == 'oid.') {
|
||||
$ocl = substr($ocl, 4);
|
||||
} else {
|
||||
if ($options['casefold'] == 'upper') $ocl = strtoupper($ocl);
|
||||
if ($options['casefold'] == 'lower') $ocl = strtolower($ocl);
|
||||
$ocl = self::escape_dn_value(array($ocl));
|
||||
$ocl = $ocl[0];
|
||||
}
|
||||
|
||||
// escaping of dn-value
|
||||
$val = self::escape_dn_value(array($val));
|
||||
$val = str_replace('/', '\/', $val[0]);
|
||||
|
||||
$dn[$pos] = $ocl.'='.$val;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($options['reverse']) $dn = array_reverse($dn);
|
||||
return implode($options['separator'], $dn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Escapes the given VALUES according to RFC 2254 so that they can be safely used in LDAP filters.
|
||||
*
|
||||
* Any control characters with an ACII code < 32 as well as the characters with special meaning in
|
||||
* LDAP filters "*", "(", ")", and "\" (the backslash) are converted into the representation of a
|
||||
* backslash followed by two hex digits representing the hexadecimal value of the character.
|
||||
*
|
||||
* @param array $values Array of values to escape
|
||||
*
|
||||
* @static
|
||||
* @return array Array $values, but escaped
|
||||
*/
|
||||
public static function escape_filter_value($values = array())
|
||||
{
|
||||
// Parameter validation
|
||||
if (!is_array($values)) {
|
||||
$values = array($values);
|
||||
}
|
||||
|
||||
foreach ($values as $key => $val) {
|
||||
// Escaping of filter meta characters
|
||||
$val = str_replace('\\', '\5c', $val);
|
||||
$val = str_replace('*', '\2a', $val);
|
||||
$val = str_replace('(', '\28', $val);
|
||||
$val = str_replace(')', '\29', $val);
|
||||
|
||||
// ASCII < 32 escaping
|
||||
$val = self::asc2hex32($val);
|
||||
|
||||
if (null === $val) $val = '\0'; // apply escaped "null" if string is empty
|
||||
|
||||
$values[$key] = $val;
|
||||
}
|
||||
|
||||
return $values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Undoes the conversion done by {@link escape_filter_value()}.
|
||||
*
|
||||
* Converts any sequences of a backslash followed by two hex digits into the corresponding character.
|
||||
*
|
||||
* @param array $values Array of values to escape
|
||||
*
|
||||
* @static
|
||||
* @return array Array $values, but unescaped
|
||||
*/
|
||||
public static function unescape_filter_value($values = array())
|
||||
{
|
||||
// Parameter validation
|
||||
if (!is_array($values)) {
|
||||
$values = array($values);
|
||||
}
|
||||
|
||||
foreach ($values as $key => $value) {
|
||||
// Translate hex code into ascii
|
||||
$values[$key] = self::hex2asc($value);
|
||||
}
|
||||
|
||||
return $values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts all ASCII chars < 32 to "\HEX"
|
||||
*
|
||||
* @param string $string String to convert
|
||||
*
|
||||
* @static
|
||||
* @return string
|
||||
*/
|
||||
public static function asc2hex32($string)
|
||||
{
|
||||
for ($i = 0; $i < strlen($string); $i++) {
|
||||
$char = substr($string, $i, 1);
|
||||
if (ord($char) < 32) {
|
||||
$hex = dechex(ord($char));
|
||||
if (strlen($hex) == 1) $hex = '0'.$hex;
|
||||
$string = str_replace($char, '\\'.$hex, $string);
|
||||
}
|
||||
}
|
||||
return $string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts all Hex expressions ("\HEX") to their original ASCII characters
|
||||
*
|
||||
* @param string $string String to convert
|
||||
*
|
||||
* @static
|
||||
* @author beni@php.net, heavily based on work from DavidSmith@byu.net
|
||||
* @return string
|
||||
*/
|
||||
public static function hex2asc($string)
|
||||
{
|
||||
$string = preg_replace_callback(
|
||||
"/\\\[0-9A-Fa-f]{2}/",
|
||||
function ($matches) {
|
||||
return chr(hexdec($matches[0]));
|
||||
},
|
||||
$string
|
||||
);
|
||||
return $string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Split an multivalued RDN value into an Array
|
||||
*
|
||||
* A RDN can contain multiple values, spearated by a plus sign.
|
||||
* This function returns each separate ocl=value pair of the RDN part.
|
||||
*
|
||||
* If no multivalued RDN is detected, an array containing only
|
||||
* the original rdn part is returned.
|
||||
*
|
||||
* For example, the multivalued RDN 'OU=Sales+CN=J. Smith' is exploded to:
|
||||
* <kbd>array([0] => 'OU=Sales', [1] => 'CN=J. Smith')</kbd>
|
||||
*
|
||||
* The method trys to be smart if it encounters unescaped "+" characters, but may fail,
|
||||
* so ensure escaped "+"es in attr names and attr values.
|
||||
*
|
||||
* [BUG] If you have a multivalued RDN with unescaped plus characters
|
||||
* and there is a unescaped plus sign at the end of an value followed by an
|
||||
* attribute name containing an unescaped plus, then you will get wrong splitting:
|
||||
* $rdn = 'OU=Sales+C+N=J. Smith';
|
||||
* returns:
|
||||
* array('OU=Sales+C', 'N=J. Smith');
|
||||
* The "C+" is treaten as value of the first pair instead as attr name of the second pair.
|
||||
* To prevent this, escape correctly.
|
||||
*
|
||||
* @param string $rdn Part of an (multivalued) escaped RDN (eg. ou=foo OR ou=foo+cn=bar)
|
||||
*
|
||||
* @static
|
||||
* @return array Array with the components of the multivalued RDN or Error
|
||||
*/
|
||||
public static function split_rdn_multival($rdn)
|
||||
{
|
||||
$rdns = preg_split('/(?<!\\\\)\+/', $rdn);
|
||||
$rdns = self::correct_dn_splitting($rdns, '+');
|
||||
return array_values($rdns);
|
||||
}
|
||||
|
||||
/**
|
||||
* Splits an attribute=value syntax into an array
|
||||
*
|
||||
* If escaped delimeters are used, they are returned escaped as well.
|
||||
* The split will occur at the first unescaped delimeter character.
|
||||
* In case an invalid delimeter is given, no split will be performed and an
|
||||
* one element array gets returned.
|
||||
* Optional also filter-assertion delimeters can be considered (>, <, >=, <=, ~=).
|
||||
*
|
||||
* @param string $attr Attribute and Value Syntax ("foo=bar")
|
||||
* @param boolean $extended If set to true, also filter-assertion delimeter will be matched
|
||||
* @param boolean $withDelim If set to true, the return array contains the delimeter at index 1, putting the value to index 2
|
||||
*
|
||||
* @return array Indexed array: 0=attribute name, 1=attribute value OR ($withDelim=true): 0=attr, 1=delimeter, 2=value
|
||||
*/
|
||||
public static function split_attribute_string($attr, $extended=false, $withDelim=false)
|
||||
{
|
||||
if ($withDelim) $withDelim = PREG_SPLIT_DELIM_CAPTURE;
|
||||
|
||||
if (!$extended) {
|
||||
return preg_split('/(?<!\\\\)(=)/', $attr, 2, $withDelim);
|
||||
} else {
|
||||
return preg_split('/(?<!\\\\)(>=|<=|>|<|~=|=)/', $attr, 2, $withDelim);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Corrects splitting of dn parts
|
||||
*
|
||||
* @param array $dn Raw DN array
|
||||
* @param array $separator Separator that was used when splitting
|
||||
*
|
||||
* @return array Corrected array
|
||||
* @access protected
|
||||
*/
|
||||
protected static function correct_dn_splitting($dn = array(), $separator = ',')
|
||||
{
|
||||
foreach ($dn as $key => $dn_value) {
|
||||
$dn_value = $dn[$key]; // refresh value (foreach caches!)
|
||||
// if the dn_value is not in attr=value format, then we had an
|
||||
// unescaped separator character inside the attr name or the value.
|
||||
// We assume, that it was the attribute value.
|
||||
// [TODO] To solve this, we might ask the schema. Keep in mind, that UTIL class
|
||||
// must remain independent from the other classes or connections.
|
||||
if (!preg_match('/.+(?<!\\\\)=.+/', $dn_value)) {
|
||||
unset($dn[$key]);
|
||||
if (array_key_exists($key-1, $dn)) {
|
||||
$dn[$key-1] = $dn[$key-1].$separator.$dn_value; // append to previous attr value
|
||||
} else {
|
||||
$dn[$key+1] = $dn_value.$separator.$dn[$key+1]; // first element: prepend to next attr name
|
||||
}
|
||||
}
|
||||
}
|
||||
return array_values($dn);
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
+1254
File diff suppressed because it is too large
Load Diff
+686
@@ -0,0 +1,686 @@
|
||||
<?php
|
||||
/**
|
||||
* Net_Socket
|
||||
*
|
||||
* PHP Version 4
|
||||
*
|
||||
* Copyright (c) 1997-2013 The PHP Group
|
||||
*
|
||||
* This source file is subject to version 2.0 of the PHP license,
|
||||
* that is bundled with this package in the file LICENSE, and is
|
||||
* available at through the world-wide-web at
|
||||
* http://www.php.net/license/2_02.txt.
|
||||
* If you did not receive a copy of the PHP license and are unable to
|
||||
* obtain it through the world-wide-web, please send a note to
|
||||
* license@php.net so we can mail you a copy immediately.
|
||||
*
|
||||
* Authors: Stig Bakken <ssb@php.net>
|
||||
* Chuck Hagenbuch <chuck@horde.org>
|
||||
*
|
||||
* @category Net
|
||||
* @package Net_Socket
|
||||
* @author Stig Bakken <ssb@php.net>
|
||||
* @author Chuck Hagenbuch <chuck@horde.org>
|
||||
* @copyright 1997-2003 The PHP Group
|
||||
* @license http://www.php.net/license/2_02.txt PHP 2.02
|
||||
* @link http://pear.php.net/packages/Net_Socket
|
||||
*/
|
||||
|
||||
require_once 'PEAR.php';
|
||||
|
||||
define('NET_SOCKET_READ', 1);
|
||||
define('NET_SOCKET_WRITE', 2);
|
||||
define('NET_SOCKET_ERROR', 4);
|
||||
|
||||
/**
|
||||
* Generalized Socket class.
|
||||
*
|
||||
* @category Net
|
||||
* @package Net_Socket
|
||||
* @author Stig Bakken <ssb@php.net>
|
||||
* @author Chuck Hagenbuch <chuck@horde.org>
|
||||
* @copyright 1997-2003 The PHP Group
|
||||
* @license http://www.php.net/license/2_02.txt PHP 2.02
|
||||
* @link http://pear.php.net/packages/Net_Socket
|
||||
*/
|
||||
class Net_Socket extends PEAR
|
||||
{
|
||||
/**
|
||||
* Socket file pointer.
|
||||
* @var resource $fp
|
||||
*/
|
||||
var $fp = null;
|
||||
|
||||
/**
|
||||
* Whether the socket is blocking. Defaults to true.
|
||||
* @var boolean $blocking
|
||||
*/
|
||||
var $blocking = true;
|
||||
|
||||
/**
|
||||
* Whether the socket is persistent. Defaults to false.
|
||||
* @var boolean $persistent
|
||||
*/
|
||||
var $persistent = false;
|
||||
|
||||
/**
|
||||
* The IP address to connect to.
|
||||
* @var string $addr
|
||||
*/
|
||||
var $addr = '';
|
||||
|
||||
/**
|
||||
* The port number to connect to.
|
||||
* @var integer $port
|
||||
*/
|
||||
var $port = 0;
|
||||
|
||||
/**
|
||||
* Number of seconds to wait on socket operations before assuming
|
||||
* there's no more data. Defaults to no timeout.
|
||||
* @var integer|float $timeout
|
||||
*/
|
||||
var $timeout = null;
|
||||
|
||||
/**
|
||||
* Number of bytes to read at a time in readLine() and
|
||||
* readAll(). Defaults to 2048.
|
||||
* @var integer $lineLength
|
||||
*/
|
||||
var $lineLength = 2048;
|
||||
|
||||
/**
|
||||
* The string to use as a newline terminator. Usually "\r\n" or "\n".
|
||||
* @var string $newline
|
||||
*/
|
||||
var $newline = "\r\n";
|
||||
|
||||
/**
|
||||
* Connect to the specified port. If called when the socket is
|
||||
* already connected, it disconnects and connects again.
|
||||
*
|
||||
* @param string $addr IP address or host name (may be with protocol prefix).
|
||||
* @param integer $port TCP port number.
|
||||
* @param boolean $persistent (optional) Whether the connection is
|
||||
* persistent (kept open between requests
|
||||
* by the web server).
|
||||
* @param integer $timeout (optional) Connection socket timeout.
|
||||
* @param array $options See options for stream_context_create.
|
||||
*
|
||||
* @access public
|
||||
*
|
||||
* @return boolean|PEAR_Error True on success or a PEAR_Error on failure.
|
||||
*/
|
||||
function connect($addr, $port = 0, $persistent = null,
|
||||
$timeout = null, $options = null)
|
||||
{
|
||||
if (is_resource($this->fp)) {
|
||||
@fclose($this->fp);
|
||||
$this->fp = null;
|
||||
}
|
||||
|
||||
if (!$addr) {
|
||||
return $this->raiseError('$addr cannot be empty');
|
||||
} else if (strspn($addr, ':.0123456789') == strlen($addr)) {
|
||||
$this->addr = strpos($addr, ':') !== false ? '['.$addr.']' : $addr;
|
||||
} else {
|
||||
$this->addr = $addr;
|
||||
}
|
||||
|
||||
$this->port = $port % 65536;
|
||||
|
||||
if ($persistent !== null) {
|
||||
$this->persistent = $persistent;
|
||||
}
|
||||
|
||||
$openfunc = $this->persistent ? 'pfsockopen' : 'fsockopen';
|
||||
$errno = 0;
|
||||
$errstr = '';
|
||||
|
||||
$old_track_errors = @ini_set('track_errors', 1);
|
||||
|
||||
if ($timeout <= 0) {
|
||||
$timeout = @ini_get('default_socket_timeout');
|
||||
}
|
||||
|
||||
if ($options && function_exists('stream_context_create')) {
|
||||
$context = stream_context_create($options);
|
||||
|
||||
// Since PHP 5 fsockopen doesn't allow context specification
|
||||
if (function_exists('stream_socket_client')) {
|
||||
$flags = STREAM_CLIENT_CONNECT;
|
||||
|
||||
if ($this->persistent) {
|
||||
$flags = STREAM_CLIENT_PERSISTENT;
|
||||
}
|
||||
|
||||
$addr = $this->addr . ':' . $this->port;
|
||||
$fp = stream_socket_client($addr, $errno, $errstr,
|
||||
$timeout, $flags, $context);
|
||||
} else {
|
||||
$fp = @$openfunc($this->addr, $this->port, $errno,
|
||||
$errstr, $timeout, $context);
|
||||
}
|
||||
} else {
|
||||
$fp = @$openfunc($this->addr, $this->port, $errno, $errstr, $timeout);
|
||||
}
|
||||
|
||||
if (!$fp) {
|
||||
if ($errno == 0 && !strlen($errstr) && isset($php_errormsg)) {
|
||||
$errstr = $php_errormsg;
|
||||
}
|
||||
@ini_set('track_errors', $old_track_errors);
|
||||
return $this->raiseError($errstr, $errno);
|
||||
}
|
||||
|
||||
@ini_set('track_errors', $old_track_errors);
|
||||
$this->fp = $fp;
|
||||
$this->setTimeout();
|
||||
return $this->setBlocking($this->blocking);
|
||||
}
|
||||
|
||||
/**
|
||||
* Disconnects from the peer, closes the socket.
|
||||
*
|
||||
* @access public
|
||||
* @return mixed true on success or a PEAR_Error instance otherwise
|
||||
*/
|
||||
function disconnect()
|
||||
{
|
||||
if (!is_resource($this->fp)) {
|
||||
return $this->raiseError('not connected');
|
||||
}
|
||||
|
||||
@fclose($this->fp);
|
||||
$this->fp = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the newline character/sequence to use.
|
||||
*
|
||||
* @param string $newline Newline character(s)
|
||||
* @return boolean True
|
||||
*/
|
||||
function setNewline($newline)
|
||||
{
|
||||
$this->newline = $newline;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find out if the socket is in blocking mode.
|
||||
*
|
||||
* @access public
|
||||
* @return boolean The current blocking mode.
|
||||
*/
|
||||
function isBlocking()
|
||||
{
|
||||
return $this->blocking;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether the socket connection should be blocking or
|
||||
* not. A read call to a non-blocking socket will return immediately
|
||||
* if there is no data available, whereas it will block until there
|
||||
* is data for blocking sockets.
|
||||
*
|
||||
* @param boolean $mode True for blocking sockets, false for nonblocking.
|
||||
*
|
||||
* @access public
|
||||
* @return mixed true on success or a PEAR_Error instance otherwise
|
||||
*/
|
||||
function setBlocking($mode)
|
||||
{
|
||||
if (!is_resource($this->fp)) {
|
||||
return $this->raiseError('not connected');
|
||||
}
|
||||
|
||||
$this->blocking = $mode;
|
||||
stream_set_blocking($this->fp, (int)$this->blocking);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the timeout value on socket descriptor,
|
||||
* expressed in the sum of seconds and microseconds
|
||||
*
|
||||
* @param integer $seconds Seconds.
|
||||
* @param integer $microseconds Microseconds, optional.
|
||||
*
|
||||
* @access public
|
||||
* @return mixed True on success or false on failure or
|
||||
* a PEAR_Error instance when not connected
|
||||
*/
|
||||
function setTimeout($seconds = null, $microseconds = null)
|
||||
{
|
||||
if (!is_resource($this->fp)) {
|
||||
return $this->raiseError('not connected');
|
||||
}
|
||||
|
||||
if ($seconds === null && $microseconds === null) {
|
||||
$seconds = (int) $this->timeout;
|
||||
$microseconds = (int) (($this->timeout - $seconds) * 1000000);
|
||||
} else {
|
||||
$this->timeout = $seconds + $microseconds/1000000;
|
||||
}
|
||||
|
||||
if ($this->timeout > 0) {
|
||||
return stream_set_timeout($this->fp, (int) $seconds, (int) $microseconds);
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the file buffering size on the stream.
|
||||
* See php's stream_set_write_buffer for more information.
|
||||
*
|
||||
* @param integer $size Write buffer size.
|
||||
*
|
||||
* @access public
|
||||
* @return mixed on success or an PEAR_Error object otherwise
|
||||
*/
|
||||
function setWriteBuffer($size)
|
||||
{
|
||||
if (!is_resource($this->fp)) {
|
||||
return $this->raiseError('not connected');
|
||||
}
|
||||
|
||||
$returned = stream_set_write_buffer($this->fp, $size);
|
||||
if ($returned == 0) {
|
||||
return true;
|
||||
}
|
||||
return $this->raiseError('Cannot set write buffer.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns information about an existing socket resource.
|
||||
* Currently returns four entries in the result array:
|
||||
*
|
||||
* <p>
|
||||
* timed_out (bool) - The socket timed out waiting for data<br>
|
||||
* blocked (bool) - The socket was blocked<br>
|
||||
* eof (bool) - Indicates EOF event<br>
|
||||
* unread_bytes (int) - Number of bytes left in the socket buffer<br>
|
||||
* </p>
|
||||
*
|
||||
* @access public
|
||||
* @return mixed Array containing information about existing socket
|
||||
* resource or a PEAR_Error instance otherwise
|
||||
*/
|
||||
function getStatus()
|
||||
{
|
||||
if (!is_resource($this->fp)) {
|
||||
return $this->raiseError('not connected');
|
||||
}
|
||||
|
||||
return stream_get_meta_data($this->fp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a specified line of data
|
||||
*
|
||||
* @param int $size Reading ends when size - 1 bytes have been read,
|
||||
* or a newline or an EOF (whichever comes first).
|
||||
* If no size is specified, it will keep reading from
|
||||
* the stream until it reaches the end of the line.
|
||||
*
|
||||
* @access public
|
||||
* @return mixed $size bytes of data from the socket, or a PEAR_Error if
|
||||
* not connected. If an error occurs, FALSE is returned.
|
||||
*/
|
||||
function gets($size = null)
|
||||
{
|
||||
if (!is_resource($this->fp)) {
|
||||
return $this->raiseError('not connected');
|
||||
}
|
||||
|
||||
if (is_null($size)) {
|
||||
return @fgets($this->fp);
|
||||
} else {
|
||||
return @fgets($this->fp, $size);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a specified amount of data. This is guaranteed to return,
|
||||
* and has the added benefit of getting everything in one fread()
|
||||
* chunk; if you know the size of the data you're getting
|
||||
* beforehand, this is definitely the way to go.
|
||||
*
|
||||
* @param integer $size The number of bytes to read from the socket.
|
||||
*
|
||||
* @access public
|
||||
* @return $size bytes of data from the socket, or a PEAR_Error if
|
||||
* not connected.
|
||||
*/
|
||||
function read($size)
|
||||
{
|
||||
if (!is_resource($this->fp)) {
|
||||
return $this->raiseError('not connected');
|
||||
}
|
||||
|
||||
return @fread($this->fp, $size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a specified amount of data.
|
||||
*
|
||||
* @param string $data Data to write.
|
||||
* @param integer $blocksize Amount of data to write at once.
|
||||
* NULL means all at once.
|
||||
*
|
||||
* @access public
|
||||
* @return mixed If the socket is not connected, returns an instance of
|
||||
* PEAR_Error.
|
||||
* If the write succeeds, returns the number of bytes written.
|
||||
* If the write fails, returns false.
|
||||
* If the socket times out, returns an instance of PEAR_Error.
|
||||
*/
|
||||
function write($data, $blocksize = null)
|
||||
{
|
||||
if (!is_resource($this->fp)) {
|
||||
return $this->raiseError('not connected');
|
||||
}
|
||||
|
||||
if (is_null($blocksize) && !OS_WINDOWS) {
|
||||
$written = @fwrite($this->fp, $data);
|
||||
|
||||
// Check for timeout or lost connection
|
||||
if (!$written) {
|
||||
$meta_data = $this->getStatus();
|
||||
|
||||
if (!is_array($meta_data)) {
|
||||
return $meta_data; // PEAR_Error
|
||||
}
|
||||
|
||||
if (!empty($meta_data['timed_out'])) {
|
||||
return $this->raiseError('timed out');
|
||||
}
|
||||
}
|
||||
|
||||
return $written;
|
||||
} else {
|
||||
if (is_null($blocksize)) {
|
||||
$blocksize = 1024;
|
||||
}
|
||||
|
||||
$pos = 0;
|
||||
$size = strlen($data);
|
||||
while ($pos < $size) {
|
||||
$written = @fwrite($this->fp, substr($data, $pos, $blocksize));
|
||||
|
||||
// Check for timeout or lost connection
|
||||
if (!$written) {
|
||||
$meta_data = $this->getStatus();
|
||||
|
||||
if (!is_array($meta_data)) {
|
||||
return $meta_data; // PEAR_Error
|
||||
}
|
||||
|
||||
if (!empty($meta_data['timed_out'])) {
|
||||
return $this->raiseError('timed out');
|
||||
}
|
||||
|
||||
return $written;
|
||||
}
|
||||
|
||||
$pos += $written;
|
||||
}
|
||||
|
||||
return $pos;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a line of data to the socket, followed by a trailing newline.
|
||||
*
|
||||
* @param string $data Data to write
|
||||
*
|
||||
* @access public
|
||||
* @return mixed fwrite() result, or PEAR_Error when not connected
|
||||
*/
|
||||
function writeLine($data)
|
||||
{
|
||||
if (!is_resource($this->fp)) {
|
||||
return $this->raiseError('not connected');
|
||||
}
|
||||
|
||||
return fwrite($this->fp, $data . $this->newline);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests for end-of-file on a socket descriptor.
|
||||
*
|
||||
* Also returns true if the socket is disconnected.
|
||||
*
|
||||
* @access public
|
||||
* @return bool
|
||||
*/
|
||||
function eof()
|
||||
{
|
||||
return (!is_resource($this->fp) || feof($this->fp));
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a byte of data
|
||||
*
|
||||
* @access public
|
||||
* @return 1 byte of data from the socket, or a PEAR_Error if
|
||||
* not connected.
|
||||
*/
|
||||
function readByte()
|
||||
{
|
||||
if (!is_resource($this->fp)) {
|
||||
return $this->raiseError('not connected');
|
||||
}
|
||||
|
||||
return ord(@fread($this->fp, 1));
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a word of data
|
||||
*
|
||||
* @access public
|
||||
* @return 1 word of data from the socket, or a PEAR_Error if
|
||||
* not connected.
|
||||
*/
|
||||
function readWord()
|
||||
{
|
||||
if (!is_resource($this->fp)) {
|
||||
return $this->raiseError('not connected');
|
||||
}
|
||||
|
||||
$buf = @fread($this->fp, 2);
|
||||
return (ord($buf[0]) + (ord($buf[1]) << 8));
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads an int of data
|
||||
*
|
||||
* @access public
|
||||
* @return integer 1 int of data from the socket, or a PEAR_Error if
|
||||
* not connected.
|
||||
*/
|
||||
function readInt()
|
||||
{
|
||||
if (!is_resource($this->fp)) {
|
||||
return $this->raiseError('not connected');
|
||||
}
|
||||
|
||||
$buf = @fread($this->fp, 4);
|
||||
return (ord($buf[0]) + (ord($buf[1]) << 8) +
|
||||
(ord($buf[2]) << 16) + (ord($buf[3]) << 24));
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a zero-terminated string of data
|
||||
*
|
||||
* @access public
|
||||
* @return string, or a PEAR_Error if
|
||||
* not connected.
|
||||
*/
|
||||
function readString()
|
||||
{
|
||||
if (!is_resource($this->fp)) {
|
||||
return $this->raiseError('not connected');
|
||||
}
|
||||
|
||||
$string = '';
|
||||
while (($char = @fread($this->fp, 1)) != "\x00") {
|
||||
$string .= $char;
|
||||
}
|
||||
return $string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads an IP Address and returns it in a dot formatted string
|
||||
*
|
||||
* @access public
|
||||
* @return Dot formatted string, or a PEAR_Error if
|
||||
* not connected.
|
||||
*/
|
||||
function readIPAddress()
|
||||
{
|
||||
if (!is_resource($this->fp)) {
|
||||
return $this->raiseError('not connected');
|
||||
}
|
||||
|
||||
$buf = @fread($this->fp, 4);
|
||||
return sprintf('%d.%d.%d.%d', ord($buf[0]), ord($buf[1]),
|
||||
ord($buf[2]), ord($buf[3]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Read until either the end of the socket or a newline, whichever
|
||||
* comes first. Strips the trailing newline from the returned data.
|
||||
*
|
||||
* @access public
|
||||
* @return All available data up to a newline, without that
|
||||
* newline, or until the end of the socket, or a PEAR_Error if
|
||||
* not connected.
|
||||
*/
|
||||
function readLine()
|
||||
{
|
||||
if (!is_resource($this->fp)) {
|
||||
return $this->raiseError('not connected');
|
||||
}
|
||||
|
||||
$line = '';
|
||||
|
||||
$timeout = time() + $this->timeout;
|
||||
|
||||
while (!feof($this->fp) && (!$this->timeout || time() < $timeout)) {
|
||||
$line .= @fgets($this->fp, $this->lineLength);
|
||||
if (substr($line, -1) == "\n") {
|
||||
return rtrim($line, $this->newline);
|
||||
}
|
||||
}
|
||||
return $line;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read until the socket closes, or until there is no more data in
|
||||
* the inner PHP buffer. If the inner buffer is empty, in blocking
|
||||
* mode we wait for at least 1 byte of data. Therefore, in
|
||||
* blocking mode, if there is no data at all to be read, this
|
||||
* function will never exit (unless the socket is closed on the
|
||||
* remote end).
|
||||
*
|
||||
* @access public
|
||||
*
|
||||
* @return string All data until the socket closes, or a PEAR_Error if
|
||||
* not connected.
|
||||
*/
|
||||
function readAll()
|
||||
{
|
||||
if (!is_resource($this->fp)) {
|
||||
return $this->raiseError('not connected');
|
||||
}
|
||||
|
||||
$data = '';
|
||||
while (!feof($this->fp)) {
|
||||
$data .= @fread($this->fp, $this->lineLength);
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the equivalent of the select() system call on the socket
|
||||
* with a timeout specified by tv_sec and tv_usec.
|
||||
*
|
||||
* @param integer $state Which of read/write/error to check for.
|
||||
* @param integer $tv_sec Number of seconds for timeout.
|
||||
* @param integer $tv_usec Number of microseconds for timeout.
|
||||
*
|
||||
* @access public
|
||||
* @return False if select fails, integer describing which of read/write/error
|
||||
* are ready, or PEAR_Error if not connected.
|
||||
*/
|
||||
function select($state, $tv_sec, $tv_usec = 0)
|
||||
{
|
||||
if (!is_resource($this->fp)) {
|
||||
return $this->raiseError('not connected');
|
||||
}
|
||||
|
||||
$read = null;
|
||||
$write = null;
|
||||
$except = null;
|
||||
if ($state & NET_SOCKET_READ) {
|
||||
$read[] = $this->fp;
|
||||
}
|
||||
if ($state & NET_SOCKET_WRITE) {
|
||||
$write[] = $this->fp;
|
||||
}
|
||||
if ($state & NET_SOCKET_ERROR) {
|
||||
$except[] = $this->fp;
|
||||
}
|
||||
if (false === ($sr = stream_select($read, $write, $except,
|
||||
$tv_sec, $tv_usec))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$result = 0;
|
||||
if (count($read)) {
|
||||
$result |= NET_SOCKET_READ;
|
||||
}
|
||||
if (count($write)) {
|
||||
$result |= NET_SOCKET_WRITE;
|
||||
}
|
||||
if (count($except)) {
|
||||
$result |= NET_SOCKET_ERROR;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns encryption on/off on a connected socket.
|
||||
*
|
||||
* @param bool $enabled Set this parameter to true to enable encryption
|
||||
* and false to disable encryption.
|
||||
* @param integer $type Type of encryption. See stream_socket_enable_crypto()
|
||||
* for values.
|
||||
*
|
||||
* @see http://se.php.net/manual/en/function.stream-socket-enable-crypto.php
|
||||
* @access public
|
||||
* @return false on error, true on success and 0 if there isn't enough data
|
||||
* and the user should try again (non-blocking sockets only).
|
||||
* A PEAR_Error object is returned if the socket is not
|
||||
* connected
|
||||
*/
|
||||
function enableCrypto($enabled, $type)
|
||||
{
|
||||
if (version_compare(phpversion(), "5.1.0", ">=")) {
|
||||
if (!is_resource($this->fp)) {
|
||||
return $this->raiseError('not connected');
|
||||
}
|
||||
return @stream_socket_enable_crypto($this->fp, $enabled, $type);
|
||||
} else {
|
||||
$msg = 'Net_Socket::enableCrypto() requires php version >= 5.1.0';
|
||||
return $this->raiseError($msg);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user