code:
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4 foldmethod=marker: */
/**
 * PEAR_Exception
 *
 * PHP versions 4 and 5
 *
 * @category   pear
 * @package    PEAR
 * @author     Tomas V. V. Cox <cox@idecnet.com>
 * @author     Hans Lellelid <hans@velum.net>
 * @author     Bertrand Mansion <bmansion@mamasam.com>
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2009 The Authors
 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
 * @version    CVS: $Id: Exception.php 313023 2011-07-06 19:17:11Z dufuz $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 1.3.3
 */

/**
 * Base PEAR_Exception Class
 *
 * 1) Features:
 *
 * - Nestable exceptions (throw new PEAR_Exception($msg, $prev_exception))
 * - Definable triggers, shot when exceptions occur
 * - Pretty and informative error messages
 * - Added more context info available (like class, method or cause)
 * - cause can be a PEAR_Exception or an array of mixed
 *   PEAR_Exceptions/PEAR_ErrorStack warnings
 * - callbacks for specific exception classes and their children
 *
 * 2) Ideas:
 *
 * - Maybe a way to define a 'template' for the output
 *
 * 3) Inherited properties from PHP Exception Class:
 *
 * protected $message
 * protected $code
 * protected $line
 * protected $file
 * private   $trace
 *
 * 4) Inherited methods from PHP Exception Class:
 *
 * __clone
 * __construct
 * getMessage
 * getCode
 * getFile
 * getLine
 * getTraceSafe
 * getTraceSafeAsString
 * __toString
 *
 * 5) Usage example
 *
 *
 *  require_once 'PEAR/Exception.php';
 *
 *  class Test {
 *     function foo() {
 *         throw new PEAR_Exception('Error Message', ERROR_CODE);
 *     }
 *  }
 *
 *  function myLogger($pear_exception) {
 *     echo $pear_exception->getMessage();
 *  }
 *  // each time a exception is thrown the 'myLogger' will be called
 *  // (its use is completely optional)
 *  PEAR_Exception::addObserver('myLogger');
 *  $test = new Test;
 *  try {
 *     $test->foo();
 *  } catch (PEAR_Exception $e) {
 *     print $e;
 *  }
 *
 *
 * @category   pear
 * @package    PEAR
 * @author     Tomas V.V.Cox <cox@idecnet.com>
 * @author     Hans Lellelid <hans@velum.net>
 * @author     Bertrand Mansion <bmansion@mamasam.com>
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2009 The Authors
 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
 * @version    Release: 1.9.4
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 1.3.3
 *
 */
class PEAR_Exception extends Exception
{
    const OBSERVER_PRINT = -2;
    const OBSERVER_TRIGGER = -4;
    const OBSERVER_DIE = -8;
    protected $cause;
    private static $_observers = array();
    private static $_uniqueid = 0;
    private $_trace;

    /**
     * Supported signatures:
     *  - PEAR_Exception(string $message);
     *  - PEAR_Exception(string $message, int $code);
     *  - PEAR_Exception(string $message, Exception $cause);
     *  - PEAR_Exception(string $message, Exception $cause, int $code);
     *  - PEAR_Exception(string $message, PEAR_Error $cause);
     *  - PEAR_Exception(string $message, PEAR_Error $cause, int $code);
     *  - PEAR_Exception(string $message, array $causes);
     *  - PEAR_Exception(string $message, array $causes, int $code);
     * @param string exception message
     * @param int|Exception|PEAR_Error|array|null exception cause
     * @param int|null exception code or null
     */
    public function __construct($message, $p2 = null, $p3 = null)
    {
        if (is_int($p2)) {
            $code = $p2;
            $this->cause = null;
        } elseif (is_object($p2) || is_array($p2)) {
            // using is_object allows both Exception and PEAR_Error
            if (is_object($p2) && !($p2 instanceof Exception)) {
                if (!class_exists('PEAR_Error') || !($p2 instanceof PEAR_Error)) {
                    throw new PEAR_Exception('exception cause must be Exception, ' .
                        'array, or PEAR_Error');
                }
            }
            $code = $p3;
            if (is_array($p2) && isset($p2['message'])) {
                // fix potential problem of passing in a single warning
                $p2 = array($p2);
            }
            $this->cause = $p2;
        } else {
            $code = null;
            $this->cause = null;
        }
        parent::__construct($message, $code);
        $this->signal();
    }

    /**
     * @param mixed $callback  - A valid php callback, see php func is_callable()
     *                         - A PEAR_Exception::OBSERVER_* constant
     *                         - An array(const PEAR_Exception::OBSERVER_*,
     *                           mixed $options)
     * @param string $label    The name of the observer. Use this if you want
     *                         to remove it later with removeObserver()
     */
    public static function addObserver($callback, $label = 'default')
    {
        self::$_observers[$label] = $callback;
    }

    public static function removeObserver($label = 'default')
    {
        unset(self::$_observers[$label]);
    }

    /**
     * @return int unique identifier for an observer
     */
    public static function getUniqueId()
    {
        return self::$_uniqueid++;
    }

    private function signal()
    {
        foreach (self::$_observers as $func) {
            if (is_callable($func)) {
                call_user_func($func, $this);
                continue;
            }
            settype($func, 'array');
            switch ($func[0]) {
                case self::OBSERVER_PRINT :
                    $f = (isset($func[1])) ? $func[1] : '%s';
                    printf($f, $this->getMessage());
                    break;
                case self::OBSERVER_TRIGGER :
                    $f = (isset($func[1])) ? $func[1] : E_USER_NOTICE;
                    trigger_error($this->getMessage(), $f);
                    break;
                case self::OBSERVER_DIE :
                    $f = (isset($func[1])) ? $func[1] : '%s';
                    die(printf($f, $this->getMessage()));
                    break;
                default:
                    trigger_error('invalid observer type', E_USER_WARNING);
            }
        }
    }

    /**
     * Return specific error information that can be used for more detailed
     * error messages or translation.
     *
     * This method may be overridden in child exception classes in order
     * to add functionality not present in PEAR_Exception and is a placeholder
     * to define API
     *
     * The returned array must be an associative array of parameter => value like so:
     *

     * array('name' => $name, 'context' => array(...))
     *

     * @return array
     */
    public function getErrorData()
    {
        return array();
    }

    /**
     * Returns the exception that caused this exception to be thrown
     * @access public
     * @return Exception|array The context of the exception
     */
    public function getCause()
    {
        return $this->cause;
    }

    /**
     * Function must be public to call on caused exceptions
     * @param array
     */
    public function getCauseMessage(&$causes)
    {
        $trace = $this->getTraceSafe();
        $cause = array('class'   => get_class($this),
                       'message' => $this->message,
                       'file' => 'unknown',
                       'line' => 'unknown');
        if (isset($trace[0])) {
            if (isset($trace[0]['file'])) {
                $cause['file'] = $trace[0]['file'];
                $cause['line'] = $trace[0]['line'];
            }
        }
        $causes[] = $cause;
        if ($this->cause instanceof PEAR_Exception) {
            $this->cause->getCauseMessage($causes);
        } elseif ($this->cause instanceof Exception) {
            $causes[] = array('class'   => get_class($this->cause),
                              'message' => $this->cause->getMessage(),
                              'file' => $this->cause->getFile(),
                              'line' => $this->cause->getLine());
        } elseif (class_exists('PEAR_Error') && $this->cause instanceof PEAR_Error) {
            $causes[] = array('class' => get_class($this->cause),
                              'message' => $this->cause->getMessage(),
                              'file' => 'unknown',
                              'line' => 'unknown');
        } elseif (is_array($this->cause)) {
            foreach ($this->cause as $cause) {
                if ($cause instanceof PEAR_Exception) {
                    $cause->getCauseMessage($causes);
                } elseif ($cause instanceof Exception) {
                    $causes[] = array('class'   => get_class($cause),
                                   'message' => $cause->getMessage(),
                                   'file' => $cause->getFile(),
                                   'line' => $cause->getLine());
                } elseif (class_exists('PEAR_Error') && $cause instanceof PEAR_Error) {
                    $causes[] = array('class' => get_class($cause),
                                      'message' => $cause->getMessage(),
                                      'file' => 'unknown',
                                      'line' => 'unknown');
                } elseif (is_array($cause) && isset($cause['message'])) {
                    // PEAR_ErrorStack warning
                    $causes[] = array(
                        'class' => $cause['package'],
                        'message' => $cause['message'],
                        'file' => isset($cause['context']['file']) ?
                                            $cause['context']['file'] :
                                            'unknown',
                        'line' => isset($cause['context']['line']) ?
                                            $cause['context']['line'] :
                                            'unknown',
                    );
                }
            }
        }
    }

    public function getTraceSafe()
    {
        if (!isset($this->_trace)) {
            $this->_trace = $this->getTrace();
            if (empty($this->_trace)) {
                $backtrace = debug_backtrace();
                $this->_trace = array($backtrace[count($backtrace)-1]);
            }
        }
        return $this->_trace;
    }

    public function getErrorClass()
    {
        $trace = $this->getTraceSafe();
        return $trace[0]['class'];
    }

    public function getErrorMethod()
    {
        $trace = $this->getTraceSafe();
        return $trace[0]['function'];
    }

    public function __toString()
    {
        if (isset($_SERVER['REQUEST_URI'])) {
            return $this->toHtml();
        }
        return $this->toText();
    }

    public function toHtml()
    {
        $trace = $this->getTraceSafe();
        $causes = array();
        $this->getCauseMessage($causes);
        $html =  '
' . "\n";
        foreach ($causes as $i => $cause) {
            $html .= '
'
               . str_repeat('-', $i) . ' ' . $cause['class'] . ': '
               . htmlspecialchars($cause['message']) . ' in ' . $cause['file'] . ' '
               . 'on line ' . $cause['line'] . ''
               . "
\n";
        }
        $html .= '
Exception trace
' . "\n"
               . '
#	'
               . 'Function	'
               . 'Location
' . "\n";

        foreach ($trace as $k => $v) {
            $html .= '
' . $k . '	'
                   . '';
            if (!empty($v['class'])) {
                $html .= $v['class'] . $v['type'];
            }
            $html .= $v['function'];
            $args = array();
            if (!empty($v['args'])) {
                foreach ($v['args'] as $arg) {
                    if (is_null($arg)) $args[] = 'null';
                    elseif (is_array($arg)) $args[] = 'Array';
                    elseif (is_object($arg)) $args[] = 'Object('.get_class($arg).')';
                    elseif (is_bool($arg)) $args[] = $arg ? 'true' : 'false';
                    elseif (is_int($arg) || is_double($arg)) $args[] = $arg;
                    else {
                        $arg = (string)$arg;
                        $str = htmlspecialchars(substr($arg, 0, 16));
                        if (strlen($arg) > 16) $str .= '…';
                        $args[] = "'" . $str . "'";
                    }
                }
            }
            $html .= '(' . implode(', ',$args) . ')'
                   . '	'
                   . '' . (isset($v['file']) ? $v['file'] : 'unknown')
                   . ':' . (isset($v['line']) ? $v['line'] : 'unknown')
                   . '
' . "\n";
        }
        $html .= '
' . ($k+1) . '	'
               . '{main}	'
               . '
' . "\n"
               . '
';
        return $html;
    }

    public function toText()
    {
        $causes = array();
        $this->getCauseMessage($causes);
        $causeMsg = '';
        foreach ($causes as $i => $cause) {
            $causeMsg .= str_repeat(' ', $i) . $cause['class'] . ': '
                   . $cause['message'] . ' in ' . $cause['file']
                   . ' on line ' . $cause['line'] . "\n";
        }
        return $causeMsg . $this->getTraceAsString();
    }
}


code:
<?php
if ($skipmsg) {
    $a = &new $ec($code, $mode, $options, $userinfo);
} else {
    $a = &new $ec($message, $code, $mode, $options, $userinfo);
}
?>

repo_name: piratas/tortuga

license: mit


code:
<?php
/**
 * This is only meant for PHP 5 to get rid of certain strict warning
 * that doesn't get hidden since it's in the shutdown function
 */
class PEAR5
{
    /**
    * If you have a class that's mostly/entirely static, and you need static
    * properties, you can use this method to simulate them. Eg. in your method(s)
    * do this: $myVar = &PEAR5::getStaticProperty('myclass', 'myVar');
    * You MUST use a reference, or they will not persist!
    *
    * @access public
    * @param  string $class  The calling classname, to prevent clashes
    * @param  string $var    The variable to retrieve.
    * @return mixed   A reference to the variable. If not set it will be
    *                 auto initialised to NULL.
    */
    static function &getStaticProperty($class, $var)
    {
        static $properties;
        if (!isset($properties[$class])) {
            $properties[$class] = array();
        }

        if (!array_key_exists($var, $properties[$class])) {
            $properties[$class][$var] = null;
        }

        return $properties[$class][$var];
    }
}


code:
--------------------------------------------------------------------
                  The PHP License, version 2.02
Copyright (c) 1999 - 2002 The PHP Group. All rights reserved.
--------------------------------------------------------------------

Redistribution and use in source and binary forms, with or without
modification, is permitted provided that the following conditions
are met:

  1. Redistributions of source code must retain the above copyright
     notice, this list of conditions and the following disclaimer.

  2. 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.

  3. The name "PHP" must not be used to endorse or promote products
     derived from this software without prior permission from the
     PHP Group.  This does not apply to add-on libraries or tools
     that work in conjunction with PHP.  In such a case the PHP
     name may be used to indicate that the product supports PHP.

  4. The PHP Group may publish revised and/or new versions of the
     license from time to time. Each version will be given a
     distinguishing version number.
     Once covered code has been published under a particular version
     of the license, you may always continue to use it under the
     terms of that version. You may also choose to use such covered
     code under the terms of any subsequent version of the license
     published by the PHP Group. No one other than the PHP Group has
     the right to modify the terms applicable to covered code created
     under this License.

  5. Redistributions of any form whatsoever must retain the following
     acknowledgment:
     "This product includes PHP, freely available from
     http://www.php.net/".

  6. The software incorporates the Zend Engine, a product of Zend
     Technologies, Ltd. ("Zend"). The Zend Engine is licensed to the
     PHP Association (pursuant to a grant from Zend that can be
     found at http://www.php.net/license/ZendGrant/) for
     distribution to you under this license agreement, only as a
     part of PHP.  In the event that you separate the Zend Engine
     (or any portion thereof) from the rest of the software, or
     modify the Zend Engine, or any portion thereof, your use of the
     separated or modified Zend Engine software shall not be governed
     by this license, and instead shall be governed by the license
     set forth at http://www.zend.com/license/ZendLicense/.

THIS SOFTWARE IS PROVIDED BY THE PHP DEVELOPMENT TEAM ``AS IS'' AND
ANY EXPRESSED 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 PHP
DEVELOPMENT TEAM OR ITS 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.

--------------------------------------------------------------------

This software consists of voluntary contributions made by many
individuals on behalf of the PHP Group.

The PHP Group can be contacted via Email at group@php.net.

For more information on the PHP Group and the PHP project,
please see <http://www.php.net>.


code:
--------------------------------------------------------------------
                  The PHP License, version 3.01
Copyright (c) 1999 - 2008 The PHP Group. All rights reserved.
--------------------------------------------------------------------

Redistribution and use in source and binary forms, with or without
modification, is permitted provided that the following conditions
are met:

  1. Redistributions of source code must retain the above copyright
     notice, this list of conditions and the following disclaimer.

  2. 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.

  3. The name "PHP" must not be used to endorse or promote products
     derived from this software without prior written permission. For
     written permission, please contact group@php.net.

  4. Products derived from this software may not be called "PHP", nor
     may "PHP" appear in their name, without prior written permission
     from group@php.net.  You may indicate that your software works in
     conjunction with PHP by saying "Foo for PHP" instead of calling
     it "PHP Foo" or "phpfoo"

  5. The PHP Group may publish revised and/or new versions of the
     license from time to time. Each version will be given a
     distinguishing version number.
     Once covered code has been published under a particular version
     of the license, you may always continue to use it under the terms
     of that version. You may also choose to use such covered code
     under the terms of any subsequent version of the license
     published by the PHP Group. No one other than the PHP Group has
     the right to modify the terms applicable to covered code created
     under this License.

  6. Redistributions of any form whatsoever must retain the following
     acknowledgment:
     "This product includes PHP software, freely available from
     <http://www.php.net/software/>".

THIS SOFTWARE IS PROVIDED BY THE PHP DEVELOPMENT TEAM ``AS IS'' AND
ANY EXPRESSED 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 PHP
DEVELOPMENT TEAM OR ITS 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.

--------------------------------------------------------------------

This software consists of voluntary contributions made by many
individuals on behalf of the PHP Group.

The PHP Group can be contacted via Email at group@php.net.

For more information on the PHP Group and the PHP project,
please see <http://www.php.net>.

PHP includes the Zend Engine, freely available at
<http://www.zend.com>.


code:
PHP Markdown
Copyright (c) 2004-2008 Michel Fortin
<http://www.michelf.com/>
All rights reserved.

Based on Markdown
Copyright (c) 2003-2006 John Gruber
<http://daringfireball.net/>
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:

* Redistributions of source code must retain the above copyright notice,
  this list of conditions and the following disclaimer.

* 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.

* Neither the name "Markdown" nor the names of its contributors may
  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.


code:
DO NOT "FIX" CODE IN THIS DIRECTORY.

ONLY UPSTREAM VERSIONS OF SOFTWARE GO IN THIS DIRECTORY.

This directory is provided as a courtesy to our users who might be
unable or unwilling to find and install libraries we depend on.

If we "fix" software in this directory, we hamstring users who do the
right thing and keep a single version of upstream libraries in a
system-wide library. We introduce subtle and maddening bugs where
our code is "accidentally" using the "wrong" library version. We may
unwittingly interfere with other software that depends on the
canonical release versions of those same libraries!

Forking upstream software for trivial reasons makes us bad citizens in
the Open Source community and adds unnecessary heartache for our
users. Don't make us "that" project.

Frequently Asked Questions
--------------------------

Q: What should we do when we find a bug in upstream software?

A: First and foremost, REPORT THE BUG, and if possible send in a patch.

   Watch for a release of the upstream software and integrate with it
   when it's released.

   In the meantime, work around the bug, if at all possible. Usually,
   it's quite possible, if slightly harder or less efficient.

Q: What if the bug can't be worked around?

A: If the upstream developers have accepted a bug patch, it's
   undesirable but acceptable to apply that patch to the library in
   the extlib dir. Ideally, use a release version for upstream or a
   version control system snapshot.

   Note that this is a last resort.

Q: What if upstream is unresponsive or won't accept a patch?

A: Try again.

Q: I tried again, and upstream is still unresponsive and nobody's
   checked on my patch. Now what?

A: If the upstream project is moribund and there's a way to adopt it,
   propose having the GNU social dev team adopt the project. Or, adopt
   it yourself.

Q: What if there's no upstream authority and it can't be adopted?

A: Then we fork it. Make a new name and a new version. Include it in
   lib/ instead of extlib/, and use the GNUsocial_* prefix to change
   the namespace to avoid collisions.

   This is a last resort; consult with the rest of the dev group
   before taking this radical step.

List of external libraries
--------------------------

A number of external PHP libraries are used to provide basic
functionality and optional functionality for your system. For your
convenience, they are available in the "extlib" directory of this
package, and you do not have to download and install them. However,
you may want to keep them up-to-date with the latest upstream version,
and the URLs are listed here for your convenience.

- DB_DataObject http://pear.php.net/package/DB_DataObject
- Validate http://pear.php.net/package/Validate
- OpenID by Janrain, http://janrain.com/openid-enabled/
- PEAR DB. Although this is an older data access system (new
  packages should use PDO), the OpenID libraries depend on PEAR DB
  or MDB2.
- OAuth.php from http://oauth.googlecode.com/svn/code/php/
                            (has been edited to avoid colliding autoload)
- markdown.php from http://michelf.com/projects/php-markdown/
- PEAR Mail, for sending out mail notifications
  http://pear.php.net/package/Mail
- PEAR Net_SMTP, if you use the SMTP factory for notifications
  http://pear.php.net/package/Net_SMTP
- PEAR Net_Socket, if you use the SMTP factory for notifications
  http://pear.php.net/package/Net_Socket
- XMPPHP, the follow-up to Class.Jabber.php. Probably the best XMPP
  library available for PHP. http://xmpphp.googlecode.com/. Note that
  as of this writing the version of this library that is available in
  the extlib directory is *significantly different* from the upstream
  version (patches have been submitted). Upgrading to the upstream
  version may render your GNU social site unable to send or receive XMPP
  messages.
- Facebook library. Used for the Facebook application.
- PEAR Validate is used for URL and email validation.
- Console_GetOpt for parsing command-line options.
- HTTP_Request2, a library for making HTTP requests.
- PEAR Net_URL2 is an HTTP_Request2 dependency.
- Michelf/Markdown.php      Markdown parser library
- Mf2/Parser.php            microformats2 parser library

A design goal of GNU Social is that the basic Web functionality should
work on even the most restrictive commercial hosting services.
However, additional functionality, such as receiving messages by XMPP,
require that you be able to run long-running processes on your account.
In addition, posting by email require that you be able to install a mail
filter in your mail server.

path: extlib/README


code:
<?php
/**
 *
 * Copyright 2005-2006 The Apache Software Foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/* vim: set expandtab tabstop=3 shiftwidth=3: */

require_once 'Stomp/Frame.php';

/**
 * A Stomp Connection
 *
 *
 * @package Stomp
 * @author Hiram Chirino <hiram@hiramchirino.com>
 * @author Dejan Bosanac <dejan@nighttale.net>
 * @author Michael Caplan <mcaplan@labnet.net>
 * @version $Revision: 43 $
 */
class Stomp
{
    /**
     * Perform request synchronously
     *
     * @var boolean
     */
    public $sync = false;

    /**
     * Default prefetch size
     *
     * @var int
     */
	public $prefetchSize = 1;

	/**
     * Client id used for durable subscriptions
     *
     * @var string
     */
	public $clientId = null;

    protected $_brokerUri = null;
    protected $_socket = null;
    protected $_hosts = array();
    protected $_params = array();
    protected $_subscriptions = array();
    protected $_defaultPort = 61613;
    protected $_currentHost = - 1;
    protected $_attempts = 10;
    protected $_username = '';
    protected $_password = '';
    protected $_sessionId;
    protected $_read_timeout_seconds = 60;
    protected $_read_timeout_milliseconds = 0;
    protected $_connect_timeout_seconds = 60;

    /**
     * Constructor
     *
     * @param string $brokerUri Broker URL
     * @throws StompException
     */
    public function __construct ($brokerUri)
    {
        $this->_brokerUri = $brokerUri;
        $this->_init();
    }
    /**
     * Initialize connection
     *
     * @throws StompException
     */
    protected function _init ()
    {
        $pattern = "|^(([a-zA-Z]+)://)+\(*([a-zA-Z0-9\.:/i,-]+)\)*\??([a-zA-Z0-9=]*)$|i";
        if (preg_match($pattern, $this->_brokerUri, $regs)) {
            $scheme = $regs[2];
            $hosts = $regs[3];
            $params = $regs[4];
            if ($scheme != "failover") {
                $this->_processUrl($this->_brokerUri);
            } else {
                $urls = explode(",", $hosts);
                foreach ($urls as $url) {
                    $this->_processUrl($url);
                }
            }
            if ($params != null) {
                parse_str($params, $this->_params);
            }
        } else {
            require_once 'Stomp/Exception.php';
            throw new StompException("Bad Broker URL {$this->_brokerUri}");
        }
    }
    /**
     * Process broker URL
     *
     * @param string $url Broker URL
     * @throws StompException
     * @return boolean
     */
    protected function _processUrl ($url)
    {
        $parsed = parse_url($url);
        if ($parsed) {
            array_push($this->_hosts, array($parsed['host'] , $parsed['port'] , $parsed['scheme']));
        } else {
            require_once 'Stomp/Exception.php';
            throw new StompException("Bad Broker URL $url");
        }
    }
    /**
     * Make socket connection to the server
     *
     * @throws StompException
     */
    protected function _makeConnection ()
    {
        if (count($this->_hosts) == 0) {
            require_once 'Stomp/Exception.php';
            throw new StompException("No broker defined");
        }

        // force disconnect, if previous established connection exists
        $this->disconnect();

        $i = $this->_currentHost;
        $att = 0;
        $connected = false;
        $connect_errno = null;
        $connect_errstr = null;

        while (! $connected && $att ++ < $this->_attempts) {
            if (isset($this->_params['randomize']) && $this->_params['randomize'] == 'true') {
                $i = rand(0, count($this->_hosts) - 1);
            } else {
                $i = ($i + 1) % count($this->_hosts);
            }
            $broker = $this->_hosts[$i];
            $host = $broker[0];
            $port = $broker[1];
            $scheme = $broker[2];
            if ($port == null) {
                $port = $this->_defaultPort;
            }
            if ($this->_socket != null) {
                fclose($this->_socket);
                $this->_socket = null;
            }
            $this->_socket = @fsockopen($scheme . '://' . $host, $port, $connect_errno, $connect_errstr, $this->_connect_timeout_seconds);
            if (!is_resource($this->_socket) && $att >= $this->_attempts && !array_key_exists($i + 1, $this->_hosts)) {
                require_once 'Stomp/Exception.php';
                throw new StompException("Could not connect to $host:$port ($att/{$this->_attempts})");
            } else if (is_resource($this->_socket)) {
                $connected = true;
                $this->_currentHost = $i;
                break;
            }
        }
        if (! $connected) {
            require_once 'Stomp/Exception.php';
            throw new StompException("Could not connect to a broker");
        }
    }
    /**
     * Connect to server
     *
     * @param string $username
     * @param string $password
     * @return boolean
     * @throws StompException
     */
    public function connect ($username = '', $password = '')
    {
        $this->_makeConnection();
        if ($username != '') {
            $this->_username = $username;
        }
        if ($password != '') {
            $this->_password = $password;
        }
		$headers = array('login' => $this->_username , 'passcode' => $this->_password);
		if ($this->clientId != null) {
			$headers["client-id"] = $this->clientId;
		}
		$frame = new StompFrame("CONNECT", $headers);
        $this->_writeFrame($frame);
        $frame = $this->readFrame();
        if ($frame instanceof StompFrame && $frame->command == 'CONNECTED') {
            $this->_sessionId = $frame->headers["session"];
            return true;
        } else {
            require_once 'Stomp/Exception.php';
            if ($frame instanceof StompFrame) {
                throw new StompException("Unexpected command: {$frame->command}", 0, $frame->body);
            } else {
                throw new StompException("Connection not acknowledged");
            }
        }
    }

    /**
     * Check if client session has ben established
     *
     * @return boolean
     */
    public function isConnected ()
    {
        return !empty($this->_sessionId) && is_resource($this->_socket);
    }
    /**
     * Current stomp session ID
     *
     * @return string
     */
    public function getSessionId()
    {
        return $this->_sessionId;
    }
    /**
     * Send a message to a destination in the messaging system
     *
     * @param string $destination Destination queue
     * @param string|StompFrame $msg Message
     * @param array $properties
     * @param boolean $sync Perform request synchronously
     * @return boolean
     */
    public function send ($destination, $msg, $properties = array(), $sync = null)
    {
        if ($msg instanceof StompFrame) {
            $msg->headers['destination'] = $destination;
            if (is_array($properties)) $msg->headers = array_merge($msg->headers, $properties);
            $frame = $msg;
        } else {
            $headers = $properties;
            $headers['destination'] = $destination;
            $frame = new StompFrame('SEND', $headers, $msg);
        }
        $this->_prepareReceipt($frame, $sync);
        $this->_writeFrame($frame);
        return $this->_waitForReceipt($frame, $sync);
    }
    /**
     * Prepair frame receipt
     *
     * @param StompFrame $frame
     * @param boolean $sync
     */
    protected function _prepareReceipt (StompFrame $frame, $sync)
    {
        $receive = $this->sync;
        if ($sync !== null) {
            $receive = $sync;
        }
        if ($receive == true) {
            $frame->headers['receipt'] = md5(microtime());
        }
    }
    /**
     * Wait for receipt
     *
     * @param StompFrame $frame
     * @param boolean $sync
     * @return boolean
     * @throws StompException
     */
    protected function _waitForReceipt (StompFrame $frame, $sync)
    {

        $receive = $this->sync;
        if ($sync !== null) {
            $receive = $sync;
        }
        if ($receive == true) {
            $id = (isset($frame->headers['receipt'])) ? $frame->headers['receipt'] : null;
            if ($id == null) {
                return true;
            }
            $frame = $this->readFrame();
            if ($frame instanceof StompFrame && $frame->command == 'RECEIPT') {
                if ($frame->headers['receipt-id'] == $id) {
                    return true;
                } else {
                    require_once 'Stomp/Exception.php';
                    throw new StompException("Unexpected receipt id {$frame->headers['receipt-id']}", 0, $frame->body);
                }
            } else {
                require_once 'Stomp/Exception.php';
                if ($frame instanceof StompFrame) {
                    throw new StompException("Unexpected command {$frame->command}", 0, $frame->body);
                } else {
                    throw new StompException("Receipt not received");
                }
            }
        }
        return true;
    }
    /**
     * Register to listen to a given destination
     *
     * @param string $destination Destination queue
     * @param array $properties
     * @param boolean $sync Perform request synchronously
     * @return boolean
     * @throws StompException
     */
    public function subscribe ($destination, $properties = null, $sync = null)
    {
        $headers = array('ack' => 'client');
		$headers['activemq.prefetchSize'] = $this->prefetchSize;
		if ($this->clientId != null) {
			$headers["activemq.subcriptionName"] = $this->clientId;
		}
        if (isset($properties)) {
            foreach ($properties as $name => $value) {
                $headers[$name] = $value;
            }
        }
        $headers['destination'] = $destination;
        $frame = new StompFrame('SUBSCRIBE', $headers);
        $this->_prepareReceipt($frame, $sync);
        $this->_writeFrame($frame);
        if ($this->_waitForReceipt($frame, $sync) == true) {
            $this->_subscriptions[$destination] = $properties;
            return true;
        } else {
            return false;
        }
    }
    /**
     * Remove an existing subscription
     *
     * @param string $destination
     * @param array $properties
     * @param boolean $sync Perform request synchronously
     * @return boolean
     * @throws StompException
     */
    public function unsubscribe ($destination, $properties = null, $sync = null)
    {
        $headers = array();
        if (isset($properties)) {
            foreach ($properties as $name => $value) {
                $headers[$name] = $value;
            }
        }
        $headers['destination'] = $destination;
        $frame = new StompFrame('UNSUBSCRIBE', $headers);
        $this->_prepareReceipt($frame, $sync);
        $this->_writeFrame($frame);
        if ($this->_waitForReceipt($frame, $sync) == true) {
            unset($this->_subscriptions[$destination]);
            return true;
        } else {
            return false;
        }
    }
    /**
     * Start a transaction
     *
     * @param string $transactionId
     * @param boolean $sync Perform request synchronously
     * @return boolean
     * @throws StompException
     */
    public function begin ($transactionId = null, $sync = null)
    {
        $headers = array();
        if (isset($transactionId)) {
            $headers['transaction'] = $transactionId;
        }
        $frame = new StompFrame('BEGIN', $headers);
        $this->_prepareReceipt($frame, $sync);
        $this->_writeFrame($frame);
        return $this->_waitForReceipt($frame, $sync);
    }
    /**
     * Commit a transaction in progress
     *
     * @param string $transactionId
     * @param boolean $sync Perform request synchronously
     * @return boolean
     * @throws StompException
     */
    public function commit ($transactionId = null, $sync = null)
    {
        $headers = array();
        if (isset($transactionId)) {
            $headers['transaction'] = $transactionId;
        }
        $frame = new StompFrame('COMMIT', $headers);
        $this->_prepareReceipt($frame, $sync);
        $this->_writeFrame($frame);
        return $this->_waitForReceipt($frame, $sync);
    }
    /**
     * Roll back a transaction in progress
     *
     * @param string $transactionId
     * @param boolean $sync Perform request synchronously
     */
    public function abort ($transactionId = null, $sync = null)
    {
        $headers = array();
        if (isset($transactionId)) {
            $headers['transaction'] = $transactionId;
        }
        $frame = new StompFrame('ABORT', $headers);
        $this->_prepareReceipt($frame, $sync);
        $this->_writeFrame($frame);
        return $this->_waitForReceipt($frame, $sync);
    }
    /**
     * Acknowledge consumption of a message from a subscription
	 * Note: This operation is always asynchronous
     *
     * @param string|StompFrame $messageMessage ID
     * @param string $transactionId
     * @return boolean
     * @throws StompException
     */
    public function ack ($message, $transactionId = null)
    {
        if ($message instanceof StompFrame) {
            $headers = $message->headers;
            if (isset($transactionId)) {
                $headers['transaction'] = $transactionId;
            }
            $frame = new StompFrame('ACK', $headers);
            $this->_writeFrame($frame);
            return true;
        } else {
            $headers = array();
            if (isset($transactionId)) {
                $headers['transaction'] = $transactionId;
            }
            $headers['message-id'] = $message;
            $frame = new StompFrame('ACK', $headers);
            $this->_writeFrame($frame);
            return true;
        }
    }
    /**
     * Graceful disconnect from the server
     *
     */
    public function disconnect ()
    {
		$headers = array();

		if ($this->clientId != null) {
			$headers["client-id"] = $this->clientId;
		}

        if (is_resource($this->_socket)) {
            $this->_writeFrame(new StompFrame('DISCONNECT', $headers));
            fclose($this->_socket);
        }
        $this->_socket = null;
        $this->_sessionId = null;
        $this->_currentHost = -1;
        $this->_subscriptions = array();
        $this->_username = '';
        $this->_password = '';
    }
    /**
     * Write frame to server
     *
     * @param StompFrame $stompFrame
     */
    protected function _writeFrame (StompFrame $stompFrame)
    {
        if (!is_resource($this->_socket)) {
            require_once 'Stomp/Exception.php';
            throw new StompException('Socket connection hasn\'t been established');
        }

        $data = $stompFrame->__toString();
        $r = fwrite($this->_socket, $data, strlen($data));
        if ($r === false || $r == 0) {
            $this->_reconnect();
            $this->_writeFrame($stompFrame);
        }
    }

    /**
     * Set timeout to wait for content to read
     *
     * @param int $seconds_to_wait  Seconds to wait for a frame
     * @param int $milliseconds Milliseconds to wait for a frame
     */
    public function setReadTimeout($seconds, $milliseconds = 0)
    {
        $this->_read_timeout_seconds = $seconds;
        $this->_read_timeout_milliseconds = $milliseconds;
    }

    /**
     * Read response frame from server
     *
     * @return StompFrame False when no frame to read
     */
    public function readFrame ()
    {
        if (!$this->hasFrameToRead()) {
            return false;
        }

        $rb = 1024;
        $data = '';
        $end = false;

        do {
            $read = fread($this->_socket, $rb);
            if ($read === false) {
                $this->_reconnect();
                return $this->readFrame();
            }
            $data .= $read;
            if (strpos($data, "\x00") !== false) {
                $end = true;
                $data = rtrim($data, "\n");
            }
            $len = strlen($data);
        } while ($len < 2 || $end == false);

        list ($header, $body) = explode("\n\n", $data, 2);
        $header = explode("\n", $header);
        $headers = array();
        $command = null;
        foreach ($header as $v) {
            if (isset($command)) {
                list ($name, $value) = explode(':', $v, 2);
                $headers[$name] = $value;
            } else {
                $command = $v;
            }
        }
        $frame = new StompFrame($command, $headers, trim($body));
        if (isset($frame->headers['transformation']) && $frame->headers['transformation'] == 'jms-map-json') {
            require_once 'Stomp/Message/Map.php';
            return new StompMessageMap($frame);
        } else {
            return $frame;
        }
        return $frame;
    }

    /**
     * Check if there is a frame to read
     *
     * @return boolean
     */
    public function hasFrameToRead()
    {
        $read = array($this->_socket);
        $write = null;
        $except = null;

        $has_frame_to_read = @stream_select($read, $write, $except, $this->_read_timeout_seconds, $this->_read_timeout_milliseconds);

        if ($has_frame_to_read !== false)
            $has_frame_to_read = count($read);

        if ($has_frame_to_read === false) {
            throw new StompException('Check failed to determine if the socket is readable');
        } else if ($has_frame_to_read > 0) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * Reconnects and renews subscriptions (if there were any)
     * Call this method when you detect connection problems
     */
    protected function _reconnect ()
    {
        $subscriptions = $this->_subscriptions;

        $this->connect($this->_username, $this->_password);
        foreach ($subscriptions as $dest => $properties) {
            $this->subscribe($dest, $properties);
        }
    }
    /**
     * Graceful object desruction
     *
     */
    public function __destruct()
    {
        $this->disconnect();
    }
}
?>


code:
<?php
/**
 *
 * Copyright 2005-2006 The Apache Software Foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/* vim: set expandtab tabstop=3 shiftwidth=3: */

/**
 * A Stomp Connection
 *
 *
 * @package Stomp
 */
class StompException extends Exception
{
    protected $_details;

    /**
     * Constructor
     *
     * @param string $message Error message
     * @param int $code Error code
     * @param string $details Stomp server error details
     */
    public function __construct($message = null, $code = 0, $details = '')
    {
        $this->_details = $details;

        parent::__construct($message, $code);
    }

    /**
     * Stomp server error details
     *
     * @return string
     */
    public function getDetails()
    {
        return $this->_details;
    }
}
?>


code:
<?php
/**
 *
 * Copyright 2005-2006 The Apache Software Foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/* vim: set expandtab tabstop=3 shiftwidth=3: */

/**
 * Stomp Frames are messages that are sent and received on a stomp connection.
 *
 * @package Stomp
 */
class StompFrame
{
    public $command;
    public $headers = array();
    public $body;

    /**
     * Constructor
     *
     * @param string $command
     * @param array $headers
     * @param string $body
     */
    public function __construct ($command = null, $headers = null, $body = null)
    {
        $this->_init($command, $headers, $body);
    }

    protected function _init ($command = null, $headers = null, $body = null)
    {
        $this->command = $command;
        if ($headers != null) {
            $this->headers = $headers;
        }
        $this->body = $body;

        if ($this->command == 'ERROR') {
            require_once 'Exception.php';
            throw new StompException($this->headers['message'], 0, $this->body);
        }
    }

    /**
     * Convert frame to transportable string
     *
     * @return string
     */
    public function __toString()
    {
        $data = $this->command . "\n";

        foreach ($this->headers as $name => $value) {
            $data .= $name . ": " . $value . "\n";
        }

        $data .= "\n";
        $data .= $this->body;
        return $data .= "\x00";
    }
}
?>


code:
<?php
/**
 *
 * Copyright 2005-2006 The Apache Software Foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/* vim: set expandtab tabstop=3 shiftwidth=3: */

require_once 'Stomp/Frame.php';

/**
 * Basic text stomp message
 *
 * @package Stomp
 */
class StompMessage extends StompFrame
{
    public function __construct ($body, $headers = null)
    {
        $this->_init("SEND", $headers, $body);
    }
}
?>


code:
<?php
/**
 *
 * Copyright 2005-2006 The Apache Software Foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/* vim: set expandtab tabstop=3 shiftwidth=3: */

require_once 'Stomp/Message.php';

/**
 * Message that contains a stream of uninterpreted bytes
 *
 * @package Stomp
 * @author Dejan Bosanac <dejan@nighttale.net>
 * @version $Revision: 23 $
 */
class Stomp_Message_Bytes extends Stomp_Message
{
    /**
     * Constructor
     *
     * @param string $body
     * @param array $headers
     */
    function __construct ($body, $headers = null)
    {
        $this->_init("SEND", $headers, $body);
        if ($this->headers == null) {
            $this->headers = array();
        }
        $this->headers['content-length'] = count($body);
    }
}
?>


code:
<?php
/**
 *
 * Copyright 2005-2006 The Apache Software Foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/* vim: set expandtab tabstop=3 shiftwidth=3: */

require_once 'Stomp/Message.php';

/**
 * Message that contains a set of name-value pairs
 *
 * @package Stomp
 */
class StompMessageMap extends StompMessage
{
    public $map;

    /**
     * Constructor
     *
     * @param StompFrame|string $msg
     * @param array $headers
     */
    function __construct ($msg, $headers = null)
    {
        if ($msg instanceof StompFrame) {
            $this->_init($msg->command, $msg->headers, $msg->body);
            $this->map = json_decode($msg->body, true);
        } else {
            $this->_init("SEND", $headers, $msg);
            if ($this->headers == null) {
                $this->headers = array();
            }
            $this->headers['transformation'] = 'jms-map-json';
            $this->body = json_encode($msg);
        }
    }
}
?>


code:
<?php
/**
 * File/Directory manipulation
 *
 * PHP versions 4 and 5
 *
 * @category   pear
 * @package    System
 * @author     Tomas V.V.Cox <cox@idecnet.com>
 * @copyright  1997-2009 The Authors
 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
 * @version    CVS: $Id: System.php 313024 2011-07-06 19:51:24Z dufuz $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 0.1
 */

/**
 * base class
 */
require_once 'PEAR.php';
require_once 'Console/Getopt.php';

$GLOBALS['_System_temp_files'] = array();

/**
* System offers cross plattform compatible system functions
*
* Static functions for different operations. Should work under
* Unix and Windows. The names and usage has been taken from its respectively
* GNU commands. The functions will return (bool) false on error and will
* trigger the error with the PHP trigger_error() function (you can silence
* the error by prefixing a '@' sign after the function call, but this
* is not recommended practice.  Instead use an error handler with
* {@link set_error_handler()}).
*
* Documentation on this class you can find in:
* http://pear.php.net/manual/
*
* Example usage:
* if (!@System::rm('-r file1 dir1')) {
*    print "could not delete file1 or dir1";
* }
*
* In case you need to to pass file names with spaces,
* pass the params as an array:
*
* System::rm(array('-r', $file1, $dir1));
*
* @category   pear
* @package    System
* @author     Tomas V.V. Cox <cox@idecnet.com>
* @copyright  1997-2006 The PHP Group
* @license    http://opensource.org/licenses/bsd-license.php New BSD License
* @version    Release: 1.9.4
* @link       http://pear.php.net/package/PEAR
* @since      Class available since Release 0.1
* @static
*/
class System
{
    /**
     * returns the commandline arguments of a function
     *
     * @param    string  $argv           the commandline
     * @param    string  $short_options  the allowed option short-tags
     * @param    string  $long_options   the allowed option long-tags
     * @return   array   the given options and there values
     * @static
     * @access private
     */
    function _parseArgs($argv, $short_options, $long_options = null)
    {
        if (!is_array($argv) && $argv !== null) {
            // Find all items, quoted or otherwise
            preg_match_all("/(?:[\"'])(.*?)(?:['\"])|([^\s]+)/", $argv, $av);
            $argv = $av[1];
            foreach ($av[2] as $k => $a) {
                if (empty($a)) {
                    continue;
                }
                $argv[$k] = trim($a) ;
            }
        }
        return Console_Getopt::getopt2($argv, $short_options, $long_options);
    }

    /**
     * Output errors with PHP trigger_error(). You can silence the errors
     * with prefixing a "@" sign to the function call: @System::mkdir(..);
     *
     * @param mixed $error a PEAR error or a string with the error message
     * @return bool false
     * @static
     * @access private
     */
    function raiseError($error)
    {
        if (PEAR::isError($error)) {
            $error = $error->getMessage();
        }
        trigger_error($error, E_USER_WARNING);
        return false;
    }

    /**
     * Creates a nested array representing the structure of a directory
     *
     * System::_dirToStruct('dir1', 0) =>
     *   Array
     *    (
     *    [dirs] => Array
     *        (
     *            [0] => dir1
     *        )
     *
     *    [files] => Array
     *        (
     *            [0] => dir1/file2
     *            [1] => dir1/file3
     *        )
     *    )
     * @param    string  $sPath      Name of the directory
     * @param    integer $maxinst    max. deep of the lookup
     * @param    integer $aktinst    starting deep of the lookup
     * @param    bool    $silent     if true, do not emit errors.
     * @return   array   the structure of the dir
     * @static
     * @access   private
     */
    function _dirToStruct($sPath, $maxinst, $aktinst = 0, $silent = false)
    {
        $struct = array('dirs' => array(), 'files' => array());
        if (($dir = @opendir($sPath)) === false) {
            if (!$silent) {
                System::raiseError("Could not open dir $sPath");
            }
            return $struct; // XXX could not open error
        }

        $struct['dirs'][] = $sPath = realpath($sPath); // XXX don't add if '.' or '..' ?
        $list = array();
        while (false !== ($file = readdir($dir))) {
            if ($file != '.' && $file != '..') {
                $list[] = $file;
            }
        }

        closedir($dir);
        natsort($list);
        if ($aktinst < $maxinst || $maxinst == 0) {
            foreach ($list as $val) {
                $path = $sPath . DIRECTORY_SEPARATOR . $val;
                if (is_dir($path) && !is_link($path)) {
                    $tmp    = System::_dirToStruct($path, $maxinst, $aktinst+1, $silent);
                    $struct = array_merge_recursive($struct, $tmp);
                } else {
                    $struct['files'][] = $path;
                }
            }
        }

        return $struct;
    }

    /**
     * Creates a nested array representing the structure of a directory and files
     *
     * @param    array $files Array listing files and dirs
     * @return   array
     * @static
     * @see System::_dirToStruct()
     */
    function _multipleToStruct($files)
    {
        $struct = array('dirs' => array(), 'files' => array());
        settype($files, 'array');
        foreach ($files as $file) {
            if (is_dir($file) && !is_link($file)) {
                $tmp    = System::_dirToStruct($file, 0);
                $struct = array_merge_recursive($tmp, $struct);
            } else {
                if (!in_array($file, $struct['files'])) {
                    $struct['files'][] = $file;
                }
            }
        }
        return $struct;
    }

    /**
     * The rm command for removing files.
     * Supports multiple files and dirs and also recursive deletes
     *
     * @param    string  $args   the arguments for rm
     * @return   mixed   PEAR_Error or true for success
     * @static
     * @access   public
     */
    function rm($args)
    {
        $opts = System::_parseArgs($args, 'rf'); // "f" does nothing but I like it :-)
        if (PEAR::isError($opts)) {
            return System::raiseError($opts);
        }
        foreach ($opts[0] as $opt) {
            if ($opt[0] == 'r') {
                $do_recursive = true;
            }
        }
        $ret = true;
        if (isset($do_recursive)) {
            $struct = System::_multipleToStruct($opts[1]);
            foreach ($struct['files'] as $file) {
                if (!@unlink($file)) {
                    $ret = false;
                }
            }

            rsort($struct['dirs']);
            foreach ($struct['dirs'] as $dir) {
                if (!@rmdir($dir)) {
                    $ret = false;
                }
            }
        } else {
            foreach ($opts[1] as $file) {
                $delete = (is_dir($file)) ? 'rmdir' : 'unlink';
                if (!@$delete($file)) {
                    $ret = false;
                }
            }
        }
        return $ret;
    }

    /**
     * Make directories.
     *
     * The -p option will create parent directories
     * @param    string  $args    the name of the director(y|ies) to create
     * @return   bool    True for success
     * @static
     * @access   public
     */
    function mkDir($args)
    {
        $opts = System::_parseArgs($args, 'pm:');
        if (PEAR::isError($opts)) {
            return System::raiseError($opts);
        }

        $mode = 0777; // default mode
        foreach ($opts[0] as $opt) {
            if ($opt[0] == 'p') {
                $create_parents = true;
            } elseif ($opt[0] == 'm') {
                // if the mode is clearly an octal number (starts with 0)
                // convert it to decimal
                if (strlen($opt[1]) && $opt[1]{0} == '0') {
                    $opt[1] = octdec($opt[1]);
                } else {
                    // convert to int
                    $opt[1] += 0;
                }
                $mode = $opt[1];
            }
        }

        $ret = true;
        if (isset($create_parents)) {
            foreach ($opts[1] as $dir) {
                $dirstack = array();
                while ((!file_exists($dir) || !is_dir($dir)) &&
                        $dir != DIRECTORY_SEPARATOR) {
                    array_unshift($dirstack, $dir);
                    $dir = dirname($dir);
                }

                while ($newdir = array_shift($dirstack)) {
                    if (!is_writeable(dirname($newdir))) {
                        $ret = false;
                        break;
                    }

                    if (!mkdir($newdir, $mode)) {
                        $ret = false;
                    }
                }
            }
        } else {
            foreach($opts[1] as $dir) {
                if ((@file_exists($dir) || !is_dir($dir)) && !mkdir($dir, $mode)) {
                    $ret = false;
                }
            }
        }

        return $ret;
    }

    /**
     * Concatenate files
     *
     * Usage:
     * 1) $var = System::cat('sample.txt test.txt');
     * 2) System::cat('sample.txt test.txt > final.txt');
     * 3) System::cat('sample.txt test.txt >> final.txt');
     *
     * Note: as the class use fopen, urls should work also (test that)
     *
     * @param    string  $args   the arguments
     * @return   boolean true on success
     * @static
     * @access   public
     */
    function &cat($args)
    {
        $ret = null;
        $files = array();
        if (!is_array($args)) {
            $args = preg_split('/\s+/', $args, -1, PREG_SPLIT_NO_EMPTY);
        }

        $count_args = count($args);
        for ($i = 0; $i < $count_args; $i++) {
            if ($args[$i] == '>') {
                $mode = 'wb';
                $outputfile = $args[$i+1];
                break;
            } elseif ($args[$i] == '>>') {
                $mode = 'ab+';
                $outputfile = $args[$i+1];
                break;
            } else {
                $files[] = $args[$i];
            }
        }
        $outputfd = false;
        if (isset($mode)) {
            if (!$outputfd = fopen($outputfile, $mode)) {
                $err = System::raiseError("Could not open $outputfile");
                return $err;
            }
            $ret = true;
        }
        foreach ($files as $file) {
            if (!$fd = fopen($file, 'r')) {
                System::raiseError("Could not open $file");
                continue;
            }
            while ($cont = fread($fd, 2048)) {
                if (is_resource($outputfd)) {
                    fwrite($outputfd, $cont);
                } else {
                    $ret .= $cont;
                }
            }
            fclose($fd);
        }
        if (is_resource($outputfd)) {
            fclose($outputfd);
        }
        return $ret;
    }

    /**
     * Creates temporary files or directories. This function will remove
     * the created files when the scripts finish its execution.
     *
     * Usage:
     *   1) $tempfile = System::mktemp("prefix");
     *   2) $tempdir  = System::mktemp("-d prefix");
     *   3) $tempfile = System::mktemp();
     *   4) $tempfile = System::mktemp("-t /var/tmp prefix");
     *
     * prefix -> The string that will be prepended to the temp name
     *           (defaults to "tmp").
     * -d     -> A temporary dir will be created instead of a file.
     * -t     -> The target dir where the temporary (file|dir) will be created. If
     *           this param is missing by default the env vars TMP on Windows or
     *           TMPDIR in Unix will be used. If these vars are also missing
     *           c:\windows\temp or /tmp will be used.
     *
     * @param   string  $args  The arguments
     * @return  mixed   the full path of the created (file|dir) or false
     * @see System::tmpdir()
     * @static
     * @access  public
     */
    function mktemp($args = null)
    {
        static $first_time = true;
        $opts = System::_parseArgs($args, 't:d');
        if (PEAR::isError($opts)) {
            return System::raiseError($opts);
        }

        foreach ($opts[0] as $opt) {
            if ($opt[0] == 'd') {
                $tmp_is_dir = true;
            } elseif ($opt[0] == 't') {
                $tmpdir = $opt[1];
            }
        }

        $prefix = (isset($opts[1][0])) ? $opts[1][0] : 'tmp';
        if (!isset($tmpdir)) {
            $tmpdir = System::tmpdir();
        }

        if (!System::mkDir(array('-p', $tmpdir))) {
            return false;
        }

        $tmp = tempnam($tmpdir, $prefix);
        if (isset($tmp_is_dir)) {
            unlink($tmp); // be careful possible race condition here
            if (!mkdir($tmp, 0700)) {
                return System::raiseError("Unable to create temporary directory $tmpdir");
            }
        }

        $GLOBALS['_System_temp_files'][] = $tmp;
        if (isset($tmp_is_dir)) {
            //$GLOBALS['_System_temp_files'][] = dirname($tmp);
        }

        if ($first_time) {
            PEAR::registerShutdownFunc(array('System', '_removeTmpFiles'));
            $first_time = false;
        }

        return $tmp;
    }

    /**
     * Remove temporary files created my mkTemp. This function is executed
     * at script shutdown time
     *
     * @static
     * @access private
     */
    function _removeTmpFiles()
    {
        if (count($GLOBALS['_System_temp_files'])) {
            $delete = $GLOBALS['_System_temp_files'];
            array_unshift($delete, '-r');
            System::rm($delete);
            $GLOBALS['_System_temp_files'] = array();
        }
    }

    /**
     * Get the path of the temporal directory set in the system
     * by looking in its environments variables.
     * Note: php.ini-recommended removes the "E" from the variables_order setting,
     * making unavaible the $_ENV array, that s why we do tests with _ENV
     *
     * @static
     * @return string The temporary directory on the system
     */
    function tmpdir()
    {
        if (OS_WINDOWS) {
            if ($var = isset($_ENV['TMP']) ? $_ENV['TMP'] : getenv('TMP')) {
                return $var;
            }
            if ($var = isset($_ENV['TEMP']) ? $_ENV['TEMP'] : getenv('TEMP')) {
                return $var;
            }
            if ($var = isset($_ENV['USERPROFILE']) ? $_ENV['USERPROFILE'] : getenv('USERPROFILE')) {
                return $var;
            }
            if ($var = isset($_ENV['windir']) ? $_ENV['windir'] : getenv('windir')) {
                return $var;
            }
            return getenv('SystemRoot') . '\temp';
        }
        if ($var = isset($_ENV['TMPDIR']) ? $_ENV['TMPDIR'] : getenv('TMPDIR')) {
            return $var;
        }
        return realpath('/tmp');
    }

    /**
     * The "which" command (show the full path of a command)
     *
     * @param string $program The command to search for
     * @param mixed  $fallback Value to return if $program is not found
     *
     * @return mixed A string with the full path or false if not found
     * @static
     * @author Stig Bakken <ssb@php.net>
     */
    function which($program, $fallback = false)
    {
        // enforce API
        if (!is_string($program) || '' == $program) {
            return $fallback;
        }

        // full path given
        if (basename($program) != $program) {
            $path_elements[] = dirname($program);
            $program = basename($program);
        } else {
            // Honor safe mode
            if (!ini_get('safe_mode') || !$path = ini_get('safe_mode_exec_dir')) {
                $path = getenv('PATH');
                if (!$path) {
                    $path = getenv('Path'); // some OSes are just stupid enough to do this
                }
            }
            $path_elements = explode(PATH_SEPARATOR, $path);
        }

        if (OS_WINDOWS) {
            $exe_suffixes = getenv('PATHEXT')
                                ? explode(PATH_SEPARATOR, getenv('PATHEXT'))
                                : array('.exe','.bat','.cmd','.com');
            // allow passing a command.exe param
            if (strpos($program, '.') !== false) {
                array_unshift($exe_suffixes, '');
            }
            // is_executable() is not available on windows for PHP4
            $pear_is_executable = (function_exists('is_executable')) ? 'is_executable' : 'is_file';
        } else {
            $exe_suffixes = array('');
            $pear_is_executable = 'is_executable';
        }

        foreach ($exe_suffixes as $suff) {
            foreach ($path_elements as $dir) {
                $file = $dir . DIRECTORY_SEPARATOR . $program . $suff;
                if (@$pear_is_executable($file)) {
                    return $file;
                }
            }
        }
        return $fallback;
    }

    /**
     * The "find" command
     *
     * Usage:
     *
     * System::find($dir);
     * System::find("$dir -type d");
     * System::find("$dir -type f");
     * System::find("$dir -name *.php");
     * System::find("$dir -name *.php -name *.htm*");
     * System::find("$dir -maxdepth 1");
     *
     * Params implmented:
     * $dir            -> Start the search at this directory
     * -type d         -> return only directories
     * -type f         -> return only files
     * -maxdepth <n>   -> max depth of recursion
     * -name <pattern> -> search pattern (bash style). Multiple -name param allowed
     *
     * @param  mixed Either array or string with the command line
     * @return array Array of found files
     * @static
     *
     */
    function find($args)
    {
        if (!is_array($args)) {
            $args = preg_split('/\s+/', $args, -1, PREG_SPLIT_NO_EMPTY);
        }
        $dir = realpath(array_shift($args));
        if (!$dir) {
            return array();
        }
        $patterns = array();
        $depth = 0;
        $do_files = $do_dirs = true;
        $args_count = count($args);
        for ($i = 0; $i < $args_count; $i++) {
            switch ($args[$i]) {
                case '-type':
                    if (in_array($args[$i+1], array('d', 'f'))) {
                        if ($args[$i+1] == 'd') {
                            $do_files = false;
                        } else {
                            $do_dirs = false;
                        }
                    }
                    $i++;
                    break;
                case '-name':
                    $name = preg_quote($args[$i+1], '#');
                    // our magic characters ? and * have just been escaped,
                    // so now we change the escaped versions to PCRE operators
                    $name = strtr($name, array('\?' => '.', '\*' => '.*'));
                    $patterns[] = '('.$name.')';
                    $i++;
                    break;
                case '-maxdepth':
                    $depth = $args[$i+1];
                    break;
            }
        }
        $path = System::_dirToStruct($dir, $depth, 0, true);
        if ($do_files && $do_dirs) {
            $files = array_merge($path['files'], $path['dirs']);
        } elseif ($do_dirs) {
            $files = $path['dirs'];
        } else {
            $files = $path['files'];
        }
        if (count($patterns)) {
            $dsq = preg_quote(DIRECTORY_SEPARATOR, '#');
            $pattern = '#(^|'.$dsq.')'.implode('|', $patterns).'($|'.$dsq.')#';
            $ret = array();
            $files_count = count($files);
            for ($i = 0; $i < $files_count; $i++) {
                // only search in the part of the file below the current directory
                $filepart = basename($files[$i]);
                if (preg_match($pattern, $filepart)) {
                    $ret[] = $files[$i];
                }
            }
            return $ret;
        }
        return $files;
    }
}


code:
<?php
// {{{ license

// +----------------------------------------------------------------------+
// | PHP Version 4.0                                                      |
// +----------------------------------------------------------------------+
// | Copyright (c) 1997-2003 The PHP Group                                |
// +----------------------------------------------------------------------+
// | This source file is subject to version 2.02 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.               |
// +----------------------------------------------------------------------+
// | Author: Anders Johannsen <anders@johannsen.com>                      |
// | Author: Dan Allen <dan@mojavelinux.com>
// +----------------------------------------------------------------------+

// $Id: Command.php,v 1.9 2007/04/20 21:08:48 cconstantine Exp $

// }}}
// {{{ includes

require_once 'PEAR.php';
require_once 'System.php';

// }}}
// {{{ constants

define('SYSTEM_COMMAND_OK',                 1);
define('SYSTEM_COMMAND_ERROR',             -1);
define('SYSTEM_COMMAND_NO_SHELL',          -2);
define('SYSTEM_COMMAND_INVALID_SHELL',     -3);
define('SYSTEM_COMMAND_TMPDIR_ERROR',      -4);
define('SYSTEM_COMMAND_INVALID_OPERATOR',  -5);
define('SYSTEM_COMMAND_INVALID_COMMAND',   -6);
define('SYSTEM_COMMAND_OPERATOR_PLACEMENT',-7);
define('SYSTEM_COMMAND_COMMAND_PLACEMENT', -8);
define('SYSTEM_COMMAND_NOHUP_MISSING',     -9);
define('SYSTEM_COMMAND_NO_OUTPUT',        -10);
define('SYSTEM_COMMAND_STDERR',           -11);
define('SYSTEM_COMMAND_NONZERO_EXIT',     -12);

// }}}

// {{{ class System_Command

/**
 * The System_Command:: class implements an abstraction for various ways
 * of executing commands (directly using the backtick operator,
 * as a background task after the script has terminated using
 * register_shutdown_function() or as a detached process using nohup).
 *
 * @author  Anders Johannsen <anders@johannsen.com>
 * @author  Dan Allen <dan@mojavelinux.com>
 * @version $Revision: 1.9 $
 */

// }}}
class System_Command {
    // {{{ properties

    /**
     * Array of settings used when creating the shell command
     *
     * @var array
     * @access private
     */
    var $options = array();

    /**
     * Array of available shells to use to execute the command
     *
     * @var array
     * @access private
     */
    var $shells = array();

    /**
     * Array of available control operators used between commands
     *
     * @var array
     * @access private
     */
    var $controlOperators = array();

    /**
     * The system command to be executed
     *
     * @var string
     * @access private
     */
    var $systemCommand = null;

    /**
     * Previously added part to the command string
     *
     * @var string
     * @access private
     */
    var $previousElement = null;

    /**
     * Directory for writing stderr output
     *
     * @var string
     * @access private
     */
    var $tmpDir = null;

    /**
     * To allow the pear error object to accumulate when building
     * the command, we use the command status to keep track when
     * a pear error is raised
     *
     * @var int
     * @access private
     */
    var $commandStatus = 0;

    /**
     * Hold initialization PEAR_Error
     *
     * @var object
     * @access private
     **/
    var $_initError = null;

    // }}}
    // {{{ constructor

    /**
     * Class constructor
     *
     * Defines all necessary constants and sets defaults
     *
     * @access public
     */
    function System_Command($in_shell = null)
    {
        // Defining constants
        $this->options = array(
            'SEQUENCE'   => true,
            'SHUTDOWN'   => false,
            'SHELL'      => $this->which($in_shell),
            'OUTPUT'     => true,
            'NOHUP'      => false,
            'BACKGROUND' => false,
            'STDERR'     => false
        );

        // prepare the available control operators
        $this->controlOperators = array(
            'PIPE'  => '|',
            'AND'   => '&&',
            'OR'    => '||',
            'GROUP' => ';',
            'LFIFO' => '<',
            'RFIFO' => '>',
        );

        // List of allowed/available shells
        $this->shells = array(
            'sh',
            'bash',
            'zsh',
            'tcsh',
            'csh',
            'ash',
            'sash',
            'esh',
            'ksh'
        );

        // Find the first available shell
        if (empty($this->options['SHELL'])) {
            foreach ($this->shells as $shell) {
                if ($this->options['SHELL'] = $this->which($shell)) {
                    break;
                }
            }

            // see if we still have no shell
            if (empty($this->options['SHELL'])) {
            	$this->_initError =& PEAR::raiseError(null, SYSTEM_COMMAND_NO_SHELL, null, E_USER_WARNING, null, 'System_Command_Error', true);
                return;
            }
        }

        // Caputre a temporary directory for capturing stderr from commands
        $this->tmpDir = System::tmpdir();
        if (!System::mkDir("-p {$this->tmpDir}")) {
            $this->_initError =& PEAR::raiseError(null, SYSTEM_COMMAND_TMPDIR_ERROR, null, E_USER_WARNING, null, 'System_Command_Error', true);
            return;
        }
    }

    // }}}
    // {{{ setOption()

    /**
     * Sets the value for an option. Each option should be set to true
     * or false; except the 'SHELL' option which should be a string
     * naming a shell. The options are:
     *
     * 'SEQUENCE'   Allow a sequence command or not (right now this is always on);
     *
     * 'SHUTDOWN'   Execute commands via a shutdown function;
     *
     * 'SHELL'      Path to shell;
     *
     * 'OUTPUT'     Output stdout from process;
     *
     * 'NOHUP'      Use nohup to detach process;
     *
     * 'BACKGROUND' Run as a background process with &;
     *
     * 'STDERR'     Output on stderr will raise an error, even if
     *              the command's exit value is zero. The output from
     *              stderr can be retrieved using the getDebugInfo()
     *              method of the Pear_ERROR object returned by
     *              execute().;
     *
     * @param string $in_option is a case-sensitive string,
     *                          corresponding to the option
     *                          that should be changed
     * @param mixed $in_setting is the new value for the option
     * @access public
     * @return bool true if succes, else false
     */
    function setOption($in_option, $in_setting)
    {
    	if ($this->_initError) {
            return $this->_initError;
        }

        $option = strtoupper($in_option);

        if (!isset($this->options[$option])) {
            PEAR::raiseError(null, SYSTEM_COMMAND_ERROR, null, E_USER_NOTICE, null, 'System_Command_Error', true);
            return false;
        }

        switch ($option) {
            case 'OUTPUT':
            case 'SHUTDOWN':
            case 'SEQUENCE':
            case 'BACKGROUND':
            case 'STDERR':
                $this->options[$option] = !empty($in_setting);
                return true;
            break;

            case 'SHELL':
                if (($shell = $this->which($in_setting)) !== false) {
                    $this->options[$option] = $shell;
                    return true;
                }
                else {
                    PEAR::raiseError(null, SYSTEM_COMMAND_NO_SHELL, null, E_USER_NOTICE, $in_setting, 'System_Command_Error', true);
                    return false;
                }
            break;

            case 'NOHUP':
                if (empty($in_setting)) {
                    $this->options[$option] = false;
                }
                else if ($location = $this->which('nohup')) {
                    $this->options[$option] = $location;
                }
                else {
                    PEAR::raiseError(null, SYSTEM_COMMAND_NOHUP_MISSING, null, E_USER_NOTICE, null, 'System_Command_Error', true);
                    return false;
                }
            break;
        }
    }

    // }}}
    // {{{ pushCommand()

    /**
     * Used to push a command onto the running command to be executed
     *
     * @param  string $in_command binary to be run
     * @param  string $in_argument either an option or argument value, to be handled appropriately
     * @param  string $in_argument
     * @param  ...
     *
     * @access public
     * @return boolean true on success {or System_Command_Error Exception}
     */
    function pushCommand($in_command)
    {
    	if ($this->_initError) {
            return $this->_initError;
        }

        if (!is_null($this->previousElement) && !in_array($this->previousElement, $this->controlOperators)) {
            $this->commandStatus = -1;
            $error = PEAR::raiseError(null, SYSTEM_COMMAND_COMMAND_PLACEMENT, null, E_USER_WARNING, null, 'System_Command_Error', true);
        }

        // check for error here
        $command = escapeshellcmd($this->which($in_command));
        if ($command === false) {
            $error = PEAR::raiseError(null, SYSTEM_COMMAND_INVALID_COMMAND, null, E_USER_WARNING, null, 'System_Command_Error', true);
        }

        $argv = func_get_args();
        array_shift($argv);
        foreach($argv as $arg) {
            if (strpos($arg, '-') === 0) {
                $command .= ' ' . $arg;
            }
            elseif ($arg != '') {
                $command .= ' ' . escapeshellarg($arg);
            }
        }

        $this->previousElement = $command;
        $this->systemCommand .= $command;

        return isset($error) ? $error : true;
    }

    // }}}
    // {{{ pushOperator()

    /**
     * Used to push an operator onto the running command to be executed
     *
     * @param  string $in_operator Either string reprentation of operator or system character
     *
     * @access public
     * @return boolean true on success {or System_Command_Error Exception}
     */
    function pushOperator($in_operator)
    {
    	if ($this->_initError) {
            return $this->_initError;
        }

        $operator = isset($this->controlOperators[$in_operator]) ? $this->controlOperators[$in_operator] : $in_operator;

        if (is_null($this->previousElement) || in_array($this->previousElement, $this->controlOperators)) {
            $this->commandStatus = -1;
            $error = PEAR::raiseError(null, SYSTEM_COMMAND_OPERATOR_PLACEMENT, null, E_USER_WARNING, null, 'System_Command_Error', true);
        }
        elseif (!in_array($operator, $this->controlOperators)) {
            $this->commandStatus = -1;
            $error = PEAR::raiseError(null, SYSTEM_COMMAND_INVALID_OPERATOR, null, E_USER_WARNING, $operator, 'System_Command_Error', true);
        }

        $this->previousElement = $operator;
        $this->systemCommand .= ' ' . $operator . ' ';
        return isset($error) ? $error : true;
    }

    // }}}
    // {{{ execute()

    /**
     * Executes the code according to given options
     *
     * @return bool true if success {or System_Command_Exception}
     *
     * @access public
     */
    function execute()
    {
    	if ($this->_initError) {
            return $this->_initError;
        }

        // if the command is empty or if the last element was a control operator, we can't continue
        if (is_null($this->previousElement) || $this->commandStatus == -1 || in_array($this->previousElement, $this->controlOperators)) {
            return PEAR::raiseError(null, SYSTEM_COMMAND_INVALID_COMMAND, null, E_USER_WARNING, $this->systemCommand, 'System_Command_Error', true);
        }

        // Warning about impossible mix of options
        if (!empty($this->options['OUTPUT'])) {
            if (!empty($this->options['SHUTDOWN']) || !empty($this->options['NOHUP'])) {
                return PEAR::raiseError(null, SYSTEM_COMMAND_NO_OUTPUT, null, E_USER_WARNING, null, 'System_Command_Error', true);
            }
        }

        // if this is not going to stdout, then redirect to /dev/null
        if (empty($this->options['OUTPUT'])) {
            $this->systemCommand .= ' >/dev/null';
        }

        $suffix = '';
        // run a command immune to hangups, with output to a non-tty
        if (!empty($this->options['NOHUP'])) {
            $this->systemCommand = $this->options['NOHUP'] . $this->systemCommand;
        }
        // run a background process (only if not nohup)
        elseif (!empty($this->options['BACKGROUND'])) {
            $suffix = ' &';
        }

        // Register to be run on shutdown
        if (!empty($this->options['SHUTDOWN'])) {
            $line = "system(\"{$this->systemCommand}$suffix\");";
            $function = create_function('', $line);
            register_shutdown_function($function);
            return true;
        }
        else {
            // send stderr to a file so that we can reap the error message
            $tmpFile = tempnam($this->tmpDir, 'System_Command-');
            $this->systemCommand .= ' 2>' . $tmpFile . $suffix;
            $shellPipe = $this->which('echo') . ' ' . escapeshellarg($this->systemCommand) . ' | ' . $this->options['SHELL'];
            exec($shellPipe, $result, $returnVal);

            if ($returnVal !== 0) {
                // command returned nonzero; that's always an error
                $return = PEAR::raiseError(null, SYSTEM_COMMAND_NONZERO_EXIT, null, E_USER_WARNING, null, 'System_Command_Error', true);
            }
            else if (!$this->options['STDERR']) {
                // caller does not care about stderr; return success
                $return = implode("\n", $result);
            }
            else {
                // our caller cares about stderr; check stderr output
                clearstatcache();
                if (filesize($tmpFile) > 0) {
                    // the command actually wrote to stderr
                    $stderr_output = file_get_contents($tmpFile);
                    $return = PEAR::raiseError(null, SYSTEM_COMMAND_STDERR, null, E_USER_WARNING, $stderr_output, 'System_Command_Error', true);
                } else {
                    // total success; return stdout gathered by exec()
                    $return = implode("\n", $result);
                }
            }

            unlink($tmpFile);
            return $return;
        }
    }

    // }}}
    // {{{ which()

    /**
     * Functionality similiar to unix 'which'. Searches the path
     * for the specified program.
     *
     * @param $cmd name of the executable to search for
     *
     * @access private
     * @return string returns the full path if found, false if not
     */
    function which($in_cmd)
    {
        // only pass non-empty strings to System::which()
        if (!is_string($in_cmd) || '' === $in_cmd) {
            return(false);
        }

        // explicitly pass false as fallback value
        return System::which($in_cmd, false);
    }

    // }}}
    // {{{ reset()

    /**
     * Prepare for a new command to be built
     *
     * @access public
     * @return void
     */
    function reset()
    {
        $this->previousElement = null;
        $this->systemCommand = null;
        $this->commandStatus = 0;
    }

    // }}}
    // {{{ errorMessage()

    /**
     * Return a textual error message for a System_Command error code
     *
     * @param integer error code
     *
     * @return string error message, or false if the error code was
     * not recognized
     */
    function errorMessage($in_value)
    {
        static $errorMessages;
        if (!isset($errorMessages)) {
            $errorMessages = array(
                SYSTEM_COMMAND_OK                     => 'no error',
                SYSTEM_COMMAND_ERROR                  => 'unknown error',
                SYSTEM_COMMAND_NO_SHELL               => 'no shell found',
                SYSTEM_COMMAND_INVALID_SHELL          => 'invalid shell',
                SYSTEM_COMMAND_TMPDIR_ERROR           => 'could not create temporary directory',
                SYSTEM_COMMAND_INVALID_OPERATOR       => 'control operator invalid',
                SYSTEM_COMMAND_INVALID_COMMAND        => 'invalid system command',
                SYSTEM_COMMAND_OPERATOR_PLACEMENT     => 'invalid placement of control operator',
                SYSTEM_COMMAND_COMMAND_PLACEMENT      => 'invalid placement of command',
                SYSTEM_COMMAND_NOHUP_MISSING          => 'nohup not found on system',
                SYSTEM_COMMAND_NO_OUTPUT              => 'output not allowed',
                SYSTEM_COMMAND_STDERR                 => 'command wrote to stderr',
                SYSTEM_COMMAND_NONZERO_EXIT           => 'non-zero exit value from command',
            );
        }

        if (System_Command::isError($in_value)) {
            $in_value = $in_value->getCode();
        }

        return isset($errorMessages[$in_value]) ? $errorMessages[$in_value] : $errorMessages[SYSTEM_COMMAND_ERROR];
    }

    // }}}
    // {{{ isError()

    /**
     * Tell whether a result code from a System_Command method is an error
     *
     * @param int result code
     *
     * @return bool whether $in_value is an error
     *
     * @access public
     */
    function isError($in_value)
    {
        return (is_object($in_value) &&
                (strtolower(get_class($in_value)) == 'system_command_error' ||
                 is_subclass_of($in_value, 'system_command_error')));
    }

    // }}}
}

// {{{ class System_Command_Error

/**
 * System_Command_Error constructor.
 *
 * @param mixed      System_Command error code, or string with error message.
 * @param integer    what "error mode" to operate in
 * @param integer    what error level to use for $mode & PEAR_ERROR_TRIGGER
 * @param mixed      additional debug info, such as the last query
 *
 * @access public
 *
 * @see PEAR_Error
 */

// }}}
class System_Command_Error extends PEAR_Error
{
    // {{{ properties

    /**
     * Message in front of the error message
     * @var string $error_message_prefix
     */
    var $error_message_prefix = 'System_Command Error: ';

    // }}}
    // {{{ constructor

    function System_Command_Error($code = SYSTEM_COMMAND_ERROR, $mode = PEAR_ERROR_RETURN,
              $level = E_USER_NOTICE, $debuginfo = null)
    {
        if (is_int($code)) {
            $this->PEAR_Error(System_Command::errorMessage($code), $code, $mode, $level, $debuginfo);
        } else {
            $this->PEAR_Error("Invalid error code: $code", SYSTEM_COMMAND_ERROR, $mode, $level, $debuginfo);
        }
    }

    // }}}
}
?>