home *** CD-ROM | disk | FTP | other *** search
/ Chip 2004 April / CMCD0404.ISO / Software / Freeware / Programare / groupoffice-com-2.01 / classes / pearTar.class.inc < prev    next >
Text File  |  2004-03-08  |  84KB  |  2,546 lines

  1. <?php
  2. //
  3. // +----------------------------------------------------------------------+
  4. // | PEAR, the PHP Extension and Application Repository                   |
  5. // +----------------------------------------------------------------------+
  6. // | Copyright (c) 1997-2003 The PHP Group                                |
  7. // +----------------------------------------------------------------------+
  8. // | This source file is subject to version 2.0 of the PHP license,       |
  9. // | that is bundled with this package in the file LICENSE, and is        |
  10. // | available through the world-wide-web at the following url:           |
  11. // | http://www.php.net/license/3_0.txt.                                  |
  12. // | If you did not receive a copy of the PHP license and are unable to   |
  13. // | obtain it through the world-wide-web, please send a note to          |
  14. // | license@php.net so we can mail you a copy immediately.               |
  15. // +----------------------------------------------------------------------+
  16. // | Authors: Sterling Hughes <sterling@php.net>                          |
  17. // |          Stig Bakken <ssb@php.net>                                   |
  18. // |          Tomas V.V.Cox <cox@idecnet.com>                             |
  19. // +----------------------------------------------------------------------+
  20. //
  21. // $Id: pearTar.class.inc,v 1.1 2003/12/03 19:46:47 mschering Exp $
  22. //
  23.  
  24. define('PEAR_ERROR_RETURN',     1);
  25. define('PEAR_ERROR_PRINT',      2);
  26. define('PEAR_ERROR_TRIGGER',    4);
  27. define('PEAR_ERROR_DIE',        8);
  28. define('PEAR_ERROR_CALLBACK',  16);
  29. define('PEAR_ERROR_EXCEPTION', 32);
  30. define('PEAR_ZE2', (function_exists('version_compare') &&
  31.                     version_compare(zend_version(), "2-dev", "ge")));
  32.  
  33. if (substr(PHP_OS, 0, 3) == 'WIN') {
  34.     define('OS_WINDOWS', true);
  35.     define('OS_UNIX',    false);
  36.     define('PEAR_OS',    'Windows');
  37. } else {
  38.     define('OS_WINDOWS', false);
  39.     define('OS_UNIX',    true);
  40.     define('PEAR_OS',    'Unix'); // blatant assumption
  41. }
  42.  
  43. $GLOBALS['_PEAR_default_error_mode']     = PEAR_ERROR_RETURN;
  44. $GLOBALS['_PEAR_default_error_options']  = E_USER_NOTICE;
  45. $GLOBALS['_PEAR_destructor_object_list'] = array();
  46. $GLOBALS['_PEAR_shutdown_funcs']         = array();
  47. $GLOBALS['_PEAR_error_handler_stack']    = array();
  48.  
  49. ini_set('track_errors', true);
  50.  
  51. /**
  52.  * Base class for other PEAR classes.  Provides rudimentary
  53.  * emulation of destructors.
  54.  *
  55.  * If you want a destructor in your class, inherit PEAR and make a
  56.  * destructor method called _yourclassname (same name as the
  57.  * constructor, but with a "_" prefix).  Also, in your constructor you
  58.  * have to call the PEAR constructor: $this->PEAR();.
  59.  * The destructor method will be called without parameters.  Note that
  60.  * at in some SAPI implementations (such as Apache), any output during
  61.  * the request shutdown (in which destructors are called) seems to be
  62.  * discarded.  If you need to get any debug information from your
  63.  * destructor, use error_log(), syslog() or something similar.
  64.  *
  65.  * IMPORTANT! To use the emulated destructors you need to create the
  66.  * objects by reference: $obj =& new PEAR_child;
  67.  *
  68.  * @since PHP 4.0.2
  69.  * @author Stig Bakken <ssb@php.net>
  70.  * @see http://pear.php.net/manual/
  71.  */
  72. class PEAR
  73. {
  74.     // {{{ properties
  75.  
  76.     /**
  77.      * Whether to enable internal debug messages.
  78.      *
  79.      * @var     bool
  80.      * @access  private
  81.      */
  82.     var $_debug = false;
  83.  
  84.     /**
  85.      * Default error mode for this object.
  86.      *
  87.      * @var     int
  88.      * @access  private
  89.      */
  90.     var $_default_error_mode = null;
  91.  
  92.     /**
  93.      * Default error options used for this object when error mode
  94.      * is PEAR_ERROR_TRIGGER.
  95.      *
  96.      * @var     int
  97.      * @access  private
  98.      */
  99.     var $_default_error_options = null;
  100.  
  101.     /**
  102.      * Default error handler (callback) for this object, if error mode is
  103.      * PEAR_ERROR_CALLBACK.
  104.      *
  105.      * @var     string
  106.      * @access  private
  107.      */
  108.     var $_default_error_handler = '';
  109.  
  110.     /**
  111.      * Which class to use for error objects.
  112.      *
  113.      * @var     string
  114.      * @access  private
  115.      */
  116.     var $_error_class = 'PEAR_Error';
  117.  
  118.     /**
  119.      * An array of expected errors.
  120.      *
  121.      * @var     array
  122.      * @access  private
  123.      */
  124.     var $_expected_errors = array();
  125.  
  126.     // }}}
  127.  
  128.     // {{{ constructor
  129.  
  130.     /**
  131.      * Constructor.  Registers this object in
  132.      * $_PEAR_destructor_object_list for destructor emulation if a
  133.      * destructor object exists.
  134.      *
  135.      * @param string $error_class  (optional) which class to use for
  136.      *        error objects, defaults to PEAR_Error.
  137.      * @access public
  138.      * @return void
  139.      */
  140.     function PEAR($error_class = null)
  141.     {
  142.         $classname = get_class($this);
  143.         if ($this->_debug) {
  144.             print "PEAR constructor called, class=$classname\n";
  145.         }
  146.         if ($error_class !== null) {
  147.             $this->_error_class = $error_class;
  148.         }
  149.         while ($classname) {
  150.             $destructor = "_$classname";
  151.             if (method_exists($this, $destructor)) {
  152.                 global $_PEAR_destructor_object_list;
  153.                 $_PEAR_destructor_object_list[] = &$this;
  154.                 break;
  155.             } else {
  156.                 $classname = get_parent_class($classname);
  157.             }
  158.         }
  159.     }
  160.  
  161.     // }}}
  162.     // {{{ destructor
  163.  
  164.     /**
  165.      * Destructor (the emulated type of...).  Does nothing right now,
  166.      * but is included for forward compatibility, so subclass
  167.      * destructors should always call it.
  168.      *
  169.      * See the note in the class desciption about output from
  170.      * destructors.
  171.      *
  172.      * @access public
  173.      * @return void
  174.      */
  175.     function _PEAR() {
  176.         if ($this->_debug) {
  177.             printf("PEAR destructor called, class=%s\n", get_class($this));
  178.         }
  179.     }
  180.  
  181.     // }}}
  182.     // {{{ getStaticProperty()
  183.  
  184.     /**
  185.     * If you have a class that's mostly/entirely static, and you need static
  186.     * properties, you can use this method to simulate them. Eg. in your method(s)
  187.     * do this: $myVar = &PEAR::getStaticProperty('myVar');
  188.     * You MUST use a reference, or they will not persist!
  189.     *
  190.     * @access public
  191.     * @param  string $class  The calling classname, to prevent clashes
  192.     * @param  string $var    The variable to retrieve.
  193.     * @return mixed   A reference to the variable. If not set it will be
  194.     *                 auto initialised to NULL.
  195.     */
  196.     function &getStaticProperty($class, $var)
  197.     {
  198.         static $properties;
  199.         return $properties[$class][$var];
  200.     }
  201.  
  202.     // }}}
  203.     // {{{ registerShutdownFunc()
  204.  
  205.     /**
  206.     * Use this function to register a shutdown method for static
  207.     * classes.
  208.     *
  209.     * @access public
  210.     * @param  mixed $func  The function name (or array of class/method) to call
  211.     * @param  mixed $args  The arguments to pass to the function
  212.     * @return void
  213.     */
  214.     function registerShutdownFunc($func, $args = array())
  215.     {
  216.         $GLOBALS['_PEAR_shutdown_funcs'][] = array($func, $args);
  217.     }
  218.  
  219.     // }}}
  220.     // {{{ isError()
  221.  
  222.     /**
  223.      * Tell whether a value is a PEAR error.
  224.      *
  225.      * @param   mixed $data   the value to test
  226.      * @param   int   $code   if $data is an error object, return true
  227.      *                        only if $code is a string and
  228.      *                        $obj->getMessage() == $code or
  229.      *                        $code is an integer and $obj->getCode() == $code
  230.      * @access  public
  231.      * @return  bool    true if parameter is an error
  232.      */
  233.     function isError($data, $code = null)
  234.     {
  235.         if (is_object($data) && (get_class($data) == 'pear_error' ||
  236.                                  is_subclass_of($data, 'pear_error'))) {
  237.             if (is_null($code)) {
  238.                 return true;
  239.             } elseif (is_string($code)) {
  240.                 return $data->getMessage() == $code;
  241.             } else {
  242.                 return $data->getCode() == $code;
  243.             }
  244.         }
  245.         return false;
  246.     }
  247.  
  248.     // }}}
  249.     // {{{ setErrorHandling()
  250.  
  251.     /**
  252.      * Sets how errors generated by this object should be handled.
  253.      * Can be invoked both in objects and statically.  If called
  254.      * statically, setErrorHandling sets the default behaviour for all
  255.      * PEAR objects.  If called in an object, setErrorHandling sets
  256.      * the default behaviour for that object.
  257.      *
  258.      * @param int $mode
  259.      *        One of PEAR_ERROR_RETURN, PEAR_ERROR_PRINT,
  260.      *        PEAR_ERROR_TRIGGER, PEAR_ERROR_DIE,
  261.      *        PEAR_ERROR_CALLBACK or PEAR_ERROR_EXCEPTION.
  262.      *
  263.      * @param mixed $options
  264.      *        When $mode is PEAR_ERROR_TRIGGER, this is the error level (one
  265.      *        of E_USER_NOTICE, E_USER_WARNING or E_USER_ERROR).
  266.      *
  267.      *        When $mode is PEAR_ERROR_CALLBACK, this parameter is expected
  268.      *        to be the callback function or method.  A callback
  269.      *        function is a string with the name of the function, a
  270.      *        callback method is an array of two elements: the element
  271.      *        at index 0 is the object, and the element at index 1 is
  272.      *        the name of the method to call in the object.
  273.      *
  274.      *        When $mode is PEAR_ERROR_PRINT or PEAR_ERROR_DIE, this is
  275.      *        a printf format string used when printing the error
  276.      *        message.
  277.      *
  278.      * @access public
  279.      * @return void
  280.      * @see PEAR_ERROR_RETURN
  281.      * @see PEAR_ERROR_PRINT
  282.      * @see PEAR_ERROR_TRIGGER
  283.      * @see PEAR_ERROR_DIE
  284.      * @see PEAR_ERROR_CALLBACK
  285.      * @see PEAR_ERROR_EXCEPTION
  286.      *
  287.      * @since PHP 4.0.5
  288.      */
  289.  
  290.     function setErrorHandling($mode = null, $options = null)
  291.     {
  292.         if (isset($this)) {
  293.             $setmode     = &$this->_default_error_mode;
  294.             $setoptions  = &$this->_default_error_options;
  295.         } else {
  296.             $setmode     = &$GLOBALS['_PEAR_default_error_mode'];
  297.             $setoptions  = &$GLOBALS['_PEAR_default_error_options'];
  298.         }
  299.  
  300.         switch ($mode) {
  301.             case PEAR_ERROR_RETURN:
  302.             case PEAR_ERROR_PRINT:
  303.             case PEAR_ERROR_TRIGGER:
  304.             case PEAR_ERROR_DIE:
  305.             case PEAR_ERROR_EXCEPTION:
  306.             case null:
  307.                 $setmode = $mode;
  308.                 $setoptions = $options;
  309.                 break;
  310.  
  311.             case PEAR_ERROR_CALLBACK:
  312.                 $setmode = $mode;
  313.                 if ((is_string($options) && function_exists($options)) ||
  314.                     (is_array($options) && method_exists(@$options[0], @$options[1])))
  315.                 {
  316.                     $setoptions = $options;
  317.                 } else {
  318.                     trigger_error("invalid error callback", E_USER_WARNING);
  319.                 }
  320.                 break;
  321.  
  322.             default:
  323.                 trigger_error("invalid error mode", E_USER_WARNING);
  324.                 break;
  325.         }
  326.     }
  327.  
  328.     // }}}
  329.     // {{{ expectError()
  330.  
  331.     /**
  332.      * This method is used to tell which errors you expect to get.
  333.      * Expected errors are always returned with error mode
  334.      * PEAR_ERROR_RETURN.  Expected error codes are stored in a stack,
  335.      * and this method pushes a new element onto it.  The list of
  336.      * expected errors are in effect until they are popped off the
  337.      * stack with the popExpect() method.
  338.      *
  339.      * Note that this method can not be called statically
  340.      *
  341.      * @param mixed $code a single error code or an array of error codes to expect
  342.      *
  343.      * @return int     the new depth of the "expected errors" stack
  344.      * @access public
  345.      */
  346.     function expectError($code = '*')
  347.     {
  348.         if (is_array($code)) {
  349.             array_push($this->_expected_errors, $code);
  350.         } else {
  351.             array_push($this->_expected_errors, array($code));
  352.         }
  353.         return sizeof($this->_expected_errors);
  354.     }
  355.  
  356.     // }}}
  357.     // {{{ popExpect()
  358.  
  359.     /**
  360.      * This method pops one element off the expected error codes
  361.      * stack.
  362.      *
  363.      * @return array   the list of error codes that were popped
  364.      */
  365.     function popExpect()
  366.     {
  367.         return array_pop($this->_expected_errors);
  368.     }
  369.  
  370.     // }}}
  371.     // {{{ _checkDelExpect()
  372.  
  373.     /**
  374.      * This method checks unsets an error code if available
  375.      *
  376.      * @param mixed error code
  377.      * @return bool true if the error code was unset, false otherwise
  378.      * @access private
  379.      * @since PHP 4.3.0
  380.      */
  381.     function _checkDelExpect($error_code)
  382.     {
  383.         $deleted = false;
  384.  
  385.         foreach ($this->_expected_errors AS $key => $error_array) {
  386.             if (in_array($error_code, $error_array)) {
  387.                 unset($this->_expected_errors[$key][array_search($error_code, $error_array)]);
  388.                 $deleted = true;
  389.             }
  390.  
  391.             // clean up empty arrays
  392.             if (0 == count($this->_expected_errors[$key])) {
  393.                 unset($this->_expected_errors[$key]);
  394.             }
  395.         }
  396.         return $deleted;
  397.     }
  398.  
  399.     // }}}
  400.     // {{{ delExpect()
  401.  
  402.     /**
  403.      * This method deletes all occurences of the specified element from
  404.      * the expected error codes stack.
  405.      *
  406.      * @param  mixed $error_code error code that should be deleted
  407.      * @return mixed list of error codes that were deleted or error
  408.      * @access public
  409.      * @since PHP 4.3.0
  410.      */
  411.     function delExpect($error_code)
  412.     {
  413.         $deleted = false;
  414.  
  415.         if ((is_array($error_code) && (0 != count($error_code)))) {
  416.             // $error_code is a non-empty array here;
  417.             // we walk through it trying to unset all
  418.             // values
  419.             foreach($error_code AS $key => $error) {
  420.                 if ($this->_checkDelExpect($error)) {
  421.                     $deleted =  true;
  422.                 } else {
  423.                     $deleted = false;
  424.                 }
  425.             }
  426.             return $deleted ? true : PEAR::raiseError("The expected error you submitted does not exist"); // IMPROVE ME
  427.         } elseif (!empty($error_code)) {
  428.             // $error_code comes alone, trying to unset it
  429.             if ($this->_checkDelExpect($error_code)) {
  430.                 return true;
  431.             } else {
  432.                 return PEAR::raiseError("The expected error you submitted does not exist"); // IMPROVE ME
  433.             }
  434.         } else {
  435.             // $error_code is empty
  436.             return PEAR::raiseError("The expected error you submitted is empty"); // IMPROVE ME
  437.         }
  438.     }
  439.  
  440.     // }}}
  441.     // {{{ raiseError()
  442.  
  443.     /**
  444.      * This method is a wrapper that returns an instance of the
  445.      * configured error class with this object's default error
  446.      * handling applied.  If the $mode and $options parameters are not
  447.      * specified, the object's defaults are used.
  448.      *
  449.      * @param mixed $message a text error message or a PEAR error object
  450.      *
  451.      * @param int $code      a numeric error code (it is up to your class
  452.      *                  to define these if you want to use codes)
  453.      *
  454.      * @param int $mode      One of PEAR_ERROR_RETURN, PEAR_ERROR_PRINT,
  455.      *                  PEAR_ERROR_TRIGGER, PEAR_ERROR_DIE,
  456.      *                  PEAR_ERROR_CALLBACK, PEAR_ERROR_EXCEPTION.
  457.      *
  458.      * @param mixed $options If $mode is PEAR_ERROR_TRIGGER, this parameter
  459.      *                  specifies the PHP-internal error level (one of
  460.      *                  E_USER_NOTICE, E_USER_WARNING or E_USER_ERROR).
  461.      *                  If $mode is PEAR_ERROR_CALLBACK, this
  462.      *                  parameter specifies the callback function or
  463.      *                  method.  In other error modes this parameter
  464.      *                  is ignored.
  465.      *
  466.      * @param string $userinfo If you need to pass along for example debug
  467.      *                  information, this parameter is meant for that.
  468.      *
  469.      * @param string $error_class The returned error object will be
  470.      *                  instantiated from this class, if specified.
  471.      *
  472.      * @param bool $skipmsg If true, raiseError will only pass error codes,
  473.      *                  the error message parameter will be dropped.
  474.      *
  475.      * @access public
  476.      * @return object   a PEAR error object
  477.      * @see PEAR::setErrorHandling
  478.      * @since PHP 4.0.5
  479.      */
  480.     function raiseError($message = null,
  481.                          $code = null,
  482.                          $mode = null,
  483.                          $options = null,
  484.                          $userinfo = null,
  485.                          $error_class = null,
  486.                          $skipmsg = false)
  487.     {
  488.         // The error is yet a PEAR error object
  489.         if (is_object($message)) {
  490.             $code        = $message->getCode();
  491.             $userinfo    = $message->getUserInfo();
  492.             $error_class = $message->getType();
  493.             $message     = $message->getMessage();
  494.         }
  495.  
  496.         if (isset($this) && isset($this->_expected_errors) && sizeof($this->_expected_errors) > 0 && sizeof($exp = end($this->_expected_errors))) {
  497.             if ($exp[0] == "*" ||
  498.                 (is_int(reset($exp)) && in_array($code, $exp)) ||
  499.                 (is_string(reset($exp)) && in_array($message, $exp))) {
  500.                 $mode = PEAR_ERROR_RETURN;
  501.             }
  502.         }
  503.         // No mode given, try global ones
  504.         if ($mode === null) {
  505.             // Class error handler
  506.             if (isset($this) && isset($this->_default_error_mode)) {
  507.                 $mode    = $this->_default_error_mode;
  508.                 $options = $this->_default_error_options;
  509.             // Global error handler
  510.             } elseif (isset($GLOBALS['_PEAR_default_error_mode'])) {
  511.                 $mode    = $GLOBALS['_PEAR_default_error_mode'];
  512.                 $options = $GLOBALS['_PEAR_default_error_options'];
  513.             }
  514.         }
  515.  
  516.         if ($error_class !== null) {
  517.             $ec = $error_class;
  518.         } elseif (isset($this) && isset($this->_error_class)) {
  519.             $ec = $this->_error_class;
  520.         } else {
  521.             $ec = 'PEAR_Error';
  522.         }
  523.         if ($skipmsg) {
  524.             return new $ec($code, $mode, $options, $userinfo);
  525.         } else {
  526.             return new $ec($message, $code, $mode, $options, $userinfo);
  527.         }
  528.     }
  529.  
  530.     // }}}
  531.     // {{{ throwError()
  532.  
  533.     /**
  534.      * Simpler form of raiseError with fewer options.  In most cases
  535.      * message, code and userinfo are enough.
  536.      *
  537.      * @param string $message
  538.      *
  539.      */
  540.     function &throwError($message = null,
  541.                          $code = null,
  542.                          $userinfo = null)
  543.     {
  544.         if (isset($this) && is_subclass_of($this, 'PEAR_Error')) {
  545.             return $this->raiseError($message, $code, null, null, $userinfo);
  546.         } else {
  547.             return PEAR::raiseError($message, $code, null, null, $userinfo);
  548.         }
  549.     }
  550.  
  551.     // }}}
  552.     // {{{ pushErrorHandling()
  553.  
  554.     /**
  555.      * Push a new error handler on top of the error handler options stack. With this
  556.      * you can easily override the actual error handler for some code and restore
  557.      * it later with popErrorHandling.
  558.      *
  559.      * @param mixed $mode (same as setErrorHandling)
  560.      * @param mixed $options (same as setErrorHandling)
  561.      *
  562.      * @return bool Always true
  563.      *
  564.      * @see PEAR::setErrorHandling
  565.      */
  566.     function pushErrorHandling($mode, $options = null)
  567.     {
  568.         $stack = &$GLOBALS['_PEAR_error_handler_stack'];
  569.         if (isset($this)) {
  570.             $def_mode    = &$this->_default_error_mode;
  571.             $def_options = &$this->_default_error_options;
  572.         } else {
  573.             $def_mode    = &$GLOBALS['_PEAR_default_error_mode'];
  574.             $def_options = &$GLOBALS['_PEAR_default_error_options'];
  575.         }
  576.         $stack[] = array($def_mode, $def_options);
  577.  
  578.         if (isset($this)) {
  579.             $this->setErrorHandling($mode, $options);
  580.         } else {
  581.             PEAR::setErrorHandling($mode, $options);
  582.         }
  583.         $stack[] = array($mode, $options);
  584.         return true;
  585.     }
  586.  
  587.     // }}}
  588.     // {{{ popErrorHandling()
  589.  
  590.     /**
  591.     * Pop the last error handler used
  592.     *
  593.     * @return bool Always true
  594.     *
  595.     * @see PEAR::pushErrorHandling
  596.     */
  597.     function popErrorHandling()
  598.     {
  599.         $stack = &$GLOBALS['_PEAR_error_handler_stack'];
  600.         array_pop($stack);
  601.         list($mode, $options) = $stack[sizeof($stack) - 1];
  602.         array_pop($stack);
  603.         if (isset($this)) {
  604.             $this->setErrorHandling($mode, $options);
  605.         } else {
  606.             PEAR::setErrorHandling($mode, $options);
  607.         }
  608.         return true;
  609.     }
  610.  
  611.     // }}}
  612.     // {{{ loadExtension()
  613.  
  614.     /**
  615.     * OS independant PHP extension load. Remember to take care
  616.     * on the correct extension name for case sensitive OSes.
  617.     *
  618.     * @param string $ext The extension name
  619.     * @return bool Success or not on the dl() call
  620.     */
  621.     function loadExtension($ext)
  622.     {
  623.         if (!extension_loaded($ext)) {
  624.             // if either returns true dl() will produce a FATAL error, stop that
  625.             if ((ini_get('enable_dl') != 1) || (ini_get('safe_mode') == 1)) {
  626.                 return false;
  627.             }
  628.             if (OS_WINDOWS) {
  629.                 $suffix = '.dll';
  630.             } elseif (PHP_OS == 'HP-UX') {
  631.                 $suffix = '.sl';
  632.             } elseif (PHP_OS == 'AIX') {
  633.                 $suffix = '.a';
  634.             } elseif (PHP_OS == 'OSX') {
  635.                 $suffix = '.bundle';
  636.             } else {
  637.                 $suffix = '.so';
  638.             }
  639.             return @dl('php_'.$ext.$suffix) || @dl($ext.$suffix);
  640.         }
  641.         return true;
  642.     }
  643.  
  644.     // }}}
  645. }
  646.  
  647. // {{{ _PEAR_call_destructors()
  648.  
  649. function _PEAR_call_destructors()
  650. {
  651.     global $_PEAR_destructor_object_list;
  652.     if (is_array($_PEAR_destructor_object_list) &&
  653.         sizeof($_PEAR_destructor_object_list))
  654.     {
  655.         reset($_PEAR_destructor_object_list);
  656.         while (list($k, $objref) = each($_PEAR_destructor_object_list)) {
  657.             $classname = get_class($objref);
  658.             while ($classname) {
  659.                 $destructor = "_$classname";
  660.                 if (method_exists($objref, $destructor)) {
  661.                     $objref->$destructor();
  662.                     break;
  663.                 } else {
  664.                     $classname = get_parent_class($classname);
  665.                 }
  666.             }
  667.         }
  668.         // Empty the object list to ensure that destructors are
  669.         // not called more than once.
  670.         $_PEAR_destructor_object_list = array();
  671.     }
  672.  
  673.     // Now call the shutdown functions
  674.     if (is_array($GLOBALS['_PEAR_shutdown_funcs']) AND !empty($GLOBALS['_PEAR_shutdown_funcs'])) {
  675.         foreach ($GLOBALS['_PEAR_shutdown_funcs'] as $value) {
  676.             call_user_func_array($value[0], $value[1]);
  677.         }
  678.     }
  679. }
  680.  
  681. // }}}
  682.  
  683. class PEAR_Error
  684. {
  685.     // {{{ properties
  686.  
  687.     var $error_message_prefix = '';
  688.     var $mode                 = PEAR_ERROR_RETURN;
  689.     var $level                = E_USER_NOTICE;
  690.     var $code                 = -1;
  691.     var $message              = '';
  692.     var $userinfo             = '';
  693.     var $backtrace            = null;
  694.  
  695.     // }}}
  696.     // {{{ constructor
  697.  
  698.     /**
  699.      * PEAR_Error constructor
  700.      *
  701.      * @param string $message  message
  702.      *
  703.      * @param int $code     (optional) error code
  704.      *
  705.      * @param int $mode     (optional) error mode, one of: PEAR_ERROR_RETURN,
  706.      * PEAR_ERROR_PRINT, PEAR_ERROR_DIE, PEAR_ERROR_TRIGGER,
  707.      * PEAR_ERROR_CALLBACK or PEAR_ERROR_EXCEPTION
  708.      *
  709.      * @param mixed $options   (optional) error level, _OR_ in the case of
  710.      * PEAR_ERROR_CALLBACK, the callback function or object/method
  711.      * tuple.
  712.      *
  713.      * @param string $userinfo (optional) additional user/debug info
  714.      *
  715.      * @access public
  716.      *
  717.      */
  718.     function PEAR_Error($message = 'unknown error', $code = null,
  719.                         $mode = null, $options = null, $userinfo = null)
  720.     {
  721.         if ($mode === null) {
  722.             $mode = PEAR_ERROR_RETURN;
  723.         }
  724.         $this->message   = $message;
  725.         $this->code      = $code;
  726.         $this->mode      = $mode;
  727.         $this->userinfo  = $userinfo;
  728.         if (function_exists("debug_backtrace")) {
  729.             $this->backtrace = debug_backtrace();
  730.         }
  731.         if ($mode & PEAR_ERROR_CALLBACK) {
  732.             $this->level = E_USER_NOTICE;
  733.             $this->callback = $options;
  734.         } else {
  735.             if ($options === null) {
  736.                 $options = E_USER_NOTICE;
  737.             }
  738.             $this->level = $options;
  739.             $this->callback = null;
  740.         }
  741.         if ($this->mode & PEAR_ERROR_PRINT) {
  742.             if (is_null($options) || is_int($options)) {
  743.                 $format = "%s";
  744.             } else {
  745.                 $format = $options;
  746.             }
  747.             printf($format, $this->getMessage());
  748.         }
  749.         if ($this->mode & PEAR_ERROR_TRIGGER) {
  750.             trigger_error($this->getMessage(), $this->level);
  751.         }
  752.         if ($this->mode & PEAR_ERROR_DIE) {
  753.             $msg = $this->getMessage();
  754.             if (is_null($options) || is_int($options)) {
  755.                 $format = "%s";
  756.                 if (substr($msg, -1) != "\n") {
  757.                     $msg .= "\n";
  758.                 }
  759.             } else {
  760.                 $format = $options;
  761.             }
  762.             die(sprintf($format, $msg));
  763.         }
  764.         if ($this->mode & PEAR_ERROR_CALLBACK) {
  765.             if (is_string($this->callback) && strlen($this->callback)) {
  766.                 call_user_func($this->callback, $this);
  767.             } elseif (is_array($this->callback) &&
  768.                       sizeof($this->callback) == 2 &&
  769.                       is_object($this->callback[0]) &&
  770.                       is_string($this->callback[1]) &&
  771.                       strlen($this->callback[1])) {
  772.                       call_user_func($this->callback, $this);
  773.             }
  774.         }
  775.         if (PEAR_ZE2 && $this->mode & PEAR_ERROR_EXCEPTION) {
  776.             eval('throw $this;');
  777.         }
  778.     }
  779.  
  780.     // }}}
  781.     // {{{ getMode()
  782.  
  783.     /**
  784.      * Get the error mode from an error object.
  785.      *
  786.      * @return int error mode
  787.      * @access public
  788.      */
  789.     function getMode() {
  790.         return $this->mode;
  791.     }
  792.  
  793.     // }}}
  794.     // {{{ getCallback()
  795.  
  796.     /**
  797.      * Get the callback function/method from an error object.
  798.      *
  799.      * @return mixed callback function or object/method array
  800.      * @access public
  801.      */
  802.     function getCallback() {
  803.         return $this->callback;
  804.     }
  805.  
  806.     // }}}
  807.     // {{{ getMessage()
  808.  
  809.  
  810.     /**
  811.      * Get the error message from an error object.
  812.      *
  813.      * @return  string  full error message
  814.      * @access public
  815.      */
  816.     function getMessage()
  817.     {
  818.         return ($this->error_message_prefix . $this->message);
  819.     }
  820.  
  821.  
  822.     // }}}
  823.     // {{{ getCode()
  824.  
  825.     /**
  826.      * Get error code from an error object
  827.      *
  828.      * @return int error code
  829.      * @access public
  830.      */
  831.      function getCode()
  832.      {
  833.         return $this->code;
  834.      }
  835.  
  836.     // }}}
  837.     // {{{ getType()
  838.  
  839.     /**
  840.      * Get the name of this error/exception.
  841.      *
  842.      * @return string error/exception name (type)
  843.      * @access public
  844.      */
  845.     function getType()
  846.     {
  847.         return get_class($this);
  848.     }
  849.  
  850.     // }}}
  851.     // {{{ getUserInfo()
  852.  
  853.     /**
  854.      * Get additional user-supplied information.
  855.      *
  856.      * @return string user-supplied information
  857.      * @access public
  858.      */
  859.     function getUserInfo()
  860.     {
  861.         return $this->userinfo;
  862.     }
  863.  
  864.     // }}}
  865.     // {{{ getDebugInfo()
  866.  
  867.     /**
  868.      * Get additional debug information supplied by the application.
  869.      *
  870.      * @return string debug information
  871.      * @access public
  872.      */
  873.     function getDebugInfo()
  874.     {
  875.         return $this->getUserInfo();
  876.     }
  877.  
  878.     // }}}
  879.     // {{{ getBacktrace()
  880.  
  881.     /**
  882.      * Get the call backtrace from where the error was generated.
  883.      * Supported with PHP 4.3.0 or newer.
  884.      *
  885.      * @param int $frame (optional) what frame to fetch
  886.      * @return array Backtrace, or NULL if not available.
  887.      * @access public
  888.      */
  889.     function getBacktrace($frame = null)
  890.     {
  891.         if ($frame === null) {
  892.             return $this->backtrace;
  893.         }
  894.         return $this->backtrace[$frame];
  895.     }
  896.  
  897.     // }}}
  898.     // {{{ addUserInfo()
  899.  
  900.     function addUserInfo($info)
  901.     {
  902.         if (empty($this->userinfo)) {
  903.             $this->userinfo = $info;
  904.         } else {
  905.             $this->userinfo .= " ** $info";
  906.         }
  907.     }
  908.  
  909.     // }}}
  910.     // {{{ toString()
  911.  
  912.     /**
  913.      * Make a string representation of this object.
  914.      *
  915.      * @return string a string with an object summary
  916.      * @access public
  917.      */
  918.     function toString() {
  919.         $modes = array();
  920.         $levels = array(E_USER_NOTICE  => 'notice',
  921.                         E_USER_WARNING => 'warning',
  922.                         E_USER_ERROR   => 'error');
  923.         if ($this->mode & PEAR_ERROR_CALLBACK) {
  924.             if (is_array($this->callback)) {
  925.                 $callback = get_class($this->callback[0]) . '::' .
  926.                     $this->callback[1];
  927.             } else {
  928.                 $callback = $this->callback;
  929.             }
  930.             return sprintf('[%s: message="%s" code=%d mode=callback '.
  931.                            'callback=%s prefix="%s" info="%s"]',
  932.                            get_class($this), $this->message, $this->code,
  933.                            $callback, $this->error_message_prefix,
  934.                            $this->userinfo);
  935.         }
  936.         if ($this->mode & PEAR_ERROR_PRINT) {
  937.             $modes[] = 'print';
  938.         }
  939.         if ($this->mode & PEAR_ERROR_TRIGGER) {
  940.             $modes[] = 'trigger';
  941.         }
  942.         if ($this->mode & PEAR_ERROR_DIE) {
  943.             $modes[] = 'die';
  944.         }
  945.         if ($this->mode & PEAR_ERROR_RETURN) {
  946.             $modes[] = 'return';
  947.         }
  948.         return sprintf('[%s: message="%s" code=%d mode=%s level=%s '.
  949.                        'prefix="%s" info="%s"]',
  950.                        get_class($this), $this->message, $this->code,
  951.                        implode("|", $modes), $levels[$this->level],
  952.                        $this->error_message_prefix,
  953.                        $this->userinfo);
  954.     }
  955.  
  956.     // }}}
  957. }
  958.  
  959. register_shutdown_function("_PEAR_call_destructors");
  960.  
  961. /*
  962.  * Local Variables:
  963.  * mode: php
  964.  * tab-width: 4
  965.  * c-basic-offset: 4
  966.  * End:
  967.  */
  968.  
  969.  
  970. /**
  971. * Creates a (compressed) Tar archive
  972. *
  973. * @author   Vincent Blavet <vincent@blavet.net>
  974. * @version  $Revision: 1.1 $
  975. * @package  Archive
  976. */
  977. class Archive_Tar extends PEAR
  978. {
  979.     /**
  980.     * @var string Name of the Tar
  981.     */
  982.     var $_tarname='';
  983.  
  984.     /**
  985.     * @var boolean if true, the Tar file will be gzipped
  986.     */
  987.     var $_compress=false;
  988.  
  989.     /**
  990.     * @var string Type of compression : 'none', 'gz' or 'bz2'
  991.     */
  992.     var $_compress_type='none';
  993.  
  994.     /**
  995.     * @var file descriptor
  996.     */
  997.     var $_file=0;
  998.  
  999.     /**
  1000.     * @var string Local Tar name of a remote Tar (http:// or ftp://)
  1001.     */
  1002.     var $_temp_tarname='';
  1003.  
  1004.     // {{{ constructor
  1005.     /**
  1006.     * Archive_Tar Class constructor. This flavour of the constructor only
  1007.     * declare a new Archive_Tar object, identifying it by the name of the
  1008.     * tar file.
  1009.     * If the compress argument is set the tar will be read or created as a
  1010.     * gzip or bz2 compressed TAR file.
  1011.     *
  1012.     * @param    string  $p_tarname  The name of the tar archive to create
  1013.     * @param    string  $p_compress can be null, 'gz' or 'bz2'. This
  1014.     *                   parameter indicates if gzip or bz2 compression
  1015.     *                   is required.  For compatibility reason the
  1016.     *                   boolean value 'true' means 'gz'.
  1017.     * @access public
  1018.     */
  1019.     function Archive_Tar($p_tarname, $p_compress = null)
  1020.     {
  1021.         $this->PEAR();
  1022.         $this->_compress = false;
  1023.         $this->_compress_type = 'none';
  1024.         if ($p_compress === null) {
  1025.             if (@file_exists($p_tarname)) {
  1026.                 if ($fp = @fopen($p_tarname, "rb")) {
  1027.                     // look for gzip magic cookie
  1028.                     $data = fread($fp, 2);
  1029.                     fclose($fp);
  1030.                     if ($data == "\37\213") {
  1031.                         $this->_compress = true;
  1032.                         $this->_compress_type = 'gz';
  1033.                     // No sure it's enought for a magic code ....
  1034.                     } elseif ($data == "BZ") {
  1035.                         $this->_compress = true;
  1036.                         $this->_compress_type = 'bz2';
  1037.                     }
  1038.                 }
  1039.             } else {
  1040.                 // probably a remote file or some file accessible
  1041.                 // through a stream interface
  1042.                 if (substr($p_tarname, -2) == 'gz') {
  1043.                     $this->_compress = true;
  1044.                     $this->_compress_type = 'gz';
  1045.                 } elseif ((substr($p_tarname, -3) == 'bz2') ||
  1046.                           (substr($p_tarname, -2) == 'bz')) {
  1047.                     $this->_compress = true;
  1048.                     $this->_compress_type = 'bz2';
  1049.                 }
  1050.             }
  1051.         } else {
  1052.             if (($p_compress == true) || ($p_compress == 'gz')) {
  1053.                 $this->_compress = true;
  1054.                 $this->_compress_type = 'gz';
  1055.             } else if ($p_compress == 'bz2') {
  1056.                 $this->_compress = true;
  1057.                 $this->_compress_type = 'bz2';
  1058.             }
  1059.         }
  1060.         $this->_tarname = $p_tarname;
  1061.         if ($this->_compress) { // assert zlib or bz2 extension support
  1062.             if ($this->_compress_type == 'gz')
  1063.                 $extname = 'zlib';
  1064.             else if ($this->_compress_type == 'bz2')
  1065.                 $extname = 'bz2';
  1066.  
  1067.             if (!extension_loaded($extname)) {
  1068.                 PEAR::loadExtension($extname);
  1069.             }
  1070.             if (!extension_loaded($extname)) {
  1071.                 die("The extension '$extname' couldn't be found.\n".
  1072.                     "Please make sure your version of PHP was built ".
  1073.                     "with '$extname' support.\n");
  1074.                 return false;
  1075.             }
  1076.         }
  1077.     }
  1078.     // }}}
  1079.  
  1080.     // {{{ destructor
  1081.     function _Archive_Tar()
  1082.     {
  1083.         $this->_close();
  1084.         // ----- Look for a local copy to delete
  1085.         if ($this->_temp_tarname != '')
  1086.             @unlink($this->_temp_tarname);
  1087.         $this->_PEAR();
  1088.     }
  1089.     // }}}
  1090.  
  1091.     // {{{ create()
  1092.     /**
  1093.     * This method creates the archive file and add the files / directories
  1094.     * that are listed in $p_filelist.
  1095.     * If a file with the same name exist and is writable, it is replaced
  1096.     * by the new tar.
  1097.     * The method return false and a PEAR error text.
  1098.     * The $p_filelist parameter can be an array of string, each string
  1099.     * representing a filename or a directory name with their path if
  1100.     * needed. It can also be a single string with names separated by a
  1101.     * single blank.
  1102.     * For each directory added in the archive, the files and
  1103.     * sub-directories are also added.
  1104.     * See also createModify() method for more details.
  1105.     *
  1106.     * @param array  $p_filelist An array of filenames and directory names, or a single
  1107.     *                           string with names separated by a single blank space.
  1108.     * @return                   true on success, false on error.
  1109.     * @see createModify()
  1110.     * @access public
  1111.     */
  1112.     function create($p_filelist)
  1113.     {
  1114.         return $this->createModify($p_filelist, '', '');
  1115.     }
  1116.     // }}}
  1117.  
  1118.     // {{{ add()
  1119.     /**
  1120.     * This method add the files / directories that are listed in $p_filelist in
  1121.     * the archive. If the archive does not exist it is created.
  1122.     * The method return false and a PEAR error text.
  1123.     * The files and directories listed are only added at the end of the archive,
  1124.     * even if a file with the same name is already archived.
  1125.     * See also createModify() method for more details.
  1126.     *
  1127.     * @param array  $p_filelist An array of filenames and directory names, or a single
  1128.     *                           string with names separated by a single blank space.
  1129.     * @return                   true on success, false on error.
  1130.     * @see createModify()
  1131.     * @access public
  1132.     */
  1133.     function add($p_filelist)
  1134.     {
  1135.         return $this->addModify($p_filelist, '', '');
  1136.     }
  1137.     // }}}
  1138.  
  1139.     // {{{ extract()
  1140.     function extract($p_path='')
  1141.     {
  1142.         return $this->extractModify($p_path, '');
  1143.     }
  1144.     // }}}
  1145.  
  1146.     // {{{ listContent()
  1147.     function listContent()
  1148.     {
  1149.         $v_list_detail = array();
  1150.  
  1151.         if ($this->_openRead()) {
  1152.             if (!$this->_extractList('', $v_list_detail, "list", '', '')) {
  1153.                 unset($v_list_detail);
  1154.                 $v_list_detail = 0;
  1155.             }
  1156.             $this->_close();
  1157.         }
  1158.  
  1159.         return $v_list_detail;
  1160.     }
  1161.     // }}}
  1162.  
  1163.     // {{{ createModify()
  1164.     /**
  1165.     * This method creates the archive file and add the files / directories
  1166.     * that are listed in $p_filelist.
  1167.     * If the file already exists and is writable, it is replaced by the
  1168.     * new tar. It is a create and not an add. If the file exists and is
  1169.     * read-only or is a directory it is not replaced. The method return
  1170.     * false and a PEAR error text.
  1171.     * The $p_filelist parameter can be an array of string, each string
  1172.     * representing a filename or a directory name with their path if
  1173.     * needed. It can also be a single string with names separated by a
  1174.     * single blank.
  1175.     * The path indicated in $p_remove_dir will be removed from the
  1176.     * memorized path of each file / directory listed when this path
  1177.     * exists. By default nothing is removed (empty path '')
  1178.     * The path indicated in $p_add_dir will be added at the beginning of
  1179.     * the memorized path of each file / directory listed. However it can
  1180.     * be set to empty ''. The adding of a path is done after the removing
  1181.     * of path.
  1182.     * The path add/remove ability enables the user to prepare an archive
  1183.     * for extraction in a different path than the origin files are.
  1184.     * See also addModify() method for file adding properties.
  1185.     *
  1186.     * @param array  $p_filelist     An array of filenames and directory names, or a single
  1187.     *                               string with names separated by a single blank space.
  1188.     * @param string $p_add_dir      A string which contains a path to be added to the
  1189.     *                               memorized path of each element in the list.
  1190.     * @param string $p_remove_dir   A string which contains a path to be removed from
  1191.     *                               the memorized path of each element in the list, when
  1192.     *                               relevant.
  1193.     * @return boolean               true on success, false on error.
  1194.     * @access public
  1195.     * @see addModify()
  1196.     */
  1197.     function createModify($p_filelist, $p_add_dir, $p_remove_dir='')
  1198.     {
  1199.         $v_result = true;
  1200.  
  1201.         if (!$this->_openWrite())
  1202.             return false;
  1203.  
  1204.         if ($p_filelist != '') {
  1205.             if (is_array($p_filelist))
  1206.                 $v_list = $p_filelist;
  1207.             elseif (is_string($p_filelist))
  1208.                 $v_list = explode(" ", $p_filelist);
  1209.             else {
  1210.                 $this->_cleanFile();
  1211.                 $this->_error('Invalid file list');
  1212.                 return false;
  1213.             }
  1214.  
  1215.             $v_result = $this->_addList($v_list, $p_add_dir, $p_remove_dir);
  1216.         }
  1217.  
  1218.         if ($v_result) {
  1219.             $this->_writeFooter();
  1220.             $this->_close();
  1221.         } else
  1222.             $this->_cleanFile();
  1223.  
  1224.         return $v_result;
  1225.     }
  1226.     // }}}
  1227.  
  1228.     // {{{ addModify()
  1229.     /**
  1230.     * This method add the files / directories listed in $p_filelist at the
  1231.     * end of the existing archive. If the archive does not yet exists it
  1232.     * is created.
  1233.     * The $p_filelist parameter can be an array of string, each string
  1234.     * representing a filename or a directory name with their path if
  1235.     * needed. It can also be a single string with names separated by a
  1236.     * single blank.
  1237.     * The path indicated in $p_remove_dir will be removed from the
  1238.     * memorized path of each file / directory listed when this path
  1239.     * exists. By default nothing is removed (empty path '')
  1240.     * The path indicated in $p_add_dir will be added at the beginning of
  1241.     * the memorized path of each file / directory listed. However it can
  1242.     * be set to empty ''. The adding of a path is done after the removing
  1243.     * of path.
  1244.     * The path add/remove ability enables the user to prepare an archive
  1245.     * for extraction in a different path than the origin files are.
  1246.     * If a file/dir is already in the archive it will only be added at the
  1247.     * end of the archive. There is no update of the existing archived
  1248.     * file/dir. However while extracting the archive, the last file will
  1249.     * replace the first one. This results in a none optimization of the
  1250.     * archive size.
  1251.     * If a file/dir does not exist the file/dir is ignored. However an
  1252.     * error text is send to PEAR error.
  1253.     * If a file/dir is not readable the file/dir is ignored. However an
  1254.     * error text is send to PEAR error.
  1255.     *
  1256.     * @param array      $p_filelist     An array of filenames and directory names, or a single
  1257.     *                                   string with names separated by a single blank space.
  1258.     * @param string     $p_add_dir      A string which contains a path to be added to the
  1259.     *                                   memorized path of each element in the list.
  1260.     * @param string     $p_remove_dir   A string which contains a path to be removed from
  1261.     *                                   the memorized path of each element in the list, when
  1262.     *                                   relevant.
  1263.     * @return                           true on success, false on error.
  1264.     * @access public
  1265.     */
  1266.     function addModify($p_filelist, $p_add_dir, $p_remove_dir='')
  1267.     {
  1268.         $v_result = true;
  1269.  
  1270.         if (!@is_file($this->_tarname))
  1271.             $v_result = $this->createModify($p_filelist, $p_add_dir, $p_remove_dir);
  1272.         else {
  1273.             if (is_array($p_filelist))
  1274.                 $v_list = $p_filelist;
  1275.             elseif (is_string($p_filelist))
  1276.                 $v_list = explode(" ", $p_filelist);
  1277.             else {
  1278.                 $this->_error('Invalid file list');
  1279.                 return false;
  1280.             }
  1281.  
  1282.             $v_result = $this->_append($v_list, $p_add_dir, $p_remove_dir);
  1283.         }
  1284.  
  1285.         return $v_result;
  1286.     }
  1287.     // }}}
  1288.  
  1289.     // {{{ addString()
  1290.     /**
  1291.     * This method add a single string as a file at the
  1292.     * end of the existing archive. If the archive does not yet exists it
  1293.     * is created.
  1294.     *
  1295.     * @param string     $p_filename     A string which contains the full filename path
  1296.     *                                   that will be associated with the string.
  1297.     * @param string     $p_string       The content of the file added in the archive.
  1298.     * @return                           true on success, false on error.
  1299.     * @access public
  1300.     */
  1301.     function addString($p_filename, $p_string)
  1302.     {
  1303.         $v_result = true;
  1304.  
  1305.         if (!@is_file($this->_tarname)) {
  1306.             if (!$this->_openWrite()) {
  1307.                 return false;
  1308.             }
  1309.             $this->_close();
  1310.         }
  1311.         
  1312.         if (!$this->_openAppend())
  1313.             return false;
  1314.  
  1315.         // Need to check the get back to the temporary file ? ....
  1316.         $v_result = $this->_addString($p_filename, $p_string);
  1317.  
  1318.         $this->_writeFooter();
  1319.  
  1320.         $this->_close();
  1321.  
  1322.         return $v_result;
  1323.     }
  1324.     // }}}
  1325.  
  1326.     // {{{ extractModify()
  1327.     /**
  1328.     * This method extract all the content of the archive in the directory
  1329.     * indicated by $p_path. When relevant the memorized path of the
  1330.     * files/dir can be modified by removing the $p_remove_path path at the
  1331.     * beginning of the file/dir path.
  1332.     * While extracting a file, if the directory path does not exists it is
  1333.     * created.
  1334.     * While extracting a file, if the file already exists it is replaced
  1335.     * without looking for last modification date.
  1336.     * While extracting a file, if the file already exists and is write
  1337.     * protected, the extraction is aborted.
  1338.     * While extracting a file, if a directory with the same name already
  1339.     * exists, the extraction is aborted.
  1340.     * While extracting a directory, if a file with the same name already
  1341.     * exists, the extraction is aborted.
  1342.     * While extracting a file/directory if the destination directory exist
  1343.     * and is write protected, or does not exist but can not be created,
  1344.     * the extraction is aborted.
  1345.     * If after extraction an extracted file does not show the correct
  1346.     * stored file size, the extraction is aborted.
  1347.     * When the extraction is aborted, a PEAR error text is set and false
  1348.     * is returned. However the result can be a partial extraction that may
  1349.     * need to be manually cleaned.
  1350.     *
  1351.     * @param string $p_path         The path of the directory where the files/dir need to by
  1352.     *                               extracted.
  1353.     * @param string $p_remove_path  Part of the memorized path that can be removed if
  1354.     *                               present at the beginning of the file/dir path.
  1355.     * @return boolean               true on success, false on error.
  1356.     * @access public
  1357.     * @see extractList()
  1358.     */
  1359.     function extractModify($p_path, $p_remove_path)
  1360.     {
  1361.         $v_result = true;
  1362.         $v_list_detail = array();
  1363.  
  1364.         if ($v_result = $this->_openRead()) {
  1365.             $v_result = $this->_extractList($p_path, $v_list_detail, "complete", 0, $p_remove_path);
  1366.             $this->_close();
  1367.         }
  1368.  
  1369.         return $v_result;
  1370.     }
  1371.     // }}}
  1372.  
  1373.     // {{{ extractInString()
  1374.     /**
  1375.     * This method extract from the archive one file identified by $p_filename.
  1376.     * The return value is a string with the file content, or NULL on error.
  1377.     * @param string $p_filename     The path of the file to extract in a string.
  1378.     * @return                       a string with the file content or NULL.
  1379.     * @access public
  1380.     */
  1381.     function extractInString($p_filename)
  1382.     {
  1383.         if ($this->_openRead()) {
  1384.             $v_result = $this->_extractInString($p_filename);
  1385.             $this->_close();
  1386.         } else {
  1387.             $v_result = NULL;
  1388.         }
  1389.  
  1390.         return $v_result;
  1391.     }
  1392.     // }}}
  1393.  
  1394.     // {{{ extractList()
  1395.     /**
  1396.     * This method extract from the archive only the files indicated in the
  1397.     * $p_filelist. These files are extracted in the current directory or
  1398.     * in the directory indicated by the optional $p_path parameter.
  1399.     * If indicated the $p_remove_path can be used in the same way as it is
  1400.     * used in extractModify() method.
  1401.     * @param array  $p_filelist     An array of filenames and directory names, or a single
  1402.     *                               string with names separated by a single blank space.
  1403.     * @param string $p_path         The path of the directory where the files/dir need to by
  1404.     *                               extracted.
  1405.     * @param string $p_remove_path  Part of the memorized path that can be removed if
  1406.     *                               present at the beginning of the file/dir path.
  1407.     * @return                       true on success, false on error.
  1408.     * @access public
  1409.     * @see extractModify()
  1410.     */
  1411.     function extractList($p_filelist, $p_path='', $p_remove_path='')
  1412.     {
  1413.         $v_result = true;
  1414.         $v_list_detail = array();
  1415.  
  1416.         if (is_array($p_filelist))
  1417.             $v_list = $p_filelist;
  1418.         elseif (is_string($p_filelist))
  1419.             $v_list = explode(" ", $p_filelist);
  1420.         else {
  1421.             $this->_error('Invalid string list');
  1422.             return false;
  1423.         }
  1424.  
  1425.         if ($v_result = $this->_openRead()) {
  1426.             $v_result = $this->_extractList($p_path, $v_list_detail, "partial", $v_list, $p_remove_path);
  1427.             $this->_close();
  1428.         }
  1429.  
  1430.         return $v_result;
  1431.     }
  1432.     // }}}
  1433.  
  1434.     // {{{ _error()
  1435.     function _error($p_message)
  1436.     {
  1437.         // ----- To be completed
  1438.         $this->raiseError($p_message);
  1439.     }
  1440.     // }}}
  1441.  
  1442.     // {{{ _warning()
  1443.     function _warning($p_message)
  1444.     {
  1445.         // ----- To be completed
  1446.         $this->raiseError($p_message);
  1447.     }
  1448.     // }}}
  1449.  
  1450.     // {{{ _openWrite()
  1451.     function _openWrite()
  1452.     {
  1453.         if ($this->_compress_type == 'gz')
  1454.             $this->_file = @gzopen($this->_tarname, "wb");
  1455.         else if ($this->_compress_type == 'bz2')
  1456.             $this->_file = @bzopen($this->_tarname, "wb");
  1457.         else if ($this->_compress_type == 'none')
  1458.             $this->_file = @fopen($this->_tarname, "wb");
  1459.         else
  1460.             $this->_error('Unknown or missing compression type ('.$this->_compress_type.')');
  1461.  
  1462.         if ($this->_file == 0) {
  1463.             $this->_error('Unable to open in write mode \''.$this->_tarname.'\'');
  1464.             return false;
  1465.         }
  1466.  
  1467.         return true;
  1468.     }
  1469.     // }}}
  1470.  
  1471.     // {{{ _openRead()
  1472.     function _openRead()
  1473.     {
  1474.         if (strtolower(substr($this->_tarname, 0, 7)) == 'http://') {
  1475.  
  1476.           // ----- Look if a local copy need to be done
  1477.           if ($this->_temp_tarname == '') {
  1478.               $this->_temp_tarname = uniqid('tar').'.tmp';
  1479.               if (!$v_file_from = @fopen($this->_tarname, 'rb')) {
  1480.                 $this->_error('Unable to open in read mode \''.$this->_tarname.'\'');
  1481.                 $this->_temp_tarname = '';
  1482.                 return false;
  1483.               }
  1484.               if (!$v_file_to = @fopen($this->_temp_tarname, 'wb')) {
  1485.                 $this->_error('Unable to open in write mode \''.$this->_temp_tarname.'\'');
  1486.                 $this->_temp_tarname = '';
  1487.                 return false;
  1488.               }
  1489.               while ($v_data = @fread($v_file_from, 1024))
  1490.                   @fwrite($v_file_to, $v_data);
  1491.               @fclose($v_file_from);
  1492.               @fclose($v_file_to);
  1493.           }
  1494.  
  1495.           // ----- File to open if the local copy
  1496.           $v_filename = $this->_temp_tarname;
  1497.  
  1498.         } else
  1499.           // ----- File to open if the normal Tar file
  1500.           $v_filename = $this->_tarname;
  1501.  
  1502.         if ($this->_compress_type == 'gz')
  1503.             $this->_file = @gzopen($v_filename, "rb");
  1504.         else if ($this->_compress_type == 'bz2')
  1505.             $this->_file = @bzopen($v_filename, "rb");
  1506.         else if ($this->_compress_type == 'none')
  1507.             $this->_file = @fopen($v_filename, "rb");
  1508.         else
  1509.             $this->_error('Unknown or missing compression type ('.$this->_compress_type.')');
  1510.  
  1511.         if ($this->_file == 0) {
  1512.             $this->_error('Unable to open in read mode \''.$v_filename.'\'');
  1513.             return false;
  1514.         }
  1515.  
  1516.         return true;
  1517.     }
  1518.     // }}}
  1519.  
  1520.     // {{{ _openReadWrite()
  1521.     function _openReadWrite()
  1522.     {
  1523.         if ($this->_compress_type == 'gz')
  1524.             $this->_file = @gzopen($this->_tarname, "r+b");
  1525.         else if ($this->_compress_type == 'bz2')
  1526.             $this->_file = @bzopen($this->_tarname, "r+b");
  1527.         else if ($this->_compress_type == 'none')
  1528.             $this->_file = @fopen($this->_tarname, "r+b");
  1529.         else
  1530.             $this->_error('Unknown or missing compression type ('.$this->_compress_type.')');
  1531.  
  1532.         if ($this->_file == 0) {
  1533.             $this->_error('Unable to open in read/write mode \''.$this->_tarname.'\'');
  1534.             return false;
  1535.         }
  1536.  
  1537.         return true;
  1538.     }
  1539.     // }}}
  1540.  
  1541.     // {{{ _close()
  1542.     function _close()
  1543.     {
  1544.         if (isset($this->_file)) {
  1545.             if ($this->_compress_type == 'gz')
  1546.                 @gzclose($this->_file);
  1547.             else if ($this->_compress_type == 'bz2')
  1548.                 @bzclose($this->_file);
  1549.             else if ($this->_compress_type == 'none')
  1550.                 @fclose($this->_file);
  1551.             else
  1552.                 $this->_error('Unknown or missing compression type ('.$this->_compress_type.')');
  1553.  
  1554.             $this->_file = 0;
  1555.         }
  1556.  
  1557.         // ----- Look if a local copy need to be erase
  1558.         // Note that it might be interesting to keep the url for a time : ToDo
  1559.         if ($this->_temp_tarname != '') {
  1560.             @unlink($this->_temp_tarname);
  1561.             $this->_temp_tarname = '';
  1562.         }
  1563.  
  1564.         return true;
  1565.     }
  1566.     // }}}
  1567.  
  1568.     // {{{ _cleanFile()
  1569.     function _cleanFile()
  1570.     {
  1571.         $this->_close();
  1572.  
  1573.         // ----- Look for a local copy
  1574.         if ($this->_temp_tarname != '') {
  1575.             // ----- Remove the local copy but not the remote tarname
  1576.             @unlink($this->_temp_tarname);
  1577.             $this->_temp_tarname = '';
  1578.         } else {
  1579.             // ----- Remove the local tarname file
  1580.             @unlink($this->_tarname);
  1581.         }
  1582.         $this->_tarname = '';
  1583.  
  1584.         return true;
  1585.     }
  1586.     // }}}
  1587.  
  1588.     // {{{ _writeBlock()
  1589.     function _writeBlock($p_binary_data, $p_len=null)
  1590.     {
  1591.       if ($this->_file) {
  1592.           if ($p_len === null) {
  1593.               if ($this->_compress_type == 'gz')
  1594.                   @gzputs($this->_file, $p_binary_data);
  1595.               else if ($this->_compress_type == 'bz2')
  1596.                   @bzwrite($this->_file, $p_binary_data);
  1597.               else if ($this->_compress_type == 'none')
  1598.                   @fputs($this->_file, $p_binary_data);
  1599.               else
  1600.                   $this->_error('Unknown or missing compression type ('.$this->_compress_type.')');
  1601.           } else {
  1602.               if ($this->_compress_type == 'gz')
  1603.                   @gzputs($this->_file, $p_binary_data, $p_len);
  1604.               else if ($this->_compress_type == 'bz2')
  1605.                   @bzwrite($this->_file, $p_binary_data, $p_len);
  1606.               else if ($this->_compress_type == 'none')
  1607.                   @fputs($this->_file, $p_binary_data, $p_len);
  1608.               else
  1609.                   $this->_error('Unknown or missing compression type ('.$this->_compress_type.')');
  1610.  
  1611.           }
  1612.       }
  1613.       return true;
  1614.     }
  1615.     // }}}
  1616.  
  1617.     // {{{ _readBlock()
  1618.     function _readBlock($p_len=null)
  1619.     {
  1620.       $v_block = null;
  1621.       if ($this->_file) {
  1622.           if ($p_len === null)
  1623.               $p_len = 512;
  1624.               
  1625.           if ($this->_compress_type == 'gz')
  1626.               $v_block = @gzread($this->_file, 512);
  1627.           else if ($this->_compress_type == 'bz2')
  1628.               $v_block = @bzread($this->_file, 512);
  1629.           else if ($this->_compress_type == 'none')
  1630.               $v_block = @fread($this->_file, 512);
  1631.           else
  1632.               $this->_error('Unknown or missing compression type ('.$this->_compress_type.')');
  1633.  
  1634.       }
  1635.       return $v_block;
  1636.     }
  1637.     // }}}
  1638.  
  1639.     // {{{ _jumpBlock()
  1640.     function _jumpBlock($p_len=null)
  1641.     {
  1642.       if ($this->_file) {
  1643.           if ($p_len === null)
  1644.               $p_len = 1;
  1645.  
  1646.           if ($this->_compress_type == 'gz')
  1647.               @gzseek($this->_file, @gztell($this->_file)+($p_len*512));
  1648.           else if ($this->_compress_type == 'bz2') {
  1649.               // ----- Replace missing bztell() and bzseek()
  1650.               for ($i=0; $i<$p_len; $i++)
  1651.                   $this->_readBlock();
  1652.           } else if ($this->_compress_type == 'none')
  1653.               @fseek($this->_file, @ftell($this->_file)+($p_len*512));
  1654.           else
  1655.               $this->_error('Unknown or missing compression type ('.$this->_compress_type.')');
  1656.  
  1657.       }
  1658.       return true;
  1659.     }
  1660.     // }}}
  1661.  
  1662.     // {{{ _writeFooter()
  1663.     function _writeFooter()
  1664.     {
  1665.       if ($this->_file) {
  1666.           // ----- Write the last 0 filled block for end of archive
  1667.           $v_binary_data = pack("a512", '');
  1668.           $this->_writeBlock($v_binary_data);
  1669.       }
  1670.       return true;
  1671.     }
  1672.     // }}}
  1673.  
  1674.     // {{{ _addList()
  1675.     function _addList($p_list, $p_add_dir, $p_remove_dir)
  1676.     {
  1677.       $v_result=true;
  1678.       $v_header = array();
  1679.  
  1680.       // ----- Remove potential windows directory separator
  1681.       $p_add_dir = $this->_translateWinPath($p_add_dir);
  1682.       $p_remove_dir = $this->_translateWinPath($p_remove_dir, false);
  1683.  
  1684.       if (!$this->_file) {
  1685.           $this->_error('Invalid file descriptor');
  1686.           return false;
  1687.       }
  1688.  
  1689.       if (sizeof($p_list) == 0)
  1690.           return true;
  1691.  
  1692.       for ($j=0; ($j<count($p_list)) && ($v_result); $j++) {
  1693.         $v_filename = $p_list[$j];
  1694.  
  1695.         // ----- Skip the current tar name
  1696.         if ($v_filename == $this->_tarname)
  1697.             continue;
  1698.  
  1699.         if ($v_filename == '')
  1700.             continue;
  1701.  
  1702.         if (!file_exists($v_filename)) {
  1703.             $this->_warning("File '$v_filename' does not exist");
  1704.             continue;
  1705.         }
  1706.  
  1707.         // ----- Add the file or directory header
  1708.         if (!$this->_addFile($v_filename, $v_header, $p_add_dir, $p_remove_dir))
  1709.             return false;
  1710.  
  1711.         if (@is_dir($v_filename)) {
  1712.             if (!($p_hdir = opendir($v_filename))) {
  1713.                 $this->_warning("Directory '$v_filename' can not be read");
  1714.                 continue;
  1715.             }
  1716.             $p_hitem = readdir($p_hdir); // '.' directory
  1717.             $p_hitem = readdir($p_hdir); // '..' directory
  1718.             while (false !== ($p_hitem = readdir($p_hdir))) {
  1719.                 if ($v_filename != ".")
  1720.                     $p_temp_list[0] = $v_filename.'/'.$p_hitem;
  1721.                 else
  1722.                     $p_temp_list[0] = $p_hitem;
  1723.  
  1724.                 $v_result = $this->_addList($p_temp_list, $p_add_dir, $p_remove_dir);
  1725.             }
  1726.  
  1727.             unset($p_temp_list);
  1728.             unset($p_hdir);
  1729.             unset($p_hitem);
  1730.         }
  1731.       }
  1732.  
  1733.       return $v_result;
  1734.     }
  1735.     // }}}
  1736.  
  1737.     // {{{ _addFile()
  1738.     function _addFile($p_filename, &$p_header, $p_add_dir, $p_remove_dir)
  1739.     {
  1740.       if (!$this->_file) {
  1741.           $this->_error('Invalid file descriptor');
  1742.           return false;
  1743.       }
  1744.  
  1745.       if ($p_filename == '') {
  1746.           $this->_error('Invalid file name');
  1747.           return false;
  1748.       }
  1749.  
  1750.       // ----- Calculate the stored filename
  1751.       $p_filename = $this->_translateWinPath($p_filename, false);;
  1752.       $v_stored_filename = $p_filename;
  1753.       if (strcmp($p_filename, $p_remove_dir) == 0) {
  1754.           return true;
  1755.       }
  1756.       if ($p_remove_dir != '') {
  1757.           if (substr($p_remove_dir, -1) != '/')
  1758.               $p_remove_dir .= '/';
  1759.  
  1760.           if (substr($p_filename, 0, strlen($p_remove_dir)) == $p_remove_dir)
  1761.               $v_stored_filename = substr($p_filename, strlen($p_remove_dir));
  1762.       }
  1763.       $v_stored_filename = $this->_translateWinPath($v_stored_filename);
  1764.       if ($p_add_dir != '') {
  1765.           if (substr($p_add_dir, -1) == '/')
  1766.               $v_stored_filename = $p_add_dir.$v_stored_filename;
  1767.           else
  1768.               $v_stored_filename = $p_add_dir.'/'.$v_stored_filename;
  1769.       }
  1770.  
  1771.       $v_stored_filename = $this->_pathReduction($v_stored_filename);
  1772.  
  1773.       if (is_file($p_filename)) {
  1774.           if (($v_file = @fopen($p_filename, "rb")) == 0) {
  1775.               $this->_warning("Unable to open file '$p_filename' in binary read mode");
  1776.               return true;
  1777.           }
  1778.  
  1779.           if (!$this->_writeHeader($p_filename, $v_stored_filename))
  1780.               return false;
  1781.  
  1782.           while (($v_buffer = fread($v_file, 512)) != '') {
  1783.               $v_binary_data = pack("a512", "$v_buffer");
  1784.               $this->_writeBlock($v_binary_data);
  1785.           }
  1786.  
  1787.           fclose($v_file);
  1788.  
  1789.       } else {
  1790.           // ----- Only header for dir
  1791.           if (!$this->_writeHeader($p_filename, $v_stored_filename))
  1792.               return false;
  1793.       }
  1794.  
  1795.       return true;
  1796.     }
  1797.     // }}}
  1798.  
  1799.     // {{{ _addString()
  1800.     function _addString($p_filename, $p_string)
  1801.     {
  1802.       if (!$this->_file) {
  1803.           $this->_error('Invalid file descriptor');
  1804.           return false;
  1805.       }
  1806.  
  1807.       if ($p_filename == '') {
  1808.           $this->_error('Invalid file name');
  1809.           return false;
  1810.       }
  1811.  
  1812.       // ----- Calculate the stored filename
  1813.       $p_filename = $this->_translateWinPath($p_filename, false);;
  1814.  
  1815.       if (!$this->_writeHeaderBlock($p_filename, strlen($p_string), 0, 0, "", 0, 0))
  1816.           return false;
  1817.  
  1818.       $i=0;
  1819.       while (($v_buffer = substr($p_string, (($i++)*512), 512)) != '') {
  1820.           $v_binary_data = pack("a512", $v_buffer);
  1821.           $this->_writeBlock($v_binary_data);
  1822.       }
  1823.  
  1824.       return true;
  1825.     }
  1826.     // }}}
  1827.  
  1828.     // {{{ _writeHeader()
  1829.     function _writeHeader($p_filename, $p_stored_filename)
  1830.     {
  1831.         if ($p_stored_filename == '')
  1832.             $p_stored_filename = $p_filename;
  1833.         $v_reduce_filename = $this->_pathReduction($p_stored_filename);
  1834.  
  1835.         if (strlen($v_reduce_filename) > 99) {
  1836.           if (!$this->_writeLongHeader($v_reduce_filename))
  1837.             return false;
  1838.         }
  1839.  
  1840.         $v_info = stat($p_filename);
  1841.         $v_uid = sprintf("%6s ", DecOct($v_info[4]));
  1842.         $v_gid = sprintf("%6s ", DecOct($v_info[5]));
  1843.         $v_perms = sprintf("%6s ", DecOct(fileperms($p_filename)));
  1844.  
  1845.         $v_mtime = sprintf("%11s", DecOct(filemtime($p_filename)));
  1846.  
  1847.         if (@is_dir($p_filename)) {
  1848.           $v_typeflag = "5";
  1849.           $v_size = sprintf("%11s ", DecOct(0));
  1850.         } else {
  1851.           $v_typeflag = '';
  1852.           clearstatcache();
  1853.           $v_size = sprintf("%11s ", DecOct(filesize($p_filename)));
  1854.         }
  1855.  
  1856.         $v_linkname = '';
  1857.  
  1858.         $v_magic = '';
  1859.  
  1860.         $v_version = '';
  1861.  
  1862.         $v_uname = '';
  1863.  
  1864.         $v_gname = '';
  1865.  
  1866.         $v_devmajor = '';
  1867.  
  1868.         $v_devminor = '';
  1869.  
  1870.         $v_prefix = '';
  1871.  
  1872.         $v_binary_data_first = pack("a100a8a8a8a12A12", $v_reduce_filename, $v_perms, $v_uid, $v_gid, $v_size, $v_mtime);
  1873.         $v_binary_data_last = pack("a1a100a6a2a32a32a8a8a155a12", $v_typeflag, $v_linkname, $v_magic, $v_version, $v_uname, $v_gname, $v_devmajor, $v_devminor, $v_prefix, '');
  1874.  
  1875.         // ----- Calculate the checksum
  1876.         $v_checksum = 0;
  1877.         // ..... First part of the header
  1878.         for ($i=0; $i<148; $i++)
  1879.             $v_checksum += ord(substr($v_binary_data_first,$i,1));
  1880.         // ..... Ignore the checksum value and replace it by ' ' (space)
  1881.         for ($i=148; $i<156; $i++)
  1882.             $v_checksum += ord(' ');
  1883.         // ..... Last part of the header
  1884.         for ($i=156, $j=0; $i<512; $i++, $j++)
  1885.             $v_checksum += ord(substr($v_binary_data_last,$j,1));
  1886.  
  1887.         // ----- Write the first 148 bytes of the header in the archive
  1888.         $this->_writeBlock($v_binary_data_first, 148);
  1889.  
  1890.         // ----- Write the calculated checksum
  1891.         $v_checksum = sprintf("%6s ", DecOct($v_checksum));
  1892.         $v_binary_data = pack("a8", $v_checksum);
  1893.         $this->_writeBlock($v_binary_data, 8);
  1894.  
  1895.         // ----- Write the last 356 bytes of the header in the archive
  1896.         $this->_writeBlock($v_binary_data_last, 356);
  1897.  
  1898.         return true;
  1899.     }
  1900.     // }}}
  1901.  
  1902.     // {{{ _writeHeaderBlock()
  1903.     function _writeHeaderBlock($p_filename, $p_size, $p_mtime=0, $p_perms=0, $p_type='', $p_uid=0, $p_gid=0)
  1904.     {
  1905.         $p_filename = $this->_pathReduction($p_filename);
  1906.  
  1907.         if (strlen($p_filename) > 99) {
  1908.           if (!$this->_writeLongHeader($p_filename))
  1909.             return false;
  1910.         }
  1911.  
  1912.         if ($p_type == "5") {
  1913.           $v_size = sprintf("%11s ", DecOct(0));
  1914.         } else {
  1915.           $v_size = sprintf("%11s ", DecOct($p_size));
  1916.         }
  1917.  
  1918.         $v_uid = sprintf("%6s ", DecOct($p_uid));
  1919.         $v_gid = sprintf("%6s ", DecOct($p_gid));
  1920.         $v_perms = sprintf("%6s ", DecOct($p_perms));
  1921.  
  1922.         $v_mtime = sprintf("%11s", DecOct($p_mtime));
  1923.  
  1924.         $v_linkname = '';
  1925.  
  1926.         $v_magic = '';
  1927.  
  1928.         $v_version = '';
  1929.  
  1930.         $v_uname = '';
  1931.  
  1932.         $v_gname = '';
  1933.  
  1934.         $v_devmajor = '';
  1935.  
  1936.         $v_devminor = '';
  1937.  
  1938.         $v_prefix = '';
  1939.  
  1940.         $v_binary_data_first = pack("a100a8a8a8a12A12", $p_filename, $v_perms, $v_uid, $v_gid, $v_size, $v_mtime);
  1941.         $v_binary_data_last = pack("a1a100a6a2a32a32a8a8a155a12", $p_type, $v_linkname, $v_magic, $v_version, $v_uname, $v_gname, $v_devmajor, $v_devminor, $v_prefix, '');
  1942.  
  1943.         // ----- Calculate the checksum
  1944.         $v_checksum = 0;
  1945.         // ..... First part of the header
  1946.         for ($i=0; $i<148; $i++)
  1947.             $v_checksum += ord(substr($v_binary_data_first,$i,1));
  1948.         // ..... Ignore the checksum value and replace it by ' ' (space)
  1949.         for ($i=148; $i<156; $i++)
  1950.             $v_checksum += ord(' ');
  1951.         // ..... Last part of the header
  1952.         for ($i=156, $j=0; $i<512; $i++, $j++)
  1953.             $v_checksum += ord(substr($v_binary_data_last,$j,1));
  1954.  
  1955.         // ----- Write the first 148 bytes of the header in the archive
  1956.         $this->_writeBlock($v_binary_data_first, 148);
  1957.  
  1958.         // ----- Write the calculated checksum
  1959.         $v_checksum = sprintf("%6s ", DecOct($v_checksum));
  1960.         $v_binary_data = pack("a8", $v_checksum);
  1961.         $this->_writeBlock($v_binary_data, 8);
  1962.  
  1963.         // ----- Write the last 356 bytes of the header in the archive
  1964.         $this->_writeBlock($v_binary_data_last, 356);
  1965.  
  1966.         return true;
  1967.     }
  1968.     // }}}
  1969.  
  1970.     // {{{ _writeLongHeader()
  1971.     function _writeLongHeader($p_filename)
  1972.     {
  1973.         $v_size = sprintf("%11s ", DecOct(strlen($p_filename)));
  1974.  
  1975.         $v_typeflag = 'L';
  1976.  
  1977.         $v_linkname = '';
  1978.  
  1979.         $v_magic = '';
  1980.  
  1981.         $v_version = '';
  1982.  
  1983.         $v_uname = '';
  1984.  
  1985.         $v_gname = '';
  1986.  
  1987.         $v_devmajor = '';
  1988.  
  1989.         $v_devminor = '';
  1990.  
  1991.         $v_prefix = '';
  1992.  
  1993.         $v_binary_data_first = pack("a100a8a8a8a12A12", '././@LongLink', 0, 0, 0, $v_size, 0);
  1994.         $v_binary_data_last = pack("a1a100a6a2a32a32a8a8a155a12", $v_typeflag, $v_linkname, $v_magic, $v_version, $v_uname, $v_gname, $v_devmajor, $v_devminor, $v_prefix, '');
  1995.  
  1996.         // ----- Calculate the checksum
  1997.         $v_checksum = 0;
  1998.         // ..... First part of the header
  1999.         for ($i=0; $i<148; $i++)
  2000.             $v_checksum += ord(substr($v_binary_data_first,$i,1));
  2001.         // ..... Ignore the checksum value and replace it by ' ' (space)
  2002.         for ($i=148; $i<156; $i++)
  2003.             $v_checksum += ord(' ');
  2004.         // ..... Last part of the header
  2005.         for ($i=156, $j=0; $i<512; $i++, $j++)
  2006.             $v_checksum += ord(substr($v_binary_data_last,$j,1));
  2007.  
  2008.         // ----- Write the first 148 bytes of the header in the archive
  2009.         $this->_writeBlock($v_binary_data_first, 148);
  2010.  
  2011.         // ----- Write the calculated checksum
  2012.         $v_checksum = sprintf("%6s ", DecOct($v_checksum));
  2013.         $v_binary_data = pack("a8", $v_checksum);
  2014.         $this->_writeBlock($v_binary_data, 8);
  2015.  
  2016.         // ----- Write the last 356 bytes of the header in the archive
  2017.         $this->_writeBlock($v_binary_data_last, 356);
  2018.  
  2019.         // ----- Write the filename as content of the block
  2020.         $i=0;
  2021.         while (($v_buffer = substr($p_filename, (($i++)*512), 512)) != '') {
  2022.             $v_binary_data = pack("a512", "$v_buffer");
  2023.             $this->_writeBlock($v_binary_data);
  2024.         }
  2025.  
  2026.         return true;
  2027.     }
  2028.     // }}}
  2029.  
  2030.     // {{{ _readHeader()
  2031.     function _readHeader($v_binary_data, &$v_header)
  2032.     {
  2033.         if (strlen($v_binary_data)==0) {
  2034.             $v_header['filename'] = '';
  2035.             return true;
  2036.         }
  2037.  
  2038.         if (strlen($v_binary_data) != 512) {
  2039.             $v_header['filename'] = '';
  2040.             $this->_error('Invalid block size : '.strlen($v_binary_data));
  2041.             return false;
  2042.         }
  2043.  
  2044.         // ----- Calculate the checksum
  2045.         $v_checksum = 0;
  2046.         // ..... First part of the header
  2047.         for ($i=0; $i<148; $i++)
  2048.             $v_checksum+=ord(substr($v_binary_data,$i,1));
  2049.         // ..... Ignore the checksum value and replace it by ' ' (space)
  2050.         for ($i=148; $i<156; $i++)
  2051.             $v_checksum += ord(' ');
  2052.         // ..... Last part of the header
  2053.         for ($i=156; $i<512; $i++)
  2054.            $v_checksum+=ord(substr($v_binary_data,$i,1));
  2055.  
  2056.         $v_data = unpack("a100filename/a8mode/a8uid/a8gid/a12size/a12mtime/a8checksum/a1typeflag/a100link/a6magic/a2version/a32uname/a32gname/a8devmajor/a8devminor", $v_binary_data);
  2057.  
  2058.         // ----- Extract the checksum
  2059.         $v_header['checksum'] = OctDec(trim($v_data['checksum']));
  2060.         if ($v_header['checksum'] != $v_checksum) {
  2061.             $v_header['filename'] = '';
  2062.  
  2063.             // ----- Look for last block (empty block)
  2064.             if (($v_checksum == 256) && ($v_header['checksum'] == 0))
  2065.                 return true;
  2066.  
  2067.             $this->_error('Invalid checksum for file "'.$v_data['filename'].'" : '.$v_checksum.' calculated, '.$v_header['checksum'].' expected');
  2068.             return false;
  2069.         }
  2070.  
  2071.         // ----- Extract the properties
  2072.         $v_header['filename'] = trim($v_data['filename']);
  2073.         $v_header['mode'] = OctDec(trim($v_data['mode']));
  2074.         $v_header['uid'] = OctDec(trim($v_data['uid']));
  2075.         $v_header['gid'] = OctDec(trim($v_data['gid']));
  2076.         $v_header['size'] = OctDec(trim($v_data['size']));
  2077.         $v_header['mtime'] = OctDec(trim($v_data['mtime']));
  2078.         if (($v_header['typeflag'] = $v_data['typeflag']) == "5") {
  2079.           $v_header['size'] = 0;
  2080.         }
  2081.         /* ----- All these fields are removed form the header because they do not carry interesting info
  2082.         $v_header[link] = trim($v_data[link]);
  2083.         $v_header[magic] = trim($v_data[magic]);
  2084.         $v_header[version] = trim($v_data[version]);
  2085.         $v_header[uname] = trim($v_data[uname]);
  2086.         $v_header[gname] = trim($v_data[gname]);
  2087.         $v_header[devmajor] = trim($v_data[devmajor]);
  2088.         $v_header[devminor] = trim($v_data[devminor]);
  2089.         */
  2090.  
  2091.         return true;
  2092.     }
  2093.     // }}}
  2094.  
  2095.     // {{{ _readLongHeader()
  2096.     function _readLongHeader(&$v_header)
  2097.     {
  2098.       $v_filename = '';
  2099.       $n = floor($v_header['size']/512);
  2100.       for ($i=0; $i<$n; $i++) {
  2101.         $v_content = $this->_readBlock();
  2102.         $v_filename .= $v_content;
  2103.       }
  2104.       if (($v_header['size'] % 512) != 0) {
  2105.         $v_content = $this->_readBlock();
  2106.         $v_filename .= $v_content;
  2107.       }
  2108.  
  2109.       // ----- Read the next header
  2110.       $v_binary_data = $this->_readBlock();
  2111.  
  2112.       if (!$this->_readHeader($v_binary_data, $v_header))
  2113.         return false;
  2114.  
  2115.       $v_header['filename'] = $v_filename;
  2116.  
  2117.       return true;
  2118.     }
  2119.     // }}}
  2120.  
  2121.     // {{{ _extractInString()
  2122.     /**
  2123.     * This method extract from the archive one file identified by $p_filename.
  2124.     * The return value is a string with the file content, or NULL on error.
  2125.     * @param string $p_filename     The path of the file to extract in a string.
  2126.     * @return                       a string with the file content or NULL.
  2127.     * @access private
  2128.     */
  2129.     function _extractInString($p_filename)
  2130.     {
  2131.         $v_result_str = "";
  2132.  
  2133.         While (strlen($v_binary_data = $this->_readBlock()) != 0)
  2134.         {
  2135.           if (!$this->_readHeader($v_binary_data, $v_header))
  2136.             return NULL;
  2137.  
  2138.           if ($v_header['filename'] == '')
  2139.             continue;
  2140.  
  2141.           // ----- Look for long filename
  2142.           if ($v_header['typeflag'] == 'L') {
  2143.             if (!$this->_readLongHeader($v_header))
  2144.               return NULL;
  2145.           }
  2146.  
  2147.           if ($v_header['filename'] == $p_filename) {
  2148.               if ($v_header['typeflag'] == "5") {
  2149.                   $this->_error('Unable to extract in string a directory entry {'.$v_header['filename'].'}');
  2150.                   return NULL;
  2151.               } else {
  2152.                   $n = floor($v_header['size']/512);
  2153.                   for ($i=0; $i<$n; $i++) {
  2154.                       $v_result_str .= $this->_readBlock();
  2155.                   }
  2156.                   if (($v_header['size'] % 512) != 0) {
  2157.                       $v_content = $this->_readBlock();
  2158.                       $v_result_str .= substr($v_content, 0, ($v_header['size'] % 512));
  2159.                   }
  2160.                   return $v_result_str;
  2161.               }
  2162.           } else {
  2163.               $this->_jumpBlock(ceil(($v_header['size']/512)));
  2164.           }
  2165.         }
  2166.  
  2167.         return NULL;
  2168.     }
  2169.     // }}}
  2170.  
  2171.     // {{{ _extractList()
  2172.     function _extractList($p_path, &$p_list_detail, $p_mode, $p_file_list, $p_remove_path)
  2173.     {
  2174.     $v_result=true;
  2175.     $v_nb = 0;
  2176.     $v_extract_all = true;
  2177.     $v_listing = false;
  2178.  
  2179.     $p_path = $this->_translateWinPath($p_path, false);
  2180.     if ($p_path == '' || (substr($p_path, 0, 1) != '/' && substr($p_path, 0, 3) != "../" && !strpos($p_path, ':'))) {
  2181.       $p_path = "./".$p_path;
  2182.     }
  2183.     $p_remove_path = $this->_translateWinPath($p_remove_path);
  2184.  
  2185.     // ----- Look for path to remove format (should end by /)
  2186.     if (($p_remove_path != '') && (substr($p_remove_path, -1) != '/'))
  2187.       $p_remove_path .= '/';
  2188.     $p_remove_path_size = strlen($p_remove_path);
  2189.  
  2190.     switch ($p_mode) {
  2191.       case "complete" :
  2192.         $v_extract_all = TRUE;
  2193.         $v_listing = FALSE;
  2194.       break;
  2195.       case "partial" :
  2196.           $v_extract_all = FALSE;
  2197.           $v_listing = FALSE;
  2198.       break;
  2199.       case "list" :
  2200.           $v_extract_all = FALSE;
  2201.           $v_listing = TRUE;
  2202.       break;
  2203.       default :
  2204.         $this->_error('Invalid extract mode ('.$p_mode.')');
  2205.         return false;
  2206.     }
  2207.  
  2208.     clearstatcache();
  2209.  
  2210.     While (strlen($v_binary_data = $this->_readBlock()) != 0)
  2211.     {
  2212.       $v_extract_file = FALSE;
  2213.       $v_extraction_stopped = 0;
  2214.  
  2215.       if (!$this->_readHeader($v_binary_data, $v_header))
  2216.         return false;
  2217.  
  2218.       if ($v_header['filename'] == '')
  2219.         continue;
  2220.  
  2221.       // ----- Look for long filename
  2222.       if ($v_header['typeflag'] == 'L') {
  2223.         if (!$this->_readLongHeader($v_header))
  2224.           return false;
  2225.       }
  2226.  
  2227.       if ((!$v_extract_all) && (is_array($p_file_list))) {
  2228.         // ----- By default no unzip if the file is not found
  2229.         $v_extract_file = false;
  2230.  
  2231.         for ($i=0; $i<sizeof($p_file_list); $i++) {
  2232.           // ----- Look if it is a directory
  2233.           if (substr($p_file_list[$i], -1) == '/') {
  2234.             // ----- Look if the directory is in the filename path
  2235.             if ((strlen($v_header['filename']) > strlen($p_file_list[$i])) && (substr($v_header['filename'], 0, strlen($p_file_list[$i])) == $p_file_list[$i])) {
  2236.               $v_extract_file = TRUE;
  2237.               break;
  2238.             }
  2239.           }
  2240.  
  2241.           // ----- It is a file, so compare the file names
  2242.           elseif ($p_file_list[$i] == $v_header['filename']) {
  2243.             $v_extract_file = TRUE;
  2244.             break;
  2245.           }
  2246.         }
  2247.       } else {
  2248.         $v_extract_file = TRUE;
  2249.       }
  2250.  
  2251.       // ----- Look if this file need to be extracted
  2252.       if (($v_extract_file) && (!$v_listing))
  2253.       {
  2254.         if (($p_remove_path != '')
  2255.             && (substr($v_header['filename'], 0, $p_remove_path_size) == $p_remove_path))
  2256.           $v_header['filename'] = substr($v_header['filename'], $p_remove_path_size);
  2257.         if (($p_path != './') && ($p_path != '/')) {
  2258.           while (substr($p_path, -1) == '/')
  2259.             $p_path = substr($p_path, 0, strlen($p_path)-1);
  2260.  
  2261.           if (substr($v_header['filename'], 0, 1) == '/')
  2262.               $v_header['filename'] = $p_path.$v_header['filename'];
  2263.           else
  2264.             $v_header['filename'] = $p_path.'/'.$v_header['filename'];
  2265.         }
  2266.         if (file_exists($v_header['filename'])) {
  2267.           if ((@is_dir($v_header['filename'])) && ($v_header['typeflag'] == '')) {
  2268.             $this->_error('File '.$v_header['filename'].' already exists as a directory');
  2269.             return false;
  2270.           }
  2271.           if ((is_file($v_header['filename'])) && ($v_header['typeflag'] == "5")) {
  2272.             $this->_error('Directory '.$v_header['filename'].' already exists as a file');
  2273.             return false;
  2274.           }
  2275.           if (!is_writeable($v_header['filename'])) {
  2276.             $this->_error('File '.$v_header['filename'].' already exists and is write protected');
  2277.             return false;
  2278.           }
  2279.           if (filemtime($v_header['filename']) > $v_header['mtime']) {
  2280.             // To be completed : An error or silent no replace ?
  2281.           }
  2282.         }
  2283.  
  2284.         // ----- Check the directory availability and create it if necessary
  2285.         elseif (($v_result = $this->_dirCheck(($v_header['typeflag'] == "5"?$v_header['filename']:dirname($v_header['filename'])))) != 1) {
  2286.             $this->_error('Unable to create path for '.$v_header['filename']);
  2287.             return false;
  2288.         }
  2289.  
  2290.         if ($v_extract_file) {
  2291.           if ($v_header['typeflag'] == "5") {
  2292.             if (!@file_exists($v_header['filename'])) {
  2293.                 if (!@mkdir($v_header['filename'], 0777)) {
  2294.                     $this->_error('Unable to create directory {'.$v_header['filename'].'}');
  2295.                     return false;
  2296.                 }
  2297.             }
  2298.           } else {
  2299.               if (($v_dest_file = @fopen($v_header['filename'], "wb")) == 0) {
  2300.                   $this->_error('Error while opening {'.$v_header['filename'].'} in write binary mode');
  2301.                   return false;
  2302.               } else {
  2303.                   $n = floor($v_header['size']/512);
  2304.                   for ($i=0; $i<$n; $i++) {
  2305.                       $v_content = $this->_readBlock();
  2306.                       fwrite($v_dest_file, $v_content, 512);
  2307.                   }
  2308.             if (($v_header['size'] % 512) != 0) {
  2309.               $v_content = $this->_readBlock();
  2310.               fwrite($v_dest_file, $v_content, ($v_header['size'] % 512));
  2311.             }
  2312.  
  2313.             @fclose($v_dest_file);
  2314.  
  2315.             // ----- Change the file mode, mtime
  2316.             @touch($v_header['filename'], $v_header['mtime']);
  2317.             // To be completed
  2318.             //chmod($v_header[filename], DecOct($v_header[mode]));
  2319.           }
  2320.  
  2321.           // ----- Check the file size
  2322.           clearstatcache();
  2323.           if (filesize($v_header['filename']) != $v_header['size']) {
  2324.               $this->_error('Extracted file '.$v_header['filename'].' does not have the correct file size \''.filesize($v_filename).'\' ('.$v_header['size'].' expected). Archive may be corrupted.');
  2325.               return false;
  2326.           }
  2327.           }
  2328.         } else {
  2329.           $this->_jumpBlock(ceil(($v_header['size']/512)));
  2330.         }
  2331.       } else {
  2332.           $this->_jumpBlock(ceil(($v_header['size']/512)));
  2333.       }
  2334.  
  2335.       /* TBC : Seems to be unused ...
  2336.       if ($this->_compress)
  2337.         $v_end_of_file = @gzeof($this->_file);
  2338.       else
  2339.         $v_end_of_file = @feof($this->_file);
  2340.         */
  2341.  
  2342.       if ($v_listing || $v_extract_file || $v_extraction_stopped) {
  2343.         // ----- Log extracted files
  2344.         if (($v_file_dir = dirname($v_header['filename'])) == $v_header['filename'])
  2345.           $v_file_dir = '';
  2346.         if ((substr($v_header['filename'], 0, 1) == '/') && ($v_file_dir == ''))
  2347.           $v_file_dir = '/';
  2348.  
  2349.         $p_list_detail[$v_nb++] = $v_header;
  2350.       }
  2351.     }
  2352.  
  2353.         return true;
  2354.     }
  2355.     // }}}
  2356.  
  2357.     // {{{ _openAppend()
  2358.     function _openAppend()
  2359.     {
  2360.         if (filesize($this->_tarname) == 0)
  2361.           return $this->_openWrite();
  2362.           
  2363.         if ($this->_compress) {
  2364.             $this->_close();
  2365.  
  2366.             if (!@rename($this->_tarname, $this->_tarname.".tmp")) {
  2367.                 $this->_error('Error while renaming \''.$this->_tarname.'\' to temporary file \''.$this->_tarname.'.tmp\'');
  2368.                 return false;
  2369.             }
  2370.  
  2371.             if ($this->_compress_type == 'gz')
  2372.                 $v_temp_tar = @gzopen($this->_tarname.".tmp", "rb");
  2373.             elseif ($this->_compress_type == 'bz2')
  2374.                 $v_temp_tar = @bzopen($this->_tarname.".tmp", "rb");
  2375.                 
  2376.             if ($v_temp_tar == 0) {
  2377.                 $this->_error('Unable to open file \''.$this->_tarname.'.tmp\' in binary read mode');
  2378.                 @rename($this->_tarname.".tmp", $this->_tarname);
  2379.                 return false;
  2380.             }
  2381.  
  2382.             if (!$this->_openWrite()) {
  2383.                 @rename($this->_tarname.".tmp", $this->_tarname);
  2384.                 return false;
  2385.             }
  2386.  
  2387.             if ($this->_compress_type == 'gz') {
  2388.                 $v_buffer = @gzread($v_temp_tar, 512);
  2389.  
  2390.                 // ----- Read the following blocks but not the last one
  2391.                 if (!@gzeof($v_temp_tar)) {
  2392.                     do{
  2393.                         $v_binary_data = pack("a512", $v_buffer);
  2394.                         $this->_writeBlock($v_binary_data);
  2395.                         $v_buffer = @gzread($v_temp_tar, 512);
  2396.  
  2397.                     } while (!@gzeof($v_temp_tar));
  2398.                 }
  2399.  
  2400.                 @gzclose($v_temp_tar);
  2401.             }
  2402.             elseif ($this->_compress_type == 'bz2') {
  2403.                 $v_buffered_lines   = array();
  2404.                 $v_buffered_lines[] = @bzread($v_temp_tar, 512);
  2405.  
  2406.                 // ----- Read the following blocks but not the last one
  2407.                 while (strlen($v_buffered_lines[] = @bzread($v_temp_tar, 512)) > 0) {
  2408.                     $v_binary_data = pack("a512", array_shift($v_buffered_lines));
  2409.                     $this->_writeBlock($v_binary_data);
  2410.                 }
  2411.  
  2412.                 @bzclose($v_temp_tar);
  2413.             }
  2414.  
  2415.             if (!@unlink($this->_tarname.".tmp")) {
  2416.                 $this->_error('Error while deleting temporary file \''.$this->_tarname.'.tmp\'');
  2417.             }
  2418.  
  2419.         } else {
  2420.             // ----- For not compressed tar, just add files before the last 512 bytes block
  2421.             if (!$this->_openReadWrite())
  2422.                return false;
  2423.  
  2424.             clearstatcache();
  2425.             $v_size = filesize($this->_tarname);
  2426.             fseek($this->_file, $v_size-512);
  2427.         }
  2428.  
  2429.         return true;
  2430.     }
  2431.     // }}}
  2432.  
  2433.     // {{{ _append()
  2434.     function _append($p_filelist, $p_add_dir='', $p_remove_dir='')
  2435.     {
  2436.         if (!$this->_openAppend())
  2437.             return false;
  2438.             
  2439.         if ($this->_addList($p_filelist, $p_add_dir, $p_remove_dir))
  2440.            $this->_writeFooter();
  2441.  
  2442.         $this->_close();
  2443.  
  2444.         return true;
  2445.     }
  2446.     // }}}
  2447.  
  2448.     // {{{ _dirCheck()
  2449.  
  2450.     /**
  2451.      * Check if a directory exists and create it (including parent
  2452.      * dirs) if not.
  2453.      *
  2454.      * @param string $p_dir directory to check
  2455.      *
  2456.      * @return bool TRUE if the directory exists or was created
  2457.      */
  2458.     function _dirCheck($p_dir)
  2459.     {
  2460.         if ((@is_dir($p_dir)) || ($p_dir == ''))
  2461.             return true;
  2462.  
  2463.         $p_parent_dir = dirname($p_dir);
  2464.  
  2465.         if (($p_parent_dir != $p_dir) &&
  2466.             ($p_parent_dir != '') &&
  2467.             (!$this->_dirCheck($p_parent_dir)))
  2468.              return false;
  2469.  
  2470.         if (!@mkdir($p_dir, 0777)) {
  2471.             $this->_error("Unable to create directory '$p_dir'");
  2472.             return false;
  2473.         }
  2474.  
  2475.         return true;
  2476.     }
  2477.  
  2478.     // }}}
  2479.  
  2480.     // {{{ _pathReduction()
  2481.  
  2482.     /**
  2483.      * Compress path by changing for example "/dir/foo/../bar" to "/dir/bar", and
  2484.      * remove double slashes.
  2485.      *
  2486.      * @param string $p_dir path to reduce
  2487.      *
  2488.      * @return string reduced path
  2489.      *
  2490.      * @access private
  2491.      *
  2492.      */
  2493.     function _pathReduction($p_dir)
  2494.     {
  2495.         $v_result = '';
  2496.  
  2497.         // ----- Look for not empty path
  2498.         if ($p_dir != '') {
  2499.             // ----- Explode path by directory names
  2500.             $v_list = explode('/', $p_dir);
  2501.  
  2502.             // ----- Study directories from last to first
  2503.             for ($i=sizeof($v_list)-1; $i>=0; $i--) {
  2504.                 // ----- Look for current path
  2505.                 if ($v_list[$i] == ".") {
  2506.                     // ----- Ignore this directory
  2507.                     // Should be the first $i=0, but no check is done
  2508.                 }
  2509.                 else if ($v_list[$i] == "..") {
  2510.                     // ----- Ignore it and ignore the $i-1
  2511.                     $i--;
  2512.                 }
  2513.                 else if (($v_list[$i] == '') && ($i!=(sizeof($v_list)-1)) && ($i!=0)) {
  2514.                     // ----- Ignore only the double '//' in path,
  2515.                     // but not the first and last /
  2516.                 } else {
  2517.                     $v_result = $v_list[$i].($i!=(sizeof($v_list)-1)?'/'.$v_result:'');
  2518.                 }
  2519.             }
  2520.         }
  2521.         $v_result = strtr($v_result, '\\', '/');
  2522.         return $v_result;
  2523.     }
  2524.  
  2525.     // }}}
  2526.  
  2527.     // {{{ _translateWinPath()
  2528.     function _translateWinPath($p_path, $p_remove_disk_letter=true)
  2529.     {
  2530.       if (OS_WINDOWS) {
  2531.           // ----- Look for potential disk letter
  2532.           if (($p_remove_disk_letter) && (($v_position = strpos($p_path, ':')) != false)) {
  2533.               $p_path = substr($p_path, $v_position+1);
  2534.           }
  2535.           // ----- Change potential windows directory separator
  2536.           if ((strpos($p_path, '\\') > 0) || (substr($p_path, 0,1) == '\\')) {
  2537.               $p_path = strtr($p_path, '\\', '/');
  2538.           }
  2539.       }
  2540.       return $p_path;
  2541.     }
  2542.     // }}}
  2543.  
  2544. }
  2545. ?>
  2546.