Source for file restore.php

Documentation is available at restore.php

  1. <?php
  2. /**
  3.  * Akeeba Restore
  4.  * A JSON-powered JPA, JPS and ZIP archive extraction library
  5.  * 
  6.  * @copyright 2010-2012 Nicholas K. Dionysopoulos / AkeebaBackup.com
  7.  * @license GNU GPL v2 or - at your option - any later version
  8.  * @package akeebabackup
  9.  * @subpackage kickstart
  10.  */
  11.  
  12. define('_AKEEBA_RESTORATION'1);
  13. defined('DS'or define('DS'DIRECTORY_SEPARATOR);
  14.  
  15. // Unarchiver run states
  16. define('AK_STATE_NOFILE',    0)// File header not read yet
  17. define('AK_STATE_HEADER',    1)// File header read; ready to process data
  18. define('AK_STATE_DATA',        2)// Processing file data
  19. define('AK_STATE_DATAREAD',    3)// Finished processing file data; ready to post-process
  20. define('AK_STATE_POSTPROC',    4)// Post-processing
  21. define('AK_STATE_DONE',        5)// Done with post-processing
  22.  
  23. /* Windows system detection */
  24. if(!defined('_AKEEBA_IS_WINDOWS'))
  25. {
  26.     if (function_exists('php_uname'))
  27.         define('_AKEEBA_IS_WINDOWS'stristr(php_uname()'windows'));
  28.     else
  29.         define('_AKEEBA_IS_WINDOWS'DIRECTORY_SEPARATOR == '\\');
  30. }
  31.  
  32. // Make sure the locale is correct for basename() to work
  33. if(function_exists('setlocale'))
  34. {
  35.     @setlocale(LC_ALL'en_US.UTF8');
  36. }
  37.  
  38. // fnmatch not available on non-POSIX systems
  39. // Thanks to soywiz@php.net for this usefull alternative function [http://gr2.php.net/fnmatch]
  40. if (!function_exists('fnmatch')) {
  41.     function fnmatch($pattern$string{
  42.         return @preg_match(
  43.             '/^' strtr(addcslashes($pattern'/\\.+^$(){}=!<>|'),
  44.         array('*' => '.*''?' => '.?')) '$/i'$string
  45.         );
  46.     }
  47. }
  48.  
  49. // Unicode-safe binary data length function
  50. if(function_exists('mb_strlen')) {
  51.     function akstringlen($stringreturn mb_strlen($string,'8bit')}
  52. else {
  53.     function akstringlen($stringreturn strlen($string)}
  54. }
  55.  
  56. /**
  57.  * Gets a query parameter from GET or POST data
  58.  * @param $key 
  59.  * @param $default 
  60.  */
  61. function getQueryParam$key$default null )
  62. {
  63.     $value null;
  64.  
  65.     if(array_key_exists($key$_REQUEST)) {
  66.         $value $_REQUEST[$key];
  67.     elseif(array_key_exists($key$_POST)) {
  68.         $value $_POST[$key];
  69.     elseif(array_key_exists($key$_GET)) {
  70.         $value $_GET[$key];
  71.     else {
  72.         return $default;
  73.     }
  74.  
  75.     if(get_magic_quotes_gpc(&& !is_null($value)) $value=stripslashes($value);
  76.  
  77.     return $value;
  78. }
  79.  
  80. /**
  81.  * Akeeba Backup's JSON compatibility layer
  82.  *
  83.  * On systems where json_encode and json_decode are not available, Akeeba
  84.  * Backup will attempt to use PEAR's Services_JSON library to emulate them.
  85.  * A copy of this library is included in this file and will be used if and
  86.  * only if it isn't already loaded, e.g. due to PEAR's auto-loading, or a
  87.  * 3PD extension loading it for its own purposes.
  88.  */
  89.  
  90. /**
  91.  * Converts to and from JSON format.
  92.  *
  93.  * JSON (JavaScript Object Notation) is a lightweight data-interchange
  94.  * format. It is easy for humans to read and write. It is easy for machines
  95.  * to parse and generate. It is based on a subset of the JavaScript
  96.  * Programming Language, Standard ECMA-262 3rd Edition - December 1999.
  97.  * This feature can also be found in  Python. JSON is a text format that is
  98.  * completely language independent but uses conventions that are familiar
  99.  * to programmers of the C-family of languages, including C, C++, C#, Java,
  100.  * JavaScript, Perl, TCL, and many others. These properties make JSON an
  101.  * ideal data-interchange language.
  102.  *
  103.  * This package provides a simple encoder and decoder for JSON notation. It
  104.  * is intended for use with client-side Javascript applications that make
  105.  * use of HTTPRequest to perform server communication functions - data can
  106.  * be encoded into JSON notation for use in a client-side javascript, or
  107.  * decoded from incoming Javascript requests. JSON format is native to
  108.  * Javascript, and can be directly eval()'ed with no further parsing
  109.  * overhead
  110.  *
  111.  * All strings should be in ASCII or UTF-8 format!
  112.  *
  113.  * LICENSE: Redistribution and use in source and binary forms, with or
  114.  * without modification, are permitted provided that the following
  115.  * conditions are met: Redistributions of source code must retain the
  116.  * above copyright notice, this list of conditions and the following
  117.  * disclaimer. Redistributions in binary form must reproduce the above
  118.  * copyright notice, this list of conditions and the following disclaimer
  119.  * in the documentation and/or other materials provided with the
  120.  * distribution.
  121.  *
  122.  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
  123.  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
  124.  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
  125.  * NO EVENT SHALL CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  126.  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  127.  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
  128.  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  129.  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
  130.  * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
  131.  * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
  132.  * DAMAGE.
  133.  *
  134.  * @category
  135.  * @package     Services_JSON
  136.  * @author      Michal Migurski <mike-json@teczno.com>
  137.  * @author      Matt Knapp <mdknapp[at]gmail[dot]com>
  138.  * @author      Brett Stimmerman <brettstimmerman[at]gmail[dot]com>
  139.  * @copyright   2005 Michal Migurski
  140.  * @version     CVS: $Id: restore.php 612 2011-05-19 08:26:26Z nikosdion $
  141.  * @license     http://www.opensource.org/licenses/bsd-license.php
  142.  * @link        http://pear.php.net/pepr/pepr-proposal-show.php?id=198
  143.  */
  144.  
  145. if(!defined('JSON_FORCE_OBJECT'))
  146. {
  147.     define('JSON_FORCE_OBJECT'1);
  148. }
  149.  
  150. if(!defined('SERVICES_JSON_SLICE'))
  151. {
  152.     /**
  153.      * Marker constant for Services_JSON::decode(), used to flag stack state
  154.      */
  155.     define('SERVICES_JSON_SLICE',   1);
  156.  
  157.     /**
  158.      * Marker constant for Services_JSON::decode(), used to flag stack state
  159.      */
  160.     define('SERVICES_JSON_IN_STR',  2);
  161.  
  162.     /**
  163.      * Marker constant for Services_JSON::decode(), used to flag stack state
  164.      */
  165.     define('SERVICES_JSON_IN_ARR',  3);
  166.  
  167.     /**
  168.      * Marker constant for Services_JSON::decode(), used to flag stack state
  169.      */
  170.     define('SERVICES_JSON_IN_OBJ',  4);
  171.  
  172.     /**
  173.      * Marker constant for Services_JSON::decode(), used to flag stack state
  174.      */
  175.     define('SERVICES_JSON_IN_CMT'5);
  176.  
  177.     /**
  178.      * Behavior switch for Services_JSON::decode()
  179.      */
  180.     define('SERVICES_JSON_LOOSE_TYPE'16);
  181.  
  182.     /**
  183.      * Behavior switch for Services_JSON::decode()
  184.      */
  185.     define('SERVICES_JSON_SUPPRESS_ERRORS'32);
  186. }
  187.  
  188. /**
  189.  * Converts to and from JSON format.
  190.  *
  191.  * Brief example of use:
  192.  *
  193.  * <code>
  194.  * // create a new instance of Services_JSON
  195.  * $json = new Services_JSON();
  196.  *
  197.  * // convert a complexe value to JSON notation, and send it to the browser
  198.  * $value = array('foo', 'bar', array(1, 2, 'baz'), array(3, array(4)));
  199.  * $output = $json->encode($value);
  200.  *
  201.  * print($output);
  202.  * // prints: ["foo","bar",[1,2,"baz"],[3,[4]]]
  203.  *
  204.  * // accept incoming POST data, assumed to be in JSON notation
  205.  * $input = file_get_contents('php://input', 1000000);
  206.  * $value = $json->decode($input);
  207.  * </code>
  208.  */
  209. if(!class_exists('Akeeba_Services_JSON'))
  210. {
  211.     class Akeeba_Services_JSON
  212.     {
  213.        /**
  214.         * constructs a new JSON instance
  215.         *
  216.         * @param    int     $use    object behavior flags; combine with boolean-OR
  217.         *
  218.         *                            possible values:
  219.         *                            - SERVICES_JSON_LOOSE_TYPE:  loose typing.
  220.         *                                    "{...}" syntax creates associative arrays
  221.         *                                    instead of objects in decode().
  222.         *                            - SERVICES_JSON_SUPPRESS_ERRORS:  error suppression.
  223.         *                                    Values which can't be encoded (e.g. resources)
  224.         *                                    appear as NULL instead of throwing errors.
  225.         *                                    By default, a deeply-nested resource will
  226.         *                                    bubble up with an error, so all return values
  227.         *                                    from encode() should be checked with isError()
  228.         */
  229.         function Akeeba_Services_JSON($use 0)
  230.         {
  231.             $this->use $use;
  232.         }
  233.  
  234.        /**
  235.         * convert a string from one UTF-16 char to one UTF-8 char
  236.         *
  237.         * Normally should be handled by mb_convert_encoding, but
  238.         * provides a slower PHP-only method for installations
  239.         * that lack the multibye string extension.
  240.         *
  241.         * @param    string  $utf16  UTF-16 character
  242.         * @return   string  UTF-8 character
  243.         * @access   private
  244.         */
  245.         function utf162utf8($utf16)
  246.         {
  247.             // oh please oh please oh please oh please oh please
  248.             if(function_exists('mb_convert_encoding')) {
  249.                 return mb_convert_encoding($utf16'UTF-8''UTF-16');
  250.             }
  251.  
  252.             $bytes (ord($utf16{0}<< 8ord($utf16{1});
  253.  
  254.             switch(true{
  255.                 case ((0x7F $bytes== $bytes):
  256.                     // this case should never be reached, because we are in ASCII range
  257.                     // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
  258.                     return chr(0x7F $bytes);
  259.  
  260.                 case (0x07FF $bytes== $bytes:
  261.                     // return a 2-byte UTF-8 character
  262.                     // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
  263.                     return chr(0xC0 (($bytes >> 60x1F))
  264.                          . chr(0x80 ($bytes 0x3F));
  265.  
  266.                 case (0xFFFF $bytes== $bytes:
  267.                     // return a 3-byte UTF-8 character
  268.                     // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
  269.                     return chr(0xE0 (($bytes >> 120x0F))
  270.                          . chr(0x80 (($bytes >> 60x3F))
  271.                          . chr(0x80 ($bytes 0x3F));
  272.             }
  273.  
  274.             // ignoring UTF-32 for now, sorry
  275.             return '';
  276.         }
  277.  
  278.        /**
  279.         * convert a string from one UTF-8 char to one UTF-16 char
  280.         *
  281.         * Normally should be handled by mb_convert_encoding, but
  282.         * provides a slower PHP-only method for installations
  283.         * that lack the multibye string extension.
  284.         *
  285.         * @param    string  $utf8   UTF-8 character
  286.         * @return   string  UTF-16 character
  287.         * @access   private
  288.         */
  289.         function utf82utf16($utf8)
  290.         {
  291.             // oh please oh please oh please oh please oh please
  292.             if(function_exists('mb_convert_encoding')) {
  293.                 return mb_convert_encoding($utf8'UTF-16''UTF-8');
  294.             }
  295.  
  296.             switch(strlen($utf8)) {
  297.                 case 1:
  298.                     // this case should never be reached, because we are in ASCII range
  299.                     // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
  300.                     return $utf8;
  301.  
  302.                 case 2:
  303.                     // return a UTF-16 character from a 2-byte UTF-8 char
  304.                     // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
  305.                     return chr(0x07 (ord($utf8{0}>> 2))
  306.                          . chr((0xC0 (ord($utf8{0}<< 6))
  307.                              | (0x3F ord($utf8{1})));
  308.  
  309.                 case 3:
  310.                     // return a UTF-16 character from a 3-byte UTF-8 char
  311.                     // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
  312.                     return chr((0xF0 (ord($utf8{0}<< 4))
  313.                              | (0x0F (ord($utf8{1}>> 2)))
  314.                          . chr((0xC0 (ord($utf8{1}<< 6))
  315.                              | (0x7F ord($utf8{2})));
  316.             }
  317.  
  318.             // ignoring UTF-32 for now, sorry
  319.             return '';
  320.         }
  321.  
  322.        /**
  323.         * encodes an arbitrary variable into JSON format
  324.         *
  325.         * @param    mixed   $var    any number, boolean, string, array, or object to be encoded.
  326.         *                            see argument 1 to Services_JSON() above for array-parsing behavior.
  327.         *                            if var is a strng, note that encode() always expects it
  328.         *                            to be in ASCII or UTF-8 format!
  329.         *
  330.         * @return   mixed   JSON string representation of input var or an error if a problem occurs
  331.         * @access   public
  332.         */
  333.         function encode($var)
  334.         {
  335.             switch (gettype($var)) {
  336.                 case 'boolean':
  337.                     return $var 'true' 'false';
  338.  
  339.                 case 'NULL':
  340.                     return 'null';
  341.  
  342.                 case 'integer':
  343.                     return (int) $var;
  344.  
  345.                 case 'double':
  346.                 case 'float':
  347.                     return (float) $var;
  348.  
  349.                 case 'string':
  350.                     // STRINGS ARE EXPECTED TO BE IN ASCII OR UTF-8 FORMAT
  351.                     $ascii '';
  352.                     $strlen_var strlen($var);
  353.  
  354.                    /*
  355.                     * Iterate over every character in the string,
  356.                     * escaping with a slash or encoding to UTF-8 where necessary
  357.                     */
  358.                     for ($c 0$c $strlen_var++$c{
  359.  
  360.                         $ord_var_c ord($var{$c});
  361.  
  362.                         switch (true{
  363.                             case $ord_var_c == 0x08:
  364.                                 $ascii .= '\b';
  365.                                 break;
  366.                             case $ord_var_c == 0x09:
  367.                                 $ascii .= '\t';
  368.                                 break;
  369.                             case $ord_var_c == 0x0A:
  370.                                 $ascii .= '\n';
  371.                                 break;
  372.                             case $ord_var_c == 0x0C:
  373.                                 $ascii .= '\f';
  374.                                 break;
  375.                             case $ord_var_c == 0x0D:
  376.                                 $ascii .= '\r';
  377.                                 break;
  378.  
  379.                             case $ord_var_c == 0x22:
  380.                             case $ord_var_c == 0x2F:
  381.                             case $ord_var_c == 0x5C:
  382.                                 // double quote, slash, slosh
  383.                                 $ascii .= '\\'.$var{$c};
  384.                                 break;
  385.  
  386.                             case (($ord_var_c >= 0x20&& ($ord_var_c <= 0x7F)):
  387.                                 // characters U-00000000 - U-0000007F (same as ASCII)
  388.                                 $ascii .= $var{$c};
  389.                                 break;
  390.  
  391.                             case (($ord_var_c 0xE0== 0xC0):
  392.                                 // characters U-00000080 - U-000007FF, mask 110XXXXX
  393.                                 // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
  394.                                 $char pack('C*'$ord_var_cord($var{$c 1}));
  395.                                 $c += 1;
  396.                                 $utf16 $this->utf82utf16($char);
  397.                                 $ascii .= sprintf('\u%04s'bin2hex($utf16));
  398.                                 break;
  399.  
  400.                             case (($ord_var_c 0xF0== 0xE0):
  401.                                 // characters U-00000800 - U-0000FFFF, mask 1110XXXX
  402.                                 // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
  403.                                 $char pack('C*'$ord_var_c,
  404.                                              ord($var{$c 1}),
  405.                                              ord($var{$c 2}));
  406.                                 $c += 2;
  407.                                 $utf16 $this->utf82utf16($char);
  408.                                 $ascii .= sprintf('\u%04s'bin2hex($utf16));
  409.                                 break;
  410.  
  411.                             case (($ord_var_c 0xF8== 0xF0):
  412.                                 // characters U-00010000 - U-001FFFFF, mask 11110XXX
  413.                                 // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
  414.                                 $char pack('C*'$ord_var_c,
  415.                                              ord($var{$c 1}),
  416.                                              ord($var{$c 2}),
  417.                                              ord($var{$c 3}));
  418.                                 $c += 3;
  419.                                 $utf16 $this->utf82utf16($char);
  420.                                 $ascii .= sprintf('\u%04s'bin2hex($utf16));
  421.                                 break;
  422.  
  423.                             case (($ord_var_c 0xFC== 0xF8):
  424.                                 // characters U-00200000 - U-03FFFFFF, mask 111110XX
  425.                                 // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
  426.                                 $char pack('C*'$ord_var_c,
  427.                                              ord($var{$c 1}),
  428.                                              ord($var{$c 2}),
  429.                                              ord($var{$c 3}),
  430.                                              ord($var{$c 4}));
  431.                                 $c += 4;
  432.                                 $utf16 $this->utf82utf16($char);
  433.                                 $ascii .= sprintf('\u%04s'bin2hex($utf16));
  434.                                 break;
  435.  
  436.                             case (($ord_var_c 0xFE== 0xFC):
  437.                                 // characters U-04000000 - U-7FFFFFFF, mask 1111110X
  438.                                 // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
  439.                                 $char pack('C*'$ord_var_c,
  440.                                              ord($var{$c 1}),
  441.                                              ord($var{$c 2}),
  442.                                              ord($var{$c 3}),
  443.                                              ord($var{$c 4}),
  444.                                              ord($var{$c 5}));
  445.                                 $c += 5;
  446.                                 $utf16 $this->utf82utf16($char);
  447.                                 $ascii .= sprintf('\u%04s'bin2hex($utf16));
  448.                                 break;
  449.                         }
  450.                     }
  451.  
  452.                     return '"'.$ascii.'"';
  453.  
  454.                 case 'array':
  455.                    /*
  456.                     * As per JSON spec if any array key is not an integer
  457.                     * we must treat the the whole array as an object. We
  458.                     * also try to catch a sparsely populated associative
  459.                     * array with numeric keys here because some JS engines
  460.                     * will create an array with empty indexes up to
  461.                     * max_index which can cause memory issues and because
  462.                     * the keys, which may be relevant, will be remapped
  463.                     * otherwise.
  464.                     *
  465.                     * As per the ECMA and JSON specification an object may
  466.                     * have any string as a property. Unfortunately due to
  467.                     * a hole in the ECMA specification if the key is a
  468.                     * ECMA reserved word or starts with a digit the
  469.                     * parameter is only accessible using ECMAScript's
  470.                     * bracket notation.
  471.                     */
  472.  
  473.                     // treat as a JSON object
  474.                     if (is_array($var&& count($var&& (array_keys($var!== range(0sizeof($var1))) {
  475.                         $properties array_map(array($this'name_value'),
  476.                                                 array_keys($var),
  477.                                                 array_values($var));
  478.  
  479.                         foreach($properties as $property{
  480.                             if(Akeeba_Services_JSON::isError($property)) {
  481.                                 return $property;
  482.                             }
  483.                         }
  484.  
  485.                         return '{' join(','$properties'}';
  486.                     }
  487.  
  488.                     // treat it like a regular array
  489.                     $elements array_map(array($this'encode')$var);
  490.  
  491.                     foreach($elements as $element{
  492.                         if(Akeeba_Services_JSON::isError($element)) {
  493.                             return $element;
  494.                         }
  495.                     }
  496.  
  497.                     return '[' join(','$elements']';
  498.  
  499.                 case 'object':
  500.                     $vars get_object_vars($var);
  501.  
  502.                     $properties array_map(array($this'name_value'),
  503.                                             array_keys($vars),
  504.                                             array_values($vars));
  505.  
  506.                     foreach($properties as $property{
  507.                         if(Akeeba_Services_JSON::isError($property)) {
  508.                             return $property;
  509.                         }
  510.                     }
  511.  
  512.                     return '{' join(','$properties'}';
  513.  
  514.                 default:
  515.                     return ($this->use SERVICES_JSON_SUPPRESS_ERRORS)
  516.                         ? 'null'
  517.                         : new Akeeba_Services_JSON_Error(gettype($var)." can not be encoded as JSON string");
  518.             }
  519.         }
  520.  
  521.        /**
  522.         * array-walking function for use in generating JSON-formatted name-value pairs
  523.         *
  524.         * @param    string  $name   name of key to use
  525.         * @param    mixed   $value  reference to an array element to be encoded
  526.         *
  527.         * @return   string  JSON-formatted name-value pair, like '"name":value'
  528.         * @access   private
  529.         */
  530.         function name_value($name$value)
  531.         {
  532.             $encoded_value $this->encode($value);
  533.  
  534.             if(Akeeba_Services_JSON::isError($encoded_value)) {
  535.                 return $encoded_value;
  536.             }
  537.  
  538.             return $this->encode(strval($name)) ':' $encoded_value;
  539.         }
  540.  
  541.        /**
  542.         * reduce a string by removing leading and trailing comments and whitespace
  543.         *
  544.         * @param    $str    string      string value to strip of comments and whitespace
  545.         *
  546.         * @return   string  string value stripped of comments and whitespace
  547.         * @access   private
  548.         */
  549.         function reduce_string($str)
  550.         {
  551.             $str preg_replace(array(
  552.  
  553.                     // eliminate single line comments in '// ...' form
  554.                     '#^\s*//(.+)$#m',
  555.  
  556.                     // eliminate multi-line comments in '/* ... */' form, at start of string
  557.                     '#^\s*/\*(.+)\*/#Us',
  558.  
  559.                     // eliminate multi-line comments in '/* ... */' form, at end of string
  560.                     '#/\*(.+)\*/\s*$#Us'
  561.  
  562.                 )''$str);
  563.  
  564.             // eliminate extraneous space
  565.             return trim($str);
  566.         }
  567.  
  568.        /**
  569.         * decodes a JSON string into appropriate variable
  570.         *
  571.         * @param    string  $str    JSON-formatted string
  572.         *
  573.         * @return   mixed   number, boolean, string, array, or object
  574.         *                    corresponding to given JSON input string.
  575.         *                    See argument 1 to Akeeba_Services_JSON() above for object-output behavior.
  576.         *                    Note that decode() always returns strings
  577.         *                    in ASCII or UTF-8 format!
  578.         * @access   public
  579.         */
  580.         function decode($str)
  581.         {
  582.             $str $this->reduce_string($str);
  583.  
  584.             switch (strtolower($str)) {
  585.                 case 'true':
  586.                     return true;
  587.  
  588.                 case 'false':
  589.                     return false;
  590.  
  591.                 case 'null':
  592.                     return null;
  593.  
  594.                 default:
  595.                     $m array();
  596.  
  597.                     if (is_numeric($str)) {
  598.                         // Lookie-loo, it's a number
  599.  
  600.                         // This would work on its own, but I'm trying to be
  601.                         // good about returning integers where appropriate:
  602.                         // return (float)$str;
  603.  
  604.                         // Return float or int, as appropriate
  605.                         return ((float)$str == (integer)$str)
  606.                             ? (integer)$str
  607.                             : (float)$str;
  608.  
  609.                     elseif (preg_match('/^("|\').*(\1)$/s'$str$m&& $m[1== $m[2]{
  610.                         // STRINGS RETURNED IN UTF-8 FORMAT
  611.                         $delim substr($str01);
  612.                         $chrs substr($str1-1);
  613.                         $utf8 '';
  614.                         $strlen_chrs strlen($chrs);
  615.  
  616.                         for ($c 0$c $strlen_chrs++$c{
  617.  
  618.                             $substr_chrs_c_2 substr($chrs$c2);
  619.                             $ord_chrs_c ord($chrs{$c});
  620.  
  621.                             switch (true{
  622.                                 case $substr_chrs_c_2 == '\b':
  623.                                     $utf8 .= chr(0x08);
  624.                                     ++$c;
  625.                                     break;
  626.                                 case $substr_chrs_c_2 == '\t':
  627.                                     $utf8 .= chr(0x09);
  628.                                     ++$c;
  629.                                     break;
  630.                                 case $substr_chrs_c_2 == '\n':
  631.                                     $utf8 .= chr(0x0A);
  632.                                     ++$c;
  633.                                     break;
  634.                                 case $substr_chrs_c_2 == '\f':
  635.                                     $utf8 .= chr(0x0C);
  636.                                     ++$c;
  637.                                     break;
  638.                                 case $substr_chrs_c_2 == '\r':
  639.                                     $utf8 .= chr(0x0D);
  640.                                     ++$c;
  641.                                     break;
  642.  
  643.                                 case $substr_chrs_c_2 == '\\"':
  644.                                 case $substr_chrs_c_2 == '\\\'':
  645.                                 case $substr_chrs_c_2 == '\\\\':
  646.                                 case $substr_chrs_c_2 == '\\/':
  647.                                     if (($delim == '"' && $substr_chrs_c_2 != '\\\''||
  648.                                        ($delim == "'" && $substr_chrs_c_2 != '\\"')) {
  649.                                         $utf8 .= $chrs{++$c};
  650.                                     }
  651.                                     break;
  652.  
  653.                                 case preg_match('/\\\u[0-9A-F]{4}/i'substr($chrs$c6)):
  654.                                     // single, escaped unicode character
  655.                                     $utf16 chr(hexdec(substr($chrs($c 2)2)))
  656.                                            . chr(hexdec(substr($chrs($c 4)2)));
  657.                                     $utf8 .= $this->utf162utf8($utf16);
  658.                                     $c += 5;
  659.                                     break;
  660.  
  661.                                 case ($ord_chrs_c >= 0x20&& ($ord_chrs_c <= 0x7F):
  662.                                     $utf8 .= $chrs{$c};
  663.                                     break;
  664.  
  665.                                 case ($ord_chrs_c 0xE0== 0xC0:
  666.                                     // characters U-00000080 - U-000007FF, mask 110XXXXX
  667.                                     //see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
  668.                                     $utf8 .= substr($chrs$c2);
  669.                                     ++$c;
  670.                                     break;
  671.  
  672.                                 case ($ord_chrs_c 0xF0== 0xE0:
  673.                                     // characters U-00000800 - U-0000FFFF, mask 1110XXXX
  674.                                     // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
  675.                                     $utf8 .= substr($chrs$c3);
  676.                                     $c += 2;
  677.                                     break;
  678.  
  679.                                 case ($ord_chrs_c 0xF8== 0xF0:
  680.                                     // characters U-00010000 - U-001FFFFF, mask 11110XXX
  681.                                     // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
  682.                                     $utf8 .= substr($chrs$c4);
  683.                                     $c += 3;
  684.                                     break;
  685.  
  686.                                 case ($ord_chrs_c 0xFC== 0xF8:
  687.                                     // characters U-00200000 - U-03FFFFFF, mask 111110XX
  688.                                     // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
  689.                                     $utf8 .= substr($chrs$c5);
  690.                                     $c += 4;
  691.                                     break;
  692.  
  693.                                 case ($ord_chrs_c 0xFE== 0xFC:
  694.                                     // characters U-04000000 - U-7FFFFFFF, mask 1111110X
  695.                                     // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
  696.                                     $utf8 .= substr($chrs$c6);
  697.                                     $c += 5;
  698.                                     break;
  699.  
  700.                             }
  701.  
  702.                         }
  703.  
  704.                         return $utf8;
  705.  
  706.                     elseif (preg_match('/^\[.*\]$/s'$str|| preg_match('/^\{.*\}$/s'$str)) {
  707.                         // array, or object notation
  708.  
  709.                         if ($str{0== '['{
  710.                             $stk array(SERVICES_JSON_IN_ARR);
  711.                             $arr array();
  712.                         else {
  713.                             if ($this->use SERVICES_JSON_LOOSE_TYPE{
  714.                                 $stk array(SERVICES_JSON_IN_OBJ);
  715.                                 $obj array();
  716.                             else {
  717.                                 $stk array(SERVICES_JSON_IN_OBJ);
  718.                                 $obj new stdClass();
  719.                             }
  720.                         }
  721.  
  722.                         array_push($stkarray('what'  => SERVICES_JSON_SLICE,
  723.                                                'where' => 0,
  724.                                                'delim' => false));
  725.  
  726.                         $chrs substr($str1-1);
  727.                         $chrs $this->reduce_string($chrs);
  728.  
  729.                         if ($chrs == ''{
  730.                             if (reset($stk== SERVICES_JSON_IN_ARR{
  731.                                 return $arr;
  732.  
  733.                             else {
  734.                                 return $obj;
  735.  
  736.                             }
  737.                         }
  738.  
  739.                         //print("\nparsing {$chrs}\n");
  740.  
  741.                         $strlen_chrs strlen($chrs);
  742.  
  743.                         for ($c 0$c <= $strlen_chrs++$c{
  744.  
  745.                             $top end($stk);
  746.                             $substr_chrs_c_2 substr($chrs$c2);
  747.  
  748.                             if (($c == $strlen_chrs|| (($chrs{$c== ','&& ($top['what'== SERVICES_JSON_SLICE))) {
  749.                                 // found a comma that is not inside a string, array, etc.,
  750.                                 // OR we've reached the end of the character list
  751.                                 $slice substr($chrs$top['where']($c $top['where']));
  752.                                 array_push($stkarray('what' => SERVICES_JSON_SLICE'where' => ($c 1)'delim' => false));
  753.                                 //print("Found split at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
  754.  
  755.                                 if (reset($stk== SERVICES_JSON_IN_ARR{
  756.                                     // we are in an array, so just push an element onto the stack
  757.                                     array_push($arr$this->decode($slice));
  758.  
  759.                                 elseif (reset($stk== SERVICES_JSON_IN_OBJ{
  760.                                     // we are in an object, so figure
  761.                                     // out the property name and set an
  762.                                     // element in an associative array,
  763.                                     // for now
  764.                                     $parts array();
  765.  
  766.                                     if (preg_match('/^\s*(["\'].*[^\\\]["\'])\s*:\s*(\S.*),?$/Uis'$slice$parts)) {
  767.                                         // "name":value pair
  768.                                         $key $this->decode($parts[1]);
  769.                                         $val $this->decode($parts[2]);
  770.  
  771.                                         if ($this->use SERVICES_JSON_LOOSE_TYPE{
  772.                                             $obj[$key$val;
  773.                                         else {
  774.                                             $obj->$key $val;
  775.                                         }
  776.                                     elseif (preg_match('/^\s*(\w+)\s*:\s*(\S.*),?$/Uis'$slice$parts)) {
  777.                                         // name:value pair, where name is unquoted
  778.                                         $key $parts[1];
  779.                                         $val $this->decode($parts[2]);
  780.  
  781.                                         if ($this->use SERVICES_JSON_LOOSE_TYPE{
  782.                                             $obj[$key$val;
  783.                                         else {
  784.                                             $obj->$key $val;
  785.                                         }
  786.                                     }
  787.  
  788.                                 }
  789.  
  790.                             elseif ((($chrs{$c== '"'|| ($chrs{$c== "'")) && ($top['what'!= SERVICES_JSON_IN_STR)) {
  791.                                 // found a quote, and we are not inside a string
  792.                                 array_push($stkarray('what' => SERVICES_JSON_IN_STR'where' => $c'delim' => $chrs{$c}));
  793.                                 //print("Found start of string at {$c}\n");
  794.  
  795.                             elseif (($chrs{$c== $top['delim']&&
  796.                                      ($top['what'== SERVICES_JSON_IN_STR&&
  797.                                      ((strlen(substr($chrs0$c)) strlen(rtrim(substr($chrs0$c)'\\'))) != 1)) {
  798.                                 // found a quote, we're in a string, and it's not escaped
  799.                                 // we know that it's not escaped becase there is _not_ an
  800.                                 // odd number of backslashes at the end of the string so far
  801.                                 array_pop($stk);
  802.                                 //print("Found end of string at {$c}: ".substr($chrs, $top['where'], (1 + 1 + $c - $top['where']))."\n");
  803.  
  804.                             elseif (($chrs{$c== '['&&
  805.                                      in_array($top['what']array(SERVICES_JSON_SLICESERVICES_JSON_IN_ARRSERVICES_JSON_IN_OBJ))) {
  806.                                 // found a left-bracket, and we are in an array, object, or slice
  807.                                 array_push($stkarray('what' => SERVICES_JSON_IN_ARR'where' => $c'delim' => false));
  808.                                 //print("Found start of array at {$c}\n");
  809.  
  810.                             elseif (($chrs{$c== ']'&& ($top['what'== SERVICES_JSON_IN_ARR)) {
  811.                                 // found a right-bracket, and we're in an array
  812.                                 array_pop($stk);
  813.                                 //print("Found end of array at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
  814.  
  815.                             elseif (($chrs{$c== '{'&&
  816.                                      in_array($top['what']array(SERVICES_JSON_SLICESERVICES_JSON_IN_ARRSERVICES_JSON_IN_OBJ))) {
  817.                                 // found a left-brace, and we are in an array, object, or slice
  818.                                 array_push($stkarray('what' => SERVICES_JSON_IN_OBJ'where' => $c'delim' => false));
  819.                                 //print("Found start of object at {$c}\n");
  820.  
  821.                             elseif (($chrs{$c== '}'&& ($top['what'== SERVICES_JSON_IN_OBJ)) {
  822.                                 // found a right-brace, and we're in an object
  823.                                 array_pop($stk);
  824.                                 //print("Found end of object at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
  825.  
  826.                             elseif (($substr_chrs_c_2 == '/*'&&
  827.                                      in_array($top['what']array(SERVICES_JSON_SLICESERVICES_JSON_IN_ARRSERVICES_JSON_IN_OBJ))) {
  828.                                 // found a comment start, and we are in an array, object, or slice
  829.                                 array_push($stkarray('what' => SERVICES_JSON_IN_CMT'where' => $c'delim' => false));
  830.                                 $c++;
  831.                                 //print("Found start of comment at {$c}\n");
  832.  
  833.                             elseif (($substr_chrs_c_2 == '*/'&& ($top['what'== SERVICES_JSON_IN_CMT)) {
  834.                                 // found a comment end, and we're in one now
  835.                                 array_pop($stk);
  836.                                 $c++;
  837.  
  838.                                 for ($i $top['where']$i <= $c++$i)
  839.                                     $chrs substr_replace($chrs' '$i1);
  840.  
  841.                                 //print("Found end of comment at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
  842.  
  843.                             }
  844.  
  845.                         }
  846.  
  847.                         if (reset($stk== SERVICES_JSON_IN_ARR{
  848.                             return $arr;
  849.  
  850.                         elseif (reset($stk== SERVICES_JSON_IN_OBJ{
  851.                             return $obj;
  852.  
  853.                         }
  854.  
  855.                     }
  856.             }
  857.         }
  858.  
  859.         function isError($data$code null)
  860.         {
  861.             if (class_exists('pear')) {
  862.                 return PEAR::isError($data$code);
  863.             elseif (is_object($data&& (get_class($data== 'services_json_error' ||
  864.                                      is_subclass_of($data'services_json_error'))) {
  865.                 return true;
  866.             }
  867.  
  868.             return false;
  869.         }
  870.     }
  871.  
  872.     {
  873.         function Akeeba_Services_JSON_Error($message 'unknown error'$code null,
  874.                                      $mode null$options null$userinfo null)
  875.         {
  876.  
  877.         }
  878.     }
  879. }
  880.  
  881. if(!function_exists('json_encode'))
  882. {
  883.     function json_encode($value$options 0{
  884.         $flags SERVICES_JSON_LOOSE_TYPE;
  885.         if$options JSON_FORCE_OBJECT $flags 0;
  886.         $encoder new Akeeba_Services_JSON($flags);
  887.         return $encoder->encode($value);
  888.     }
  889. }
  890.  
  891. if(!function_exists('json_decode'))
  892. {
  893.     function json_decode($value$assoc false)
  894.     {
  895.         $flags 0;
  896.         if($assoc$flags SERVICES_JSON_LOOSE_TYPE;
  897.         $decoder new Akeeba_Services_JSON($flags);
  898.         return $decoder->decode($value);
  899.     }
  900. }
  901.  
  902. /**
  903.  * The base class of Akeeba Engine objects. Allows for error and warnings logging
  904.  * and propagation. Largely based on the Joomla! 1.5 JObject class.
  905.  */
  906. abstract class AKAbstractObject
  907. {
  908.     /** @var    array    An array of errors */
  909.     private $_errors array();
  910.  
  911.     /** @var    array    The queue size of the $_errors array. Set to 0 for infinite size. */
  912.     protected $_errors_queue_size = 0;
  913.  
  914.     /** @var    array    An array of warnings */
  915.     private $_warnings array();
  916.  
  917.     /** @var    array    The queue size of the $_warnings array. Set to 0 for infinite size. */
  918.     protected $_warnings_queue_size = 0;
  919.  
  920.     /**
  921.      * Public constructor, makes sure we are instanciated only by the factory class
  922.      */
  923.     public function __construct()
  924.     {
  925.         /*
  926.         // Assisted Singleton pattern
  927.         if(function_exists('debug_backtrace'))
  928.         {
  929.             $caller=debug_backtrace();
  930.             if(
  931.                 ($caller[1]['class'] != 'AKFactory') &&
  932.                 ($caller[2]['class'] != 'AKFactory') &&
  933.                 ($caller[3]['class'] != 'AKFactory') &&
  934.                 ($caller[4]['class'] != 'AKFactory')
  935.             ) {
  936.                 var_dump(debug_backtrace());
  937.                 trigger_error("You can't create direct descendants of ".__CLASS__, E_USER_ERROR);
  938.             }
  939.         }
  940.         */
  941.     }
  942.  
  943.     /**
  944.      * Get the most recent error message
  945.      * @param    integer    $i Optional error index
  946.      * @return    string    Error message
  947.      */
  948.     public function getError($i null)
  949.     {
  950.         return $this->getItemFromArray($this->_errors$i);
  951.     }
  952.  
  953.     /**
  954.      * Return all errors, if any
  955.      * @return    array    Array of error messages
  956.      */
  957.     public function getErrors()
  958.     {
  959.         return $this->_errors;
  960.     }
  961.  
  962.     /**
  963.      * Add an error message
  964.      * @param    string $error Error message
  965.      */
  966.     public function setError($error)
  967.     {
  968.         if($this->_errors_queue_size > 0)
  969.         {
  970.             if(count($this->_errors>= $this->_errors_queue_size)
  971.             {
  972.                 array_shift($this->_errors);
  973.             }
  974.         }
  975.         array_push($this->_errors$error);
  976.     }
  977.  
  978.     /**
  979.      * Resets all error messages
  980.      */
  981.     public function resetErrors()
  982.     {
  983.         $this->_errors array();
  984.     }
  985.  
  986.     /**
  987.      * Get the most recent warning message
  988.      * @param    integer    $i Optional warning index
  989.      * @return    string    Error message
  990.      */
  991.     public function getWarning($i null)
  992.     {
  993.         return $this->getItemFromArray($this->_warnings$i);
  994.     }
  995.  
  996.     /**
  997.      * Return all warnings, if any
  998.      * @return    array    Array of error messages
  999.      */
  1000.     public function getWarnings()
  1001.     {
  1002.         return $this->_warnings;
  1003.     }
  1004.  
  1005.     /**
  1006.      * Add an error message
  1007.      * @param    string $error Error message
  1008.      */
  1009.     public function setWarning($warning)
  1010.     {
  1011.         if($this->_warnings_queue_size > 0)
  1012.         {
  1013.             if(count($this->_warnings>= $this->_warnings_queue_size)
  1014.             {
  1015.                 array_shift($this->_warnings);
  1016.             }
  1017.         }
  1018.  
  1019.         array_push($this->_warnings$warning);
  1020.     }
  1021.  
  1022.     /**
  1023.      * Resets all warning messages
  1024.      */
  1025.     public function resetWarnings()
  1026.     {
  1027.         $this->_warnings array();
  1028.     }
  1029.  
  1030.     /**
  1031.      * Propagates errors and warnings to a foreign object. The foreign object SHOULD
  1032.      * implement the setError() and/or setWarning() methods but DOESN'T HAVE TO be of
  1033.      * AKAbstractObject type. For example, this can even be used to propagate to a
  1034.      * JObject instance in Joomla!. Propagated items will be removed from ourself.
  1035.      * @param object $object The object to propagate errors and warnings to.
  1036.      */
  1037.     public function propagateToObject(&$object)
  1038.     {
  1039.         // Skip non-objects
  1040.         if(!is_object($object)) return;
  1041.  
  1042.         ifmethod_exists($object,'setError') )
  1043.         {
  1044.             if(!empty($this->_errors))
  1045.             {
  1046.                 foreach($this->_errors as $error)
  1047.                 {
  1048.                     $object->setError($error);
  1049.                 }
  1050.                 $this->_errors array();
  1051.             }
  1052.         }
  1053.  
  1054.         ifmethod_exists($object,'setWarning') )
  1055.         {
  1056.             if(!empty($this->_warnings))
  1057.             {
  1058.                 foreach($this->_warnings as $warning)
  1059.                 {
  1060.                     $object->setWarning($warning);
  1061.                 }
  1062.                 $this->_warnings array();
  1063.             }
  1064.         }
  1065.     }
  1066.  
  1067.     /**
  1068.      * Propagates errors and warnings from a foreign object. Each propagated list is
  1069.      * then cleared on the foreign object, as long as it implements resetErrors() and/or
  1070.      * resetWarnings() methods.
  1071.      * @param object $object The object to propagate errors and warnings from
  1072.      */
  1073.     public function propagateFromObject(&$object)
  1074.     {
  1075.         ifmethod_exists($object,'getErrors') )
  1076.         {
  1077.             $errors $object->getErrors();
  1078.             if(!empty($errors))
  1079.             {
  1080.                 foreach($errors as $error)
  1081.                 {
  1082.                     $this->setError($error);
  1083.                 }
  1084.             }
  1085.             if(method_exists($object,'resetErrors'))
  1086.             {
  1087.                 $object->resetErrors();
  1088.             }
  1089.         }
  1090.  
  1091.         ifmethod_exists($object,'getWarnings') )
  1092.         {
  1093.             $warnings $object->getWarnings();
  1094.             if(!empty($warnings))
  1095.             {
  1096.                 foreach($warnings as $warning)
  1097.                 {
  1098.                     $this->setWarning($warning);
  1099.                 }
  1100.             }
  1101.             if(method_exists($object,'resetWarnings'))
  1102.             {
  1103.                 $object->resetWarnings();
  1104.             }
  1105.         }
  1106.     }
  1107.  
  1108.     /**
  1109.      * Sets the size of the error queue (acts like a LIFO buffer)
  1110.      * @param int $newSize The new queue size. Set to 0 for infinite length.
  1111.      */
  1112.     protected function setErrorsQueueSize($newSize 0)
  1113.     {
  1114.         $this->_errors_queue_size = (int)$newSize;
  1115.     }
  1116.  
  1117.     /**
  1118.      * Sets the size of the warnings queue (acts like a LIFO buffer)
  1119.      * @param int $newSize The new queue size. Set to 0 for infinite length.
  1120.      */
  1121.     protected function setWarningsQueueSize($newSize 0)
  1122.     {
  1123.         $this->_warnings_queue_size = (int)$newSize;
  1124.     }
  1125.  
  1126.     /**
  1127.      * Returns the last item of a LIFO string message queue, or a specific item
  1128.      * if so specified.
  1129.      * @param array $array An array of strings, holding messages
  1130.      * @param int $i Optional message index
  1131.      * @return mixed The message string, or false if the key doesn't exist
  1132.      */
  1133.     private function getItemFromArray($array$i null)
  1134.     {
  1135.         // Find the item
  1136.         if $i === null{
  1137.             // Default, return the last item
  1138.             $item end($array);
  1139.         }
  1140.         else
  1141.         if array_key_exists($i$array) ) {
  1142.             // If $i has been specified but does not exist, return false
  1143.             return false;
  1144.         }
  1145.         else
  1146.         {
  1147.             $item    $array[$i];
  1148.         }
  1149.  
  1150.         return $item;
  1151.     }
  1152.  
  1153. }
  1154.  
  1155. /**
  1156.  * File post processor engines base class
  1157.  */
  1158. abstract class AKAbstractPostproc extends AKAbstractObject
  1159. {
  1160.     /** @var string The current (real) file path we'll have to process */
  1161.     protected $filename = null;
  1162.  
  1163.     /** @var int The requested permissions */
  1164.     protected $perms = 0755;
  1165.  
  1166.     /** @var string The temporary file path we gave to the unarchiver engine */
  1167.     protected $tempFilename = null;
  1168.  
  1169.     /** @var int The UNIX timestamp of the file's desired modification date */
  1170.     public $timestamp = 0;
  1171.  
  1172.     /**
  1173.      * Processes the current file, e.g. moves it from temp to final location by FTP
  1174.      */
  1175.     abstract public function process();
  1176.  
  1177.     /**
  1178.      * The unarchiver tells us the path to the filename it wants to extract and we give it
  1179.      * a different path instead.
  1180.      * @param string $filename The path to the real file
  1181.      * @param int $perms The permissions we need the file to have
  1182.      * @return string The path to the temporary file
  1183.      */
  1184.     abstract public function processFilename($filename$perms 0755);
  1185.  
  1186.     /**
  1187.      * Recursively creates a directory if it doesn't exist
  1188.      * @param string $dirName The directory to create
  1189.      * @param int $perms The permissions to give to that directory
  1190.      */
  1191.     abstract public function createDirRecursive$dirName$perms );
  1192.  
  1193.     abstract public function chmod$file$perms );
  1194.  
  1195.     abstract public function unlink$file );
  1196.  
  1197.     abstract public function rmdir$directory );
  1198.  
  1199.     abstract public function rename$from$to );
  1200. }
  1201.  
  1202. /**
  1203.  * The base class of unarchiver classes
  1204.  */
  1205. abstract class AKAbstractUnarchiver extends AKAbstractPart
  1206. {
  1207.     /** @var string Archive filename */
  1208.     protected $filename = null;
  1209.  
  1210.     /** @var array List of the names of all archive parts */
  1211.     public $archiveList = array();
  1212.  
  1213.     /** @var int The total size of all archive parts */
  1214.     public $totalSize = array();
  1215.  
  1216.     /** @var integer Current archive part number */
  1217.     protected $currentPartNumber = -1;
  1218.  
  1219.     /** @var integer The offset inside the current part */
  1220.     protected $currentPartOffset = 0;
  1221.  
  1222.     /** @var bool Should I restore permissions? */
  1223.     protected $flagRestorePermissions = false;
  1224.  
  1225.     /** @var AKAbstractPostproc Post processing class */
  1226.     protected $postProcEngine = null;
  1227.  
  1228.     /** @var string Absolute path to prepend to extracted files */
  1229.     protected $addPath = '';
  1230.  
  1231.     /** @var array Which files to rename */
  1232.     public $renameFiles = array();
  1233.  
  1234.     /** @var array Which directories to rename */
  1235.     public $renameDirs = array();
  1236.  
  1237.     /** @var array Which files to skip */
  1238.     public $skipFiles = array();
  1239.  
  1240.     /** @var integer Chunk size for processing */
  1241.     protected $chunkSize = 524288;
  1242.  
  1243.     /** @var resource File pointer to the current archive part file */
  1244.     protected $fp = null;
  1245.  
  1246.     /** @var int Run state when processing the current archive file */
  1247.     protected $runState = null;
  1248.  
  1249.     /** @var stdClass File header data, as read by the readFileHeader() method */
  1250.     protected $fileHeader = null;
  1251.  
  1252.     /** @var int How much of the uncompressed data we've read so far */
  1253.     protected $dataReadLength = 0;
  1254.  
  1255.     /**
  1256.      * Public constructor
  1257.      */
  1258.     public function __construct()
  1259.     {
  1260.         parent::__construct();
  1261.     }
  1262.  
  1263.     /**
  1264.      * Wakeup function, called whenever the class is unserialized
  1265.      */
  1266.     public function __wakeup()
  1267.     {
  1268.         if($this->currentPartNumber >= 0)
  1269.         {
  1270.             $this->fp = @fopen($this->archiveList[$this->currentPartNumber]'rb');
  1271.             if( (is_resource($this->fp)) && ($this->currentPartOffset > 0) )
  1272.             {
  1273.                 @fseek($this->fp$this->currentPartOffset);
  1274.             }
  1275.         }
  1276.     }
  1277.  
  1278.     /**
  1279.      * Sleep function, called whenever the class is serialized
  1280.      */
  1281.     public function shutdown()
  1282.     {
  1283.         if(is_resource($this->fp))
  1284.         {
  1285.             $this->currentPartOffset = @ftell($this->fp);
  1286.             @fclose($this->fp);
  1287.         }
  1288.     }
  1289.  
  1290.     /**
  1291.      * Implements the abstract _prepare() method
  1292.      */
  1293.     final protected function _prepare()
  1294.     {
  1295.         parent::__construct();
  1296.  
  1297.         ifcount($this->_parametersArray)
  1298.         {
  1299.             foreach($this->_parametersArray as $key => $value)
  1300.             {
  1301.                 switch($key)
  1302.                 {
  1303.                     case 'filename'// Archive's absolute filename
  1304.                         $this->filename = $value;
  1305.                         break;
  1306.  
  1307.                     case 'restore_permissions'// Should I restore permissions?
  1308.                         $this->flagRestorePermissions = $value;
  1309.                         break;
  1310.  
  1311.                     case 'post_proc'// Should I use FTP?
  1312.                         $this->postProcEngine = AKFactory::getpostProc($value);
  1313.                         break;
  1314.  
  1315.                     case 'add_path'// Path to prepend
  1316.                         $this->addPath = $value;
  1317.                         $this->addPath = str_replace('\\','/',$this->addPath);
  1318.                         $this->addPath = rtrim($this->addPath,'/');
  1319.                         if(!empty($this->addPath)) $this->addPath .= '/';
  1320.                         break;
  1321.  
  1322.                     case 'rename_files'// Which files to rename (hash array)
  1323.                         $this->renameFiles = $value;
  1324.                         break;
  1325.                     
  1326.                     case 'rename_dirs'// Which files to rename (hash array)
  1327.                         $this->renameDirs = $value;
  1328.                         break;
  1329.  
  1330.                     case 'skip_files'// Which files to skip (indexed array)
  1331.                         $this->skipFiles = $value;
  1332.                         break;
  1333.                 }
  1334.             }
  1335.         }
  1336.  
  1337.         $this->scanArchives();
  1338.  
  1339.         $this->readArchiveHeader();
  1340.         $errMessage $this->getError();
  1341.         if(!empty($errMessage))
  1342.         {
  1343.             $this->setState('error'$errMessage);
  1344.         }
  1345.         else
  1346.         {
  1347.             $this->runState = AK_STATE_NOFILE;
  1348.             $this->setState('prepared');
  1349.         }
  1350.     }
  1351.  
  1352.     protected function _run()
  1353.     {
  1354.         if($this->getState(== 'postrun'return;
  1355.  
  1356.         $this->setState('running');
  1357.  
  1358.         $timer AKFactory::getTimer();
  1359.  
  1360.         $status true;
  1361.         while$status && ($timer->getTimeLeft(0) )
  1362.         {
  1363.             switch$this->runState )
  1364.             {
  1365.                 case AK_STATE_NOFILE:
  1366.                     $status $this->readFileHeader();
  1367.                     if($status)
  1368.                     {
  1369.                         // Send start of file notification
  1370.                         $message new stdClass;
  1371.                         $message->type 'startfile';
  1372.                         $message->content new stdClass;
  1373.                         ifarray_key_exists('realfile'get_object_vars($this->fileHeader)) ) {
  1374.                             $message->content->realfile $this->fileHeader->realFile;
  1375.                         else {
  1376.                             $message->content->realfile $this->fileHeader->file;
  1377.                         }
  1378.                         $message->content->file $this->fileHeader->file;
  1379.                         ifarray_key_exists('compressed'get_object_vars($this->fileHeader)) ) {
  1380.                             $message->content->compressed $this->fileHeader->compressed;
  1381.                         else {
  1382.                             $message->content->compressed 0;
  1383.                         }
  1384.                         $message->content->uncompressed $this->fileHeader->uncompressed;
  1385.                         $this->notify($message);
  1386.                     }
  1387.                     break;
  1388.  
  1389.                 case AK_STATE_HEADER:
  1390.                 case AK_STATE_DATA:
  1391.                     $status $this->processFileData();
  1392.                     break;
  1393.  
  1394.                 case AK_STATE_DATAREAD:
  1395.                 case AK_STATE_POSTPROC:
  1396.                     $this->postProcEngine->timestamp = $this->fileHeader->timestamp;
  1397.                     $status $this->postProcEngine->process();
  1398.                     $this->propagateFromObject$this->postProcEngine );
  1399.                     $this->runState = AK_STATE_DONE;
  1400.                     break;
  1401.  
  1402.                 case AK_STATE_DONE:
  1403.                 default:
  1404.                     if($status)
  1405.                     {
  1406.                         // Send end of file notification
  1407.                         $message new stdClass;
  1408.                         $message->type 'endfile';
  1409.                         $message->content new stdClass;
  1410.                         ifarray_key_exists('realfile'get_object_vars($this->fileHeader)) ) {
  1411.                             $message->content->realfile $this->fileHeader->realFile;
  1412.                         else {
  1413.                             $message->content->realfile $this->fileHeader->file;
  1414.                         }
  1415.                         $message->content->file $this->fileHeader->file;
  1416.                         ifarray_key_exists('compressed'get_object_vars($this->fileHeader)) ) {
  1417.                             $message->content->compressed $this->fileHeader->compressed;
  1418.                         else {
  1419.                             $message->content->compressed 0;
  1420.                         }
  1421.                         $message->content->uncompressed $this->fileHeader->uncompressed;
  1422.                         $this->notify($message);
  1423.                     }
  1424.                     $this->runState = AK_STATE_NOFILE;
  1425.                     continue;
  1426.             }
  1427.         }
  1428.  
  1429.         $error $this->getError();
  1430.         if!$status && ($this->runState == AK_STATE_NOFILE&& empty$error ) )
  1431.         {
  1432.             // We just finished
  1433.             $this->setState('postrun');
  1434.         }
  1435.         elseif!empty($error) )
  1436.         {
  1437.             $this->setState'error'$error );
  1438.         }
  1439.     }
  1440.  
  1441.     protected function _finalize()
  1442.     {
  1443.         // Nothing to do
  1444.         $this->setState('finished');
  1445.     }
  1446.  
  1447.     /**
  1448.      * Returns the base extension of the file, e.g. '.jpa'
  1449.      * @return string 
  1450.      */
  1451.     private function getBaseExtension()
  1452.     {
  1453.         static $baseextension;
  1454.  
  1455.         if(empty($baseextension))
  1456.         {
  1457.             $basename basename($this->filename);
  1458.             $lastdot strrpos($basename,'.');
  1459.             $baseextension substr($basename$lastdot);
  1460.         }
  1461.  
  1462.         return $baseextension;
  1463.     }
  1464.  
  1465.     /**
  1466.      * Scans for archive parts
  1467.      */
  1468.     private function scanArchives()
  1469.     {
  1470.         $privateArchiveList array();
  1471.  
  1472.         // Get the components of the archive filename
  1473.         $dirname dirname($this->filename);
  1474.         $base_extension $this->getBaseExtension();
  1475.         $basename basename($this->filename$base_extension);
  1476.         $this->totalSize = 0;
  1477.  
  1478.         // Scan for multiple parts until we don't find any more of them
  1479.         $count 0;
  1480.         $found true;
  1481.         $this->archiveList = array();
  1482.         while($found)
  1483.         {
  1484.             ++$count;
  1485.             $extension substr($base_extension02).sprintf('%02d'$count);
  1486.             $filename $dirname.DIRECTORY_SEPARATOR.$basename.$extension;
  1487.             $found file_exists($filename);
  1488.             if($found)
  1489.             {
  1490.                 // Add yet another part, with a numeric-appended filename
  1491.                 $this->archiveList[$filename;
  1492.  
  1493.                 $filesize @filesize($filename);
  1494.                 $this->totalSize += $filesize;
  1495.  
  1496.                 $privateArchiveList[array($filename$filesize);
  1497.             }
  1498.             else
  1499.             {
  1500.                 // Add the last part, with the regular extension
  1501.                 $this->archiveList[$this->filename;
  1502.  
  1503.                 $filename $this->filename;
  1504.                 $filesize @filesize($filename);
  1505.                 $this->totalSize += $filesize;
  1506.  
  1507.                 $privateArchiveList[array($filename$filesize);
  1508.             }
  1509.         }
  1510.  
  1511.         $this->currentPartNumber = -1;
  1512.         $this->currentPartOffset = 0;
  1513.         $this->runState = AK_STATE_NOFILE;
  1514.  
  1515.         // Send start of file notification
  1516.         $message new stdClass;
  1517.         $message->type 'totalsize';
  1518.         $message->content new stdClass;
  1519.         $message->content->totalsize $this->totalSize;
  1520.         $message->content->filelist $privateArchiveList;
  1521.         $this->notify($message);
  1522.     }
  1523.  
  1524.     /**
  1525.      * Opens the next part file for reading
  1526.      */
  1527.     protected function nextFile()
  1528.     {
  1529.         ++$this->currentPartNumber;
  1530.  
  1531.         if$this->currentPartNumber > (count($this->archiveList1) )
  1532.         {
  1533.             $this->setState('postrun');
  1534.             return false;
  1535.         }
  1536.         else
  1537.         {
  1538.             ifis_resource($this->fp) ) @fclose($this->fp);
  1539.             $this->fp = @fopen$this->archiveList[$this->currentPartNumber]'rb' );
  1540.             fseek($this->fp0);
  1541.             $this->currentPartOffset = 0;
  1542.             return true;
  1543.         }
  1544.     }
  1545.  
  1546.     /**
  1547.      * Returns true if we have reached the end of file
  1548.      * @param $local bool True to return EOF of the local file, false (default) to return if we have reached the end of the archive set
  1549.      * @return bool True if we have reached End Of File
  1550.      */
  1551.     protected function isEOF($local false)
  1552.     {
  1553.         $eof @feof($this->fp);
  1554.  
  1555.         if(!$eof)
  1556.         {
  1557.             // Border case: right at the part's end (eeeek!!!). For the life of me, I don't understand why
  1558.             // feof() doesn't report true. It expects the fp to be positioned *beyond* the EOF to report
  1559.             // true. Incredible! :(
  1560.             $position @ftell($this->fp);
  1561.             $filesize @filesize$this->archiveList[$this->currentPartNumber);
  1562.             if$position >= $filesize  $eof true;
  1563.         }
  1564.  
  1565.         if($local)
  1566.         {
  1567.             return $eof;
  1568.         }
  1569.         else
  1570.         {
  1571.             return $eof && ($this->currentPartNumber >= (count($this->archiveList)-1) );
  1572.         }
  1573.     }
  1574.  
  1575.     /**
  1576.      * Tries to make a directory user-writable so that we can write a file to it
  1577.      * @param $path string A path to a file
  1578.      */
  1579.     protected function setCorrectPermissions($path)
  1580.     {
  1581.         static $rootDir null;
  1582.         
  1583.         if(is_null($rootDir)) {
  1584.             $rootDir rtrim(AKFactory::get('kickstart.setup.destdir',''),'/\\');
  1585.         }
  1586.         
  1587.         $directory rtrim(dirname($path),'/\\');
  1588.         if($directory != $rootDir{
  1589.             // Is this an unwritable directory?
  1590.             if(!is_writeable($directory)) {
  1591.                 $this->postProcEngine->chmod$directory0755 );
  1592.             }
  1593.         }
  1594.         $this->postProcEngine->chmod$path0644 );
  1595.     }
  1596.  
  1597.     /**
  1598.      * Concrete classes are supposed to use this method in order to read the archive's header and
  1599.      * prepare themselves to the point of being ready to extract the first file.
  1600.      */
  1601.     protected abstract function readArchiveHeader();
  1602.  
  1603.     /**
  1604. /**
  1605.      * Concrete classes must use this method to read the file header
  1606.      * @return bool True if reading the file was successful, false if an error occured or we reached end of archive
  1607.      */
  1608.     protected abstract function readFileHeader();
  1609.  
  1610.     /**
  1611. /**
  1612.      * Concrete classes must use this method to process file data. It must set $runState to AK_STATE_DATAREAD when
  1613.      * it's finished processing the file data.
  1614.      * @return bool True if processing the file data was successful, false if an error occured
  1615.      */
  1616.     protected abstract function processFileData();
  1617.  
  1618.     /**
  1619. /**
  1620.      * Reads data from the archive and notifies the observer with the 'reading' message
  1621.      * @param $fp 
  1622.      * @param $length 
  1623.      */
  1624.     protected function fread($fp$length null)
  1625.     {
  1626.         if(is_numeric($length))
  1627.         {
  1628.             if($length 0{
  1629.                 $data fread($fp$length);
  1630.             else {
  1631.                 $data fread($fp);
  1632.             }
  1633.         }
  1634.         else
  1635.         {
  1636.             $data fread($fp);
  1637.         }
  1638.         if($data === false$data '';
  1639.  
  1640.         // Send start of file notification
  1641.         $message new stdClass;
  1642.         $message->type 'reading';
  1643.         $message->content = new stdClass;
  1644.         $message->content->length strlen($data);
  1645.         $this->notify($message);
  1646.  
  1647.         return $data;
  1648.     }
  1649. }
  1650.  
  1651. /**
  1652.  * The superclass of all Akeeba Kickstart parts. The "parts" are intelligent stateful
  1653.  * classes which perform a single procedure and have preparation, running and
  1654.  * finalization phases. The transition between phases is handled automatically by
  1655.  * this superclass' tick() final public method, which should be the ONLY public API
  1656.  * exposed to the rest of the Akeeba Engine.
  1657.  */
  1658. abstract class AKAbstractPart extends AKAbstractObject
  1659. {
  1660.     /**
  1661.      * Indicates whether this part has finished its initialisation cycle
  1662.      * @var boolean 
  1663.      */
  1664.     protected $isPrepared = false;
  1665.  
  1666.     /**
  1667.      * Indicates whether this part has more work to do (it's in running state)
  1668.      * @var boolean 
  1669.      */
  1670.     protected $isRunning = false;
  1671.  
  1672.     /**
  1673.      * Indicates whether this part has finished its finalization cycle
  1674.      * @var boolean 
  1675.      */
  1676.     protected $isFinished = false;
  1677.  
  1678.     /**
  1679.      * Indicates whether this part has finished its run cycle
  1680.      * @var boolean 
  1681.      */
  1682.     protected $hasRan = false;
  1683.  
  1684.     /**
  1685.      * The name of the engine part (a.k.a. Domain), used in return table
  1686.      * generation.
  1687.      * @var string 
  1688.      */
  1689.     protected $active_domain = "";
  1690.  
  1691.     /**
  1692.      * The step this engine part is in. Used verbatim in return table and
  1693.      * should be set by the code in the _run() method.
  1694.      * @var string 
  1695.      */
  1696.     protected $active_step = "";
  1697.  
  1698.     /**
  1699.      * A more detailed description of the step this engine part is in. Used
  1700.      * verbatim in return table and should be set by the code in the _run()
  1701.      * method.
  1702.      * @var string 
  1703.      */
  1704.     protected $active_substep = "";
  1705.  
  1706.     /**
  1707.      * Any configuration variables, in the form of an array.
  1708.      * @var array 
  1709.      */
  1710.     protected $_parametersArray = array();
  1711.  
  1712.     /** @var string The database root key */
  1713.     protected $databaseRoot = array();
  1714.  
  1715.     /** @var int Last reported warnings's position in array */
  1716.     private $warnings_pointer = -1;
  1717.  
  1718.     /** @var array An array of observers */
  1719.     protected $observers = array();
  1720.  
  1721.     /**
  1722.      * Runs the preparation for this part. Should set _isPrepared
  1723.      * to true
  1724.      */
  1725.     abstract protected function _prepare();
  1726.  
  1727.     /**
  1728.      * Runs the finalisation process for this part. Should set
  1729.      * _isFinished to true.
  1730.      */
  1731.     abstract protected function _finalize();
  1732.  
  1733.     /**
  1734.      * Runs the main functionality loop for this part. Upon calling,
  1735.      * should set the _isRunning to true. When it finished, should set
  1736.      * the _hasRan to true. If an error is encountered, setError should
  1737.      * be used.
  1738.      */
  1739.     abstract protected function _run();
  1740.  
  1741.     /**
  1742.      * Sets the BREAKFLAG, which instructs this engine part that the current step must break immediately,
  1743.      * in fear of timing out.
  1744.      */
  1745.     protected function setBreakFlag()
  1746.     {
  1747.         AKFactory::set('volatile.breakflag'true);
  1748.     }
  1749.  
  1750.     /**
  1751.      * Sets the engine part's internal state, in an easy to use manner
  1752.      *
  1753.      * @param    string    $state            One of init, prepared, running, postrun, finished, error
  1754.      * @param    string    $errorMessage    The reported error message, should the state be set to error
  1755.      */
  1756.     protected function setState($state 'init'$errorMessage='Invalid setState argument')
  1757.     {
  1758.         switch($state)
  1759.         {
  1760.             case 'init':
  1761.                 $this->isPrepared = false;
  1762.                 $this->isRunning  = false;
  1763.                 $this->isFinished = false;
  1764.                 $this->hasRun     false;
  1765.                 break;
  1766.  
  1767.             case 'prepared':
  1768.                 $this->isPrepared = true;
  1769.                 $this->isRunning  = false;
  1770.                 $this->isFinished = false;
  1771.                 $this->hasRun     false;
  1772.                 break;
  1773.  
  1774.             case 'running':
  1775.                 $this->isPrepared = true;
  1776.                 $this->isRunning  = true;
  1777.                 $this->isFinished = false;
  1778.                 $this->hasRun     false;
  1779.                 break;
  1780.  
  1781.             case 'postrun':
  1782.                 $this->isPrepared = true;
  1783.                 $this->isRunning  = false;
  1784.                 $this->isFinished = false;
  1785.                 $this->hasRun     true;
  1786.                 break;
  1787.  
  1788.             case 'finished':
  1789.                 $this->isPrepared = true;
  1790.                 $this->isRunning  = false;
  1791.                 $this->isFinished = true;
  1792.                 $this->hasRun     false;
  1793.                 break;
  1794.  
  1795.             case 'error':
  1796.             default:
  1797.                 $this->setError($errorMessage);
  1798.                 break;
  1799.         }
  1800.     }
  1801.  
  1802.     /**
  1803.      * The public interface to an engine part. This method takes care for
  1804.      * calling the correct method in order to perform the initialisation -
  1805.      * run - finalisation cycle of operation and return a proper reponse array.
  1806.      * @return    array    A Reponse Array
  1807.      */
  1808.     final public function tick()
  1809.     {
  1810.         // Call the right action method, depending on engine part state
  1811.         switch$this->getState() )
  1812.         {
  1813.             case "init":
  1814.                 $this->_prepare();
  1815.                 break;
  1816.             case "prepared":
  1817.                 $this->_run();
  1818.                 break;
  1819.             case "running":
  1820.                 $this->_run();
  1821.                 break;
  1822.             case "postrun":
  1823.                 $this->_finalize();
  1824.                 break;
  1825.         }
  1826.  
  1827.         // Send a Return Table back to the caller
  1828.         $out $this->_makeReturnTable();
  1829.         return $out;
  1830.     }
  1831.  
  1832.     /**
  1833.      * Returns a copy of the class's status array
  1834.      * @return array 
  1835.      */
  1836.     public function getStatusArray()
  1837.     {
  1838.         return $this->_makeReturnTable();
  1839.     }
  1840.  
  1841.     /**
  1842.      * Sends any kind of setup information to the engine part. Using this,
  1843.      * we avoid passing parameters to the constructor of the class. These
  1844.      * parameters should be passed as an indexed array and should be taken
  1845.      * into account during the preparation process only. This function will
  1846.      * set the error flag if it's called after the engine part is prepared.
  1847.      *
  1848.      * @param array $parametersArray The parameters to be passed to the
  1849.      *  engine part.
  1850.      */
  1851.     final public function setup$parametersArray )
  1852.     {
  1853.         if$this->isPrepared )
  1854.         {
  1855.             $this->setState('error'"Can't modify configuration after the preparation of " $this->active_domain);
  1856.         }
  1857.         else
  1858.         {
  1859.             $this->_parametersArray = $parametersArray;
  1860.             if(array_key_exists('root'$parametersArray))
  1861.             {
  1862.                 $this->databaseRoot = $parametersArray['root'];
  1863.             }
  1864.         }
  1865.     }
  1866.  
  1867.     /**
  1868.      * Returns the state of this engine part.
  1869.      *
  1870.      * @return string The state of this engine part. It can be one of
  1871.      *  error, init, prepared, running, postrun, finished.
  1872.      */
  1873.     final public function getState()
  1874.     {
  1875.         if$this->getError() )
  1876.         {
  1877.             return "error";
  1878.         }
  1879.  
  1880.         if!($this->isPrepared) )
  1881.         {
  1882.             return "init";
  1883.         }
  1884.  
  1885.         if!($this->isFinished&& !($this->isRunning&& !$this->hasRun && ($this->isPrepared) )
  1886.         {
  1887.             return "prepared";
  1888.         }
  1889.  
  1890.         if !($this->isFinished&& $this->isRunning && !$this->hasRun ) )
  1891.         {
  1892.             return "running";
  1893.         }
  1894.  
  1895.         if !($this->isFinished&& !($this->isRunning&& $this->hasRun )
  1896.         {
  1897.             return "postrun";
  1898.         }
  1899.  
  1900.         if $this->isFinished )
  1901.         {
  1902.             return "finished";
  1903.         }
  1904.     }
  1905.  
  1906.     /**
  1907.      * Constructs a Response Array based on the engine part's state.
  1908.      * @return array The Response Array for the current state
  1909.      */
  1910.     final protected function _makeReturnTable()
  1911.     {
  1912.         // Get a list of warnings
  1913.         $warnings $this->getWarnings();
  1914.         // Report only new warnings if there is no warnings queue size
  1915.         if$this->_warnings_queue_size == )
  1916.         {
  1917.             if( ($this->warnings_pointer 0&& ($this->warnings_pointer (count($warnings)) ) )
  1918.             {
  1919.                 $warnings array_slice($warnings$this->warnings_pointer 1);
  1920.                 $this->warnings_pointer += count($warnings);
  1921.             }
  1922.             else
  1923.             {
  1924.                 $this->warnings_pointer count($warnings);
  1925.             }
  1926.         }
  1927.  
  1928.         $out =  array(
  1929.             'HasRun'    => (!($this->isFinished)),
  1930.             'Domain'    => $this->active_domain,
  1931.             'Step'        => $this->active_step,
  1932.             'Substep'    => $this->active_substep,
  1933.             'Error'        => $this->getError(),
  1934.             'Warnings'    => $warnings
  1935.         );
  1936.  
  1937.         return $out;
  1938.     }
  1939.  
  1940.     final protected function setDomain($new_domain)
  1941.     {
  1942.         $this->active_domain = $new_domain;
  1943.     }
  1944.  
  1945.     final public function getDomain()
  1946.     {
  1947.         return $this->active_domain;
  1948.     }
  1949.  
  1950.     final protected function setStep($new_step)
  1951.     {
  1952.         $this->active_step = $new_step;
  1953.     }
  1954.  
  1955.     final public function getStep()
  1956.     {
  1957.         return $this->active_step;
  1958.     }
  1959.  
  1960.     final protected function setSubstep($new_substep)
  1961.     {
  1962.         $this->active_substep = $new_substep;
  1963.     }
  1964.  
  1965.     final public function getSubstep()
  1966.     {
  1967.         return $this->active_substep;
  1968.     }
  1969.  
  1970.     /**
  1971.      * Attaches an observer object
  1972.      * @param AKAbstractPartObserver $obs 
  1973.      */
  1974.     function attach(AKAbstractPartObserver $obs{
  1975.         $this->observers["$obs"$obs;
  1976.     }
  1977.  
  1978.     /**
  1979.      * Dettaches an observer object
  1980.      * @param AKAbstractPartObserver $obs 
  1981.      */
  1982.     function detach(AKAbstractPartObserver $obs{
  1983.         delete($this->observers["$obs"]);
  1984.     }
  1985.  
  1986.     /**
  1987.      * Notifies observers each time something interesting happened to the part
  1988.      * @param mixed $message The event object
  1989.      */
  1990.     protected function notify($message{
  1991.         foreach ($this->observers as $obs{
  1992.             $obs->update($this$message);
  1993.         }
  1994.     }
  1995. }
  1996.  
  1997. /**
  1998.  * Descendants of this class can be used in the unarchiver's observer methods (attach, detach and notify)
  1999.  * @author Nicholas
  2000.  *
  2001.  */
  2002. abstract class AKAbstractPartObserver
  2003. {
  2004.     abstract public function update($object$message);
  2005. }
  2006.  
  2007. /**
  2008.  * Direct file writer
  2009.  */
  2010. {
  2011.     public function process()
  2012.     {
  2013.         $restorePerms AKFactory::get('kickstart.setup.restoreperms'false);
  2014.         if($restorePerms)
  2015.         {
  2016.             @chmod($this->filename$this->perms);
  2017.         }
  2018.         else
  2019.         {
  2020.             if(@is_file($this->filename))
  2021.             {
  2022.                 @chmod($this->filename0644);
  2023.             }
  2024.             else
  2025.             {
  2026.                 @chmod($this->filename0755);
  2027.             }
  2028.         }
  2029.         if($this->timestamp > 0)
  2030.         {
  2031.             @touch($this->filename$this->timestamp);
  2032.         }
  2033.         return true;
  2034.     }
  2035.  
  2036.     public function processFilename($filename$perms 0755)
  2037.     {
  2038.         $this->perms = $perms;
  2039.         $this->filename = $filename;
  2040.         return $filename;
  2041.     }
  2042.  
  2043.     public function createDirRecursive$dirName$perms )
  2044.     {
  2045.         ifAKFactory::get('kickstart.setup.dryrun','0') ) return true;
  2046.         if (@mkdir($dirName0755true)) {
  2047.             @chmod($dirName0755);
  2048.             return true;
  2049.         }
  2050.  
  2051.         $root AKFactory::get('kickstart.setup.destdir');
  2052.         $root rtrim(str_replace('\\','/',$root),'/');
  2053.         $dir rtrim(str_replace('\\','/',$dirName),'/');
  2054.         if(strpos($dir$root=== 0{
  2055.             $dir ltrim(substr($dirstrlen($root))'/');
  2056.             $root .= '/';
  2057.         else {
  2058.             $root '';
  2059.         }
  2060.         
  2061.         if(empty($dir)) return true;
  2062.  
  2063.         $dirArray explode('/'$dir);
  2064.         $path '';
  2065.         foreach$dirArray as $dir )
  2066.         {
  2067.             $path .= $dir '/';
  2068.             $ret is_dir($root.$pathtrue @mkdir($root.$path);
  2069.             if!$ret {
  2070.                 // Is this a file instead of a directory?
  2071.                 if(is_file($root.$path) )
  2072.                 {
  2073.                     @unlink($root.$path);
  2074.                     $ret @mkdir($root.$path);
  2075.                 }
  2076.                 if!$ret {
  2077.                     $this->setErrorAKText::sprintf('COULDNT_CREATE_DIR',$path) );
  2078.                     return false;
  2079.                 }
  2080.             }
  2081.             // Try to set new directory permissions to 0755
  2082.             @chmod($root.$path$perms);
  2083.         }
  2084.         return true;
  2085.     }
  2086.  
  2087.     public function chmod$file$perms )
  2088.     {
  2089.         ifAKFactory::get('kickstart.setup.dryrun','0') ) return true;
  2090.  
  2091.         return @chmod$file$perms );
  2092.     }
  2093.  
  2094.     public function unlink$file )
  2095.     {
  2096.         return @unlink$file );
  2097.     }
  2098.  
  2099.     public function rmdir$directory )
  2100.     {
  2101.         return @rmdir$directory );
  2102.     }
  2103.  
  2104.     public function rename$from$to )
  2105.     {
  2106.         return @rename($from$to);
  2107.     }
  2108.  
  2109. }
  2110.  
  2111. /**
  2112.  * FTP file writer
  2113.  */
  2114. {
  2115.     /** @var bool Should I use FTP over implicit SSL? */
  2116.     public $useSSL = false;
  2117.     /** @var bool use Passive mode? */
  2118.     public $passive = true;
  2119.     /** @var string FTP host name */
  2120.     public $host = '';
  2121.     /** @var int FTP port */
  2122.     public $port = 21;
  2123.     /** @var string FTP user name */
  2124.     public $user = '';
  2125.     /** @var string FTP password */
  2126.     public $pass = '';
  2127.     /** @var string FTP initial directory */
  2128.     public $dir = '';
  2129.     /** @var resource The FTP handle */
  2130.     private $handle null;
  2131.     /** @var string The temporary directory where the data will be stored */
  2132.     private $tempDir '';
  2133.  
  2134.     public function __construct()
  2135.     {
  2136.         parent::__construct();
  2137.  
  2138.         $this->useSSL = AKFactory::get('kickstart.ftp.ssl'false);
  2139.         $this->passive = AKFactory::get('kickstart.ftp.passive'true);
  2140.         $this->host = AKFactory::get('kickstart.ftp.host''');
  2141.         $this->port = AKFactory::get('kickstart.ftp.port'21);
  2142.         if(trim($this->port== ''$this->port = 21;
  2143.         $this->user = AKFactory::get('kickstart.ftp.user''');
  2144.         $this->pass = AKFactory::get('kickstart.ftp.pass''');
  2145.         $this->dir = AKFactory::get('kickstart.ftp.dir''');
  2146.         $this->tempDir AKFactory::get('kickstart.ftp.tempdir''');
  2147.  
  2148.         $connected $this->connect();
  2149.  
  2150.         if($connected)
  2151.         {
  2152.             if(!empty($this->tempDir))
  2153.             {
  2154.                 $tempDir rtrim($this->tempDir'/\\').'/';
  2155.                 $writable $this->isDirWritable($tempDir);
  2156.             }
  2157.             else
  2158.             {
  2159.                 $tempDir '';
  2160.                 $writable false;
  2161.             }
  2162.  
  2163.             if(!$writable{
  2164.                 // Default temporary directory is the current root
  2165.                 $tempDir function_exists('getcwd'getcwd(dirname(__FILE__);
  2166.                 if(empty($tempDir))
  2167.                 {
  2168.                     // Oh, we have no directory reported!
  2169.                     $tempDir '.';
  2170.                 }
  2171.                 $absoluteDirToHere $tempDir;
  2172.                 $tempDir rtrim(str_replace('\\','/',$tempDir),'/');
  2173.                 if(!empty($tempDir)) $tempDir .= '/';
  2174.                 $this->tempDir $tempDir;
  2175.                 // Is this directory writable?
  2176.                 $writable $this->isDirWritable($tempDir);
  2177.             }
  2178.  
  2179.             if(!$writable)
  2180.             {
  2181.                 // Nope. Let's try creating a temporary directory in the site's root.
  2182.                 $tempDir $absoluteDirToHere.'/kicktemp';
  2183.                 $this->createDirRecursive($tempDir0777);
  2184.                 // Try making it writable...
  2185.                 $this->fixPermissions($tempDir);
  2186.                 $writable $this->isDirWritable($tempDir);
  2187.             }
  2188.  
  2189.             // Was the new directory writable?
  2190.             if(!$writable)
  2191.             {
  2192.                 // Let's see if the user has specified one
  2193.                 $userdir AKFactory::get('kickstart.ftp.tempdir''');
  2194.                 if(!empty($userdir))
  2195.                 {
  2196.                     // Is it an absolute or a relative directory?
  2197.                     $absolute false;
  2198.                     $absolute $absolute || substr($userdir,0,1== '/' );
  2199.                     $absolute $absolute || substr($userdir,1,1== ':' );
  2200.                     $absolute $absolute || substr($userdir,2,1== ':' );
  2201.                     if(!$absolute)
  2202.                     {
  2203.                         // Make absolute
  2204.                         $tempDir $absoluteDirToHere.$userdir;
  2205.                     }
  2206.                     else
  2207.                     {
  2208.                         // it's already absolute
  2209.                         $tempDir $userdir;
  2210.                     }
  2211.                     // Does the directory exist?
  2212.                     ifis_dir($tempDir) )
  2213.                     {
  2214.                         // Yeah. Is it writable?
  2215.                         $writable $this->isDirWritable($tempDir);
  2216.                     }
  2217.                 }
  2218.             }
  2219.             $this->tempDir $tempDir;
  2220.  
  2221.             if(!$writable)
  2222.             {
  2223.                 // No writable directory found!!!
  2224.                 $this->setError(AKText::_('FTP_TEMPDIR_NOT_WRITABLE'));
  2225.             }
  2226.             else
  2227.             {
  2228.                 AKFactory::set('kickstart.ftp.tempdir'$tempDir);
  2229.                 $this->tempDir $tempDir;
  2230.             }
  2231.         }
  2232.     }
  2233.  
  2234.     function __wakeup()
  2235.     {
  2236.         $this->connect();
  2237.     }
  2238.  
  2239.     public function connect()
  2240.     {
  2241.         // Connect to server, using SSL if so required
  2242.         if($this->useSSL{
  2243.             $this->handle @ftp_ssl_connect($this->host$this->port);
  2244.         else {
  2245.             $this->handle @ftp_connect($this->host$this->port);
  2246.         }
  2247.         if($this->handle === false)
  2248.         {
  2249.             $this->setError(AKText::_('WRONG_FTP_HOST'));
  2250.             return false;
  2251.         }
  2252.  
  2253.         // Login
  2254.         if(@ftp_login($this->handle$this->user$this->pass))
  2255.         {
  2256.             $this->setError(AKText::_('WRONG_FTP_USER'));
  2257.             @ftp_close($this->handle);
  2258.             return false;
  2259.         }
  2260.  
  2261.         // Change to initial directory
  2262.         if(@ftp_chdir($this->handle$this->dir))
  2263.         {
  2264.             $this->setError(AKText::_('WRONG_FTP_PATH1'));
  2265.             @ftp_close($this->handle);
  2266.             return false;
  2267.         }
  2268.  
  2269.         // Enable passive mode if the user requested it
  2270.         if$this->passive )
  2271.         {
  2272.             @ftp_pasv($this->handletrue);
  2273.         }
  2274.         else
  2275.         {
  2276.             @ftp_pasv($this->handlefalse);
  2277.         }
  2278.  
  2279.         return true;
  2280.     }
  2281.  
  2282.     public function process()
  2283.     {
  2284.         ifis_null($this->tempFilename) )
  2285.         {
  2286.             // If an empty filename is passed, it means that we shouldn't do any post processing, i.e.
  2287.             // the entity was a directory or symlink
  2288.             return true;
  2289.         }
  2290.  
  2291.         $remotePath dirname($this->filename);
  2292.         $removePath AKFactory::get('kickstart.setup.destdir','');
  2293.         if(!empty($removePath))
  2294.         {
  2295.             $removePath ltrim($removePath"/");
  2296.             $remotePath ltrim($remotePath"/");
  2297.             $left substr($remotePath0strlen($removePath));
  2298.             if($left == $removePath)
  2299.             {
  2300.                 $remotePath substr($remotePathstrlen($removePath));
  2301.             }
  2302.         }
  2303.  
  2304.         $absoluteFSPath dirname($this->filename);
  2305.         $relativeFTPPath trim($remotePath'/');
  2306.         $absoluteFTPPath '/'.trim$this->dir'/' ).'/'.trim($remotePath'/');
  2307.         $onlyFilename basename($this->filename);
  2308.  
  2309.         $remoteName $absoluteFTPPath.'/'.$onlyFilename;
  2310.  
  2311.         $ret @ftp_chdir($this->handle$absoluteFTPPath);
  2312.         if($ret === false)
  2313.         {
  2314.             $ret $this->createDirRecursive$absoluteFSPath0755);
  2315.             if($ret === false{
  2316.                 $this->setError(AKText::sprintf('FTP_COULDNT_UPLOAD'$this->filename));
  2317.                 return false;
  2318.             }
  2319.             $ret @ftp_chdir($this->handle$absoluteFTPPath);
  2320.             if($ret === false{
  2321.                 $this->setError(AKText::sprintf('FTP_COULDNT_UPLOAD'$this->filename));
  2322.                 return false;
  2323.             }
  2324.         }
  2325.  
  2326.         $ret @ftp_put($this->handle$remoteName$this->tempFilenameFTP_BINARY);
  2327.         if($ret === false)
  2328.         {
  2329.             // If we couldn't create the file, attempt to fix the permissions in the PHP level and retry!
  2330.             $this->fixPermissions($this->filename);
  2331.             $this->unlink($this->filename);
  2332.  
  2333.             $fp @fopen($this->tempFilename);
  2334.             if($fp !== false)
  2335.             {
  2336.                 $ret @ftp_fput($this->handle$remoteName$fpFTP_BINARY);
  2337.                 @fclose($fp);
  2338.             }
  2339.             else
  2340.             {
  2341.                 $ret false;
  2342.             }
  2343.         }
  2344.         @unlink($this->tempFilename);
  2345.  
  2346.         if($ret === false)
  2347.         {
  2348.             $this->setError(AKText::sprintf('FTP_COULDNT_UPLOAD'$this->filename));
  2349.             return false;
  2350.         }
  2351.         $restorePerms AKFactory::get('kickstart.setup.restoreperms'false);
  2352.         if($restorePerms)
  2353.         {
  2354.             @ftp_chmod($this->_handle$perms$remoteName);
  2355.         }
  2356.         else
  2357.         {
  2358.             @ftp_chmod($this->_handle0644$remoteName);
  2359.         }
  2360.         return true;
  2361.     }
  2362.  
  2363.     public function processFilename($filename$perms 0755)
  2364.     {
  2365.         // Catch some error conditions...
  2366.         if($this->getError())
  2367.         {
  2368.             return false;
  2369.         }
  2370.  
  2371.         // If a null filename is passed, it means that we shouldn't do any post processing, i.e.
  2372.         // the entity was a directory or symlink
  2373.         if(is_null($filename))
  2374.         {
  2375.             $this->filename = null;
  2376.             $this->tempFilename = null;
  2377.             return null;
  2378.         }
  2379.  
  2380.         // Strip absolute filesystem path to website's root
  2381.         $removePath AKFactory::get('kickstart.setup.destdir','');
  2382.         if(!empty($removePath))
  2383.         {
  2384.             $left substr($filename0strlen($removePath));
  2385.             if($left == $removePath)
  2386.             {
  2387.                 $filename substr($filenamestrlen($removePath));
  2388.             }
  2389.         }
  2390.  
  2391.         // Trim slash on the left
  2392.         $filename ltrim($filename'/');
  2393.  
  2394.         $this->filename = $filename;
  2395.         $this->tempFilename = tempnam($this->tempDir'kickstart-');
  2396.         $this->perms = $perms;
  2397.  
  2398.         ifempty($this->tempFilename) )
  2399.         {
  2400.             // Oops! Let's try something different
  2401.             $this->tempFilename = $this->tempDir.'/kickstart-'.time().'.dat';
  2402.         }
  2403.  
  2404.         return $this->tempFilename;
  2405.     }
  2406.  
  2407.     private function isDirWritable($dir)
  2408.     {
  2409.         $fp @fopen($dir.'/kickstart.dat''wb');
  2410.         if($fp === false)
  2411.         {
  2412.             return false;
  2413.         }
  2414.         else
  2415.         {
  2416.             @fclose($fp);
  2417.             unlink($dir.'/kickstart.dat');
  2418.             return true;
  2419.         }
  2420.     }
  2421.  
  2422.     public function createDirRecursive$dirName$perms )
  2423.     {
  2424.         // Strip absolute filesystem path to website's root
  2425.         $removePath AKFactory::get('kickstart.setup.destdir','');
  2426.         if(!empty($removePath))
  2427.         {
  2428.             // UNIXize the paths
  2429.             $removePath str_replace('\\','/',$removePath);
  2430.             $dirName str_replace('\\','/',$dirName);
  2431.             // Make sure they both end in a slash
  2432.             $removePath rtrim($removePath,'/\\').'/';
  2433.             $dirName rtrim($dirName,'/\\').'/';
  2434.             // Process the path removal
  2435.             $left substr($dirName0strlen($removePath));
  2436.             if($left == $removePath)
  2437.             {
  2438.                 $dirName substr($dirNamestrlen($removePath));
  2439.             }
  2440.         }
  2441.         if(empty($dirName)) $dirName ''// 'cause the substr() above may return FALSE.
  2442.         
  2443.         $check '/'.trim($this->dir,'/').'/'.trim($dirName'/');
  2444.         if($this->is_dir($check)) return true;
  2445.  
  2446.         $alldirs explode('/'$dirName);
  2447.         $previousDir '/'.trim($this->dir);
  2448.         foreach($alldirs as $curdir)
  2449.         {
  2450.             $check $previousDir.'/'.$curdir;
  2451.             if(!$this->is_dir($check))
  2452.             {
  2453.                 // Proactively try to delete a file by the same name
  2454.                 @ftp_delete($this->handle$check);
  2455.  
  2456.                 if(@ftp_mkdir($this->handle$check=== false)
  2457.                 {
  2458.                     // If we couldn't create the directory, attempt to fix the permissions in the PHP level and retry!
  2459.                     $this->fixPermissions($removePath.$check);
  2460.                     if(@ftp_mkdir($this->handle$check=== false)
  2461.                     {
  2462.                         // Can we fall back to pure PHP mode, sire?
  2463.                         if(!@mkdir($check))
  2464.                         {
  2465.                             $this->setError(AKText::sprintf('FTP_CANT_CREATE_DIR',$dir));
  2466.                             return false;
  2467.                         }
  2468.                         else
  2469.                         {
  2470.                             // Since the directory was built by PHP, change its permissions
  2471.                             @chmod($check"0777");
  2472.                             return true;
  2473.                         }
  2474.                     }
  2475.                 }
  2476.                 @ftp_chmod($this->handle$perms$check);
  2477.             }
  2478.             $previousDir $check;
  2479.         }
  2480.  
  2481.         return true;
  2482.     }
  2483.  
  2484.     public function close()
  2485.     {
  2486.         @ftp_close($this->handle);
  2487.     }
  2488.  
  2489.     /*
  2490.      * Tries to fix directory/file permissions in the PHP level, so that
  2491.      * the FTP operation doesn't fail.
  2492.      * @param $path string The full path to a directory or file
  2493.      */
  2494.     private function fixPermissions$path )
  2495.     {
  2496.         // Turn off error reporting
  2497.         if(!defined('KSDEBUG')) {
  2498.             $oldErrorReporting @error_reporting(E_NONE);
  2499.         }
  2500.  
  2501.         // Get UNIX style paths
  2502.         $relPath str_replace('\\','/',$path);
  2503.         $basePath rtrim(str_replace('\\','/',dirname(__FILE__)),'/');
  2504.         $basePath rtrim($basePath,'/');
  2505.         if(!empty($basePath)) $basePath .= '/';
  2506.         // Remove the leading relative root
  2507.         ifsubstr($relPath,0,strlen($basePath)) == $basePath )
  2508.             $relPath substr($relPath,strlen($basePath));
  2509.         $dirArray explode('/'$relPath);
  2510.         $pathBuilt rtrim($basePath,'/');
  2511.         foreach$dirArray as $dir )
  2512.         {
  2513.             if(empty($dir)) continue;
  2514.             $oldPath $pathBuilt;
  2515.             $pathBuilt .= '/'.$dir;
  2516.             if(is_dir($oldPath.$dir))
  2517.             {
  2518.                 @chmod($oldPath.$dir0777);
  2519.             }
  2520.             else
  2521.             {
  2522.                 if(@chmod($oldPath.$dir0777=== false)
  2523.                 {
  2524.                     @unlink($oldPath.$dir);
  2525.                 }
  2526.             }
  2527.         }
  2528.  
  2529.         // Restore error reporting
  2530.         if(!defined('KSDEBUG')) {
  2531.             @error_reporting($oldErrorReporting);
  2532.         }
  2533.     }
  2534.  
  2535.     public function chmod$file$perms )
  2536.     {
  2537.         return @ftp_chmod($this->handle$perms$path);
  2538.     }
  2539.  
  2540.     private function is_dir$dir )
  2541.     {
  2542.         return @ftp_chdir$this->handle$dir );
  2543.     }
  2544.  
  2545.     public function unlink$file )
  2546.     {
  2547.         $removePath AKFactory::get('kickstart.setup.destdir','');
  2548.         if(!empty($removePath))
  2549.         {
  2550.             $left substr($file0strlen($removePath));
  2551.             if($left == $removePath)
  2552.             {
  2553.                 $file substr($filestrlen($removePath));
  2554.             }
  2555.         }
  2556.  
  2557.         $check '/'.trim($this->dir,'/').'/'.trim($file'/');
  2558.  
  2559.         return @ftp_delete$this->handle$check );
  2560.     }
  2561.  
  2562.     public function rmdir$directory )
  2563.     {
  2564.         $removePath AKFactory::get('kickstart.setup.destdir','');
  2565.         if(!empty($removePath))
  2566.         {
  2567.             $left substr($directory0strlen($removePath));
  2568.             if($left == $removePath)
  2569.             {
  2570.                 $directory substr($directorystrlen($removePath));
  2571.             }
  2572.         }
  2573.  
  2574.         $check '/'.trim($this->dir,'/').'/'.trim($directory'/');
  2575.  
  2576.         return @ftp_rmdir$this->handle$check );
  2577.     }
  2578.  
  2579.     public function rename$from$to )
  2580.     {
  2581.         $originalFrom $from;
  2582.         $originalTo $to;
  2583.  
  2584.         $removePath AKFactory::get('kickstart.setup.destdir','');
  2585.         if(!empty($removePath))
  2586.         {
  2587.             $left substr($from0strlen($removePath));
  2588.             if($left == $removePath)
  2589.             {
  2590.                 $from substr($fromstrlen($removePath));
  2591.             }
  2592.         }
  2593.         $from '/'.trim($this->dir,'/').'/'.trim($from'/');
  2594.  
  2595.         if(!empty($removePath))
  2596.         {
  2597.             $left substr($to0strlen($removePath));
  2598.             if($left == $removePath)
  2599.             {
  2600.                 $to substr($tostrlen($removePath));
  2601.             }
  2602.         }
  2603.         $to '/'.trim($this->dir,'/').'/'.trim($to'/');
  2604.  
  2605.         $result @ftp_rename$this->handle$from$to );
  2606.         if($result !== true)
  2607.         {
  2608.             return @rename($from$to);
  2609.         }
  2610.         else
  2611.         {
  2612.             return true;
  2613.         }
  2614.     }
  2615.  
  2616. }
  2617.  
  2618. /**
  2619.  * JPA archive extraction class
  2620.  */
  2621. {
  2622.     private $archiveHeaderData array();
  2623.  
  2624.     protected function readArchiveHeader()
  2625.     {
  2626.         // Initialize header data array
  2627.         $this->archiveHeaderData new stdClass();
  2628.  
  2629.         // Open the first part
  2630.         $this->nextFile();
  2631.  
  2632.         // Fail for unreadable files
  2633.         if$this->fp === false return false;
  2634.  
  2635.         // Read the signature
  2636.         $sig fread$this->fp);
  2637.  
  2638.         if ($sig != 'JPA')
  2639.         {
  2640.             // Not a JPA file
  2641.             $this->setErrorAKText::_('ERR_NOT_A_JPA_FILE') );
  2642.             return false;
  2643.         }
  2644.  
  2645.         // Read and parse header length
  2646.         $header_length_array unpack'v'fread$this->fp) );
  2647.         $header_length $header_length_array[1];
  2648.  
  2649.         // Read and parse the known portion of header data (14 bytes)
  2650.         $bin_data fread($this->fp14);
  2651.         $header_data unpack('Cmajor/Cminor/Vcount/Vuncsize/Vcsize'$bin_data);
  2652.  
  2653.         // Load any remaining header data (forward compatibility)
  2654.         $rest_length $header_length 19;
  2655.         if$rest_length )
  2656.             $junk fread($this->fp$rest_length);
  2657.         else
  2658.             $junk '';
  2659.  
  2660.         // Temporary array with all the data we read
  2661.         $temp array(
  2662.             'signature' =>             $sig,
  2663.             'length' =>             $header_length,
  2664.             'major' =>                 $header_data['major'],
  2665.             'minor' =>                 $header_data['minor'],
  2666.             'filecount' =>             $header_data['count'],
  2667.             'uncompressedsize' =>     $header_data['uncsize'],
  2668.             'compressedsize' =>     $header_data['csize'],
  2669.             'unknowndata' =>         $junk
  2670.         );
  2671.         // Array-to-object conversion
  2672.         foreach($temp as $key => $value)
  2673.         {
  2674.             $this->archiveHeaderData->{$key$value;
  2675.         }
  2676.  
  2677.         $this->currentPartOffset = @ftell($this->fp);
  2678.  
  2679.         $this->dataReadLength = 0;
  2680.  
  2681.         return true;
  2682.     }
  2683.  
  2684.     /**
  2685.      * Concrete classes must use this method to read the file header
  2686.      * @return bool True if reading the file was successful, false if an error occured or we reached end of archive
  2687.      */
  2688.     protected function readFileHeader()
  2689.     {
  2690.         // If the current part is over, proceed to the next part please
  2691.         if$this->isEOF(true) ) {
  2692.             $this->nextFile();
  2693.         }
  2694.  
  2695.         // Get and decode Entity Description Block
  2696.         $signature fread($this->fp3);
  2697.  
  2698.         $this->fileHeader new stdClass();
  2699.         $this->fileHeader->timestamp 0;
  2700.  
  2701.         // Check signature
  2702.         if$signature != 'JPF' )
  2703.         {
  2704.             if($this->isEOF(true))
  2705.             {
  2706.                 // This file is finished; make sure it's the last one
  2707.                 $this->nextFile();
  2708.                 if(!$this->isEOF(false))
  2709.                 {
  2710.                     $this->setError(AKText::sprintf('INVALID_FILE_HEADER'$this->currentPartNumber$this->currentPartOffset));
  2711.                     return false;
  2712.                 }
  2713.                 // We're just finished
  2714.                 return false;
  2715.             }
  2716.             else
  2717.             {
  2718.                 // This is not a file block! The archive is corrupt.
  2719.                 $this->setError(AKText::sprintf('INVALID_FILE_HEADER'$this->currentPartNumber$this->currentPartOffset));
  2720.                 return false;
  2721.             }
  2722.         }
  2723.         // This a JPA Entity Block. Process the header.
  2724.  
  2725.         $isBannedFile false;
  2726.  
  2727.         // Read length of EDB and of the Entity Path Data
  2728.         $length_array unpack('vblocksize/vpathsize'fread($this->fp4));
  2729.         // Read the path data
  2730.         if($length_array['pathsize'0{
  2731.             $file fread$this->fp$length_array['pathsize');
  2732.         else {
  2733.             $file '';
  2734.         }
  2735.  
  2736.         // Handle file renaming
  2737.         $isRenamed false;
  2738.         if(is_array($this->renameFiles&& (count($this->renameFiles0) )
  2739.         {
  2740.             if(array_key_exists($file$this->renameFiles))
  2741.             {
  2742.                 $file $this->renameFiles[$file];
  2743.                 $isRenamed true;
  2744.             }
  2745.         }
  2746.         
  2747.         // Handle directory renaming
  2748.         $isDirRenamed false;
  2749.         if(is_array($this->renameDirs&& (count($this->renameDirs0)) {
  2750.             if(array_key_exists(dirname($file)$this->renameDirs)) {
  2751.                 $file rtrim($this->renameDirs[dirname($file)],'/').'/'.basename($file);
  2752.                 $isRenamed true;
  2753.                 $isDirRenamed true;
  2754.             }
  2755.         }
  2756.  
  2757.         // Read and parse the known data portion
  2758.         $bin_data fread$this->fp14 );
  2759.         $header_data unpack('Ctype/Ccompression/Vcompsize/Vuncompsize/Vperms'$bin_data);
  2760.         // Read any unknown data
  2761.         $restBytes $length_array['blocksize'(21 $length_array['pathsize']);
  2762.         if$restBytes )
  2763.         {
  2764.             // Start reading the extra fields
  2765.             while($restBytes >= 4)
  2766.             {
  2767.                 $extra_header_data fread($this->fp4);
  2768.                 $extra_header unpack('vsignature/vlength'$extra_header_data);
  2769.                 $restBytes -= 4;
  2770.                 $extra_header['length'-= 4;
  2771.                 switch($extra_header['signature'])
  2772.                 {
  2773.                     case 256:
  2774.                         // File modified timestamp
  2775.                         if($extra_header['length'0)
  2776.                         {
  2777.                             $bindata fread($this->fp$extra_header['length']);
  2778.                             $restBytes -= $extra_header['length'];
  2779.                             $timestamps unpack('Vmodified'substr($bindata,0,4));
  2780.                             $filectime $timestamps['modified'];
  2781.                             $this->fileHeader->timestamp $filectime;
  2782.                         }
  2783.                         break;
  2784.  
  2785.                     default:
  2786.                         // Unknown field
  2787.                         if($extra_header['length']>0{
  2788.                             $junk fread($this->fp$extra_header['length']);
  2789.                             $restBytes -= $extra_header['length'];
  2790.                         }
  2791.                         break;
  2792.                 }
  2793.             }
  2794.             if($restBytes 0$junk fread($this->fp$restBytes);
  2795.         }
  2796.  
  2797.         $compressionType $header_data['compression'];
  2798.  
  2799.         // Populate the return array
  2800.         $this->fileHeader->file = $file;
  2801.         $this->fileHeader->compressed $header_data['compsize'];
  2802.         $this->fileHeader->uncompressed $header_data['uncompsize'];
  2803.         switch($header_data['type'])
  2804.         {
  2805.             case 0:
  2806.                 $this->fileHeader->type 'dir';
  2807.                 break;
  2808.  
  2809.             case 1:
  2810.                 $this->fileHeader->type 'file';
  2811.                 break;
  2812.  
  2813.             case 2:
  2814.                 $this->fileHeader->type 'link';
  2815.                 break;
  2816.         }
  2817.         switch$compressionType )
  2818.         {
  2819.             case 0:
  2820.                 $this->fileHeader->compression 'none';
  2821.                 break;
  2822.             case 1:
  2823.                 $this->fileHeader->compression 'gzip';
  2824.                 break;
  2825.             case 2:
  2826.                 $this->fileHeader->compression 'bzip2';
  2827.                 break;
  2828.         }
  2829.         $this->fileHeader->permissions $header_data['perms'];
  2830.  
  2831.         // Find hard-coded banned files
  2832.         if( (basename($this->fileHeader->file== "."|| (basename($this->fileHeader->file== "..") )
  2833.         {
  2834.             $isBannedFile true;
  2835.         }
  2836.  
  2837.         // Also try to find banned files passed in class configuration
  2838.         if((count($this->skipFiles0&& (!$isRenamed) )
  2839.         {
  2840.             if(in_array($this->fileHeader->file$this->skipFiles))
  2841.             {
  2842.                 $isBannedFile true;
  2843.             }
  2844.         }
  2845.  
  2846.         // If we have a banned file, let's skip it
  2847.         if($isBannedFile)
  2848.         {
  2849.             // Advance the file pointer, skipping exactly the size of the compressed data
  2850.             $seekleft $this->fileHeader->compressed;
  2851.             while($seekleft 0)
  2852.             {
  2853.                 // Ensure that we can seek past archive part boundaries
  2854.                 $curSize @filesize($this->archiveList[$this->currentPartNumber]);
  2855.                 $curPos @ftell($this->fp);
  2856.                 $canSeek $curSize $curPos;
  2857.                 if($canSeek $seekleft$canSeek $seekleft;
  2858.                 @fseek$this->fp$canSeekSEEK_CUR );
  2859.                 $seekleft -= $canSeek;
  2860.                 if($seekleft$this->nextFile();
  2861.             }
  2862.  
  2863.             $this->currentPartOffset @ftell($this->fp);
  2864.             $this->runState AK_STATE_DONE;
  2865.             return true;
  2866.         }
  2867.  
  2868.         // Last chance to prepend a path to the filename
  2869.         if(!empty($this->addPath&& !$isDirRenamed)
  2870.         {
  2871.             $this->fileHeader->file = $this->addPath.$this->fileHeader->file;
  2872.         }
  2873.  
  2874.         // Get the translated path name
  2875.         $restorePerms AKFactory::get('kickstart.setup.restoreperms'false);
  2876.         if($this->fileHeader->type == 'file')
  2877.         {
  2878.             // Regular file; ask the postproc engine to process its filename
  2879.             if($restorePerms)
  2880.             {
  2881.                 $this->fileHeader->realFile $this->postProcEngine->processFilename$this->fileHeader->file$this->fileHeader->permissions );
  2882.             }
  2883.             else
  2884.             {
  2885.                 $this->fileHeader->realFile $this->postProcEngine->processFilename$this->fileHeader->file );
  2886.             }
  2887.         }
  2888.         elseif($this->fileHeader->type == 'dir')
  2889.         {
  2890.             $dir $this->fileHeader->file;
  2891.  
  2892.             // Directory; just create it
  2893.             if($restorePerms)
  2894.             {
  2895.                 $this->postProcEngine->createDirRecursive$this->fileHeader->file$this->fileHeader->permissions );
  2896.             }
  2897.             else
  2898.             {
  2899.                 $this->postProcEngine->createDirRecursive$this->fileHeader->file0755 );
  2900.             }
  2901.             $this->postProcEngine->processFilename(null);
  2902.         }
  2903.         else
  2904.         {
  2905.             // Symlink; do not post-process
  2906.             $this->postProcEngine->processFilename(null);
  2907.         }
  2908.  
  2909.         $this->createDirectory();
  2910.  
  2911.         // Header is read
  2912.         $this->runState AK_STATE_HEADER;
  2913.  
  2914.         $this->dataReadLength 0;
  2915.  
  2916.         return true;
  2917.     }
  2918.  
  2919.     /**
  2920.      * Concrete classes must use this method to process file data. It must set $runState to AK_STATE_DATAREAD when
  2921.      * it's finished processing the file data.
  2922.      * @return bool True if processing the file data was successful, false if an error occured
  2923.      */
  2924.     protected function processFileData()
  2925.     {
  2926.         switch$this->fileHeader->type )
  2927.         {
  2928.             case 'dir':
  2929.                 return $this->processTypeDir();
  2930.                 break;
  2931.  
  2932.             case 'link':
  2933.                 return $this->processTypeLink();
  2934.                 break;
  2935.  
  2936.             case 'file':
  2937.                 switch($this->fileHeader->compression)
  2938.                 {
  2939.                     case 'none':
  2940.                         return $this->processTypeFileUncompressed();
  2941.                         break;
  2942.  
  2943.                     case 'gzip':
  2944.                     case 'bzip2':
  2945.                         return $this->processTypeFileCompressedSimple();
  2946.                         break;
  2947.  
  2948.                 }
  2949.                 break;
  2950.         }
  2951.     }
  2952.  
  2953.     private function processTypeFileUncompressed()
  2954.     {
  2955.         // Uncompressed files are being processed in small chunks, to avoid timeouts
  2956.         if( ($this->dataReadLength == 0&& !AKFactory::get('kickstart.setup.dryrun','0') )
  2957.         {
  2958.             // Before processing file data, ensure permissions are adequate
  2959.             $this->setCorrectPermissions$this->fileHeader->file );
  2960.         }
  2961.  
  2962.         // Open the output file
  2963.         if!AKFactory::get('kickstart.setup.dryrun','0') )
  2964.         {
  2965.             $ignore AKFactory::get('kickstart.setup.ignoreerrors'false);
  2966.             if ($this->dataReadLength == 0{
  2967.                 $outfp @fopen$this->fileHeader->realFile'wb' );
  2968.             else {
  2969.                 $outfp @fopen$this->fileHeader->realFile'ab' );
  2970.             }
  2971.  
  2972.             // Can we write to the file?
  2973.             if( ($outfp === false&& (!$ignore) ) {
  2974.                 // An error occured
  2975.                 $this->setErrorAKText::sprintf('COULDNT_WRITE_FILE'$this->fileHeader->realFile) );
  2976.                 return false;
  2977.             }
  2978.         }
  2979.  
  2980.         // Does the file have any data, at all?
  2981.         if$this->fileHeader->compressed == )
  2982.         {
  2983.             // No file data!
  2984.             if!AKFactory::get('kickstart.setup.dryrun','0'&& is_resource($outfp) ) @fclose($outfp);
  2985.             $this->runState AK_STATE_DATAREAD;
  2986.             return true;
  2987.         }
  2988.  
  2989.         // Reference to the global timer
  2990.         $timer AKFactory::getTimer();
  2991.  
  2992.         $toReadBytes 0;
  2993.         $leftBytes $this->fileHeader->compressed $this->dataReadLength;
  2994.  
  2995.         // Loop while there's data to read and enough time to do it
  2996.         while( ($leftBytes 0&& ($timer->getTimeLeft(0) )
  2997.         {
  2998.             $toReadBytes ($leftBytes $this->chunkSize$this->chunkSize $leftBytes;
  2999.             $data $this->fread$this->fp$toReadBytes );
  3000.             $reallyReadBytes akstringlen($data);
  3001.             $leftBytes -= $reallyReadBytes;
  3002.             $this->dataReadLength += $reallyReadBytes;
  3003.             if($reallyReadBytes $toReadBytes)
  3004.             {
  3005.                 // We read less than requested! Why? Did we hit local EOF?
  3006.                 if$this->isEOF(true&& !$this->isEOF(false) )
  3007.                 {
  3008.                     // Yeap. Let's go to the next file
  3009.                     $this->nextFile();
  3010.                 }
  3011.                 else
  3012.                 {
  3013.                     // Nope. The archive is corrupt
  3014.                     $this->setErrorAKText::_('ERR_CORRUPT_ARCHIVE') );
  3015.                     return false;
  3016.                 }
  3017.             }
  3018.             if!AKFactory::get('kickstart.setup.dryrun','0') )
  3019.                 if(is_resource($outfp)) @fwrite$outfp$data );
  3020.         }
  3021.  
  3022.         // Close the file pointer
  3023.         if!AKFactory::get('kickstart.setup.dryrun','0') )
  3024.             if(is_resource($outfp)) @fclose($outfp);
  3025.  
  3026.         // Was this a pre-timeout bail out?
  3027.         if$leftBytes )
  3028.         {
  3029.             $this->runState AK_STATE_DATA;
  3030.         }
  3031.         else
  3032.         {
  3033.             // Oh! We just finished!
  3034.             $this->runState AK_STATE_DATAREAD;
  3035.             $this->dataReadLength 0;
  3036.         }
  3037.  
  3038.         return true;
  3039.     }
  3040.  
  3041.     private function processTypeFileCompressedSimple()
  3042.     {
  3043.         if!AKFactory::get('kickstart.setup.dryrun','0') )
  3044.         {
  3045.             // Before processing file data, ensure permissions are adequate
  3046.             $this->setCorrectPermissions$this->fileHeader->file );
  3047.  
  3048.             // Open the output file
  3049.             $outfp @fopen$this->fileHeader->realFile'wb' );
  3050.  
  3051.             // Can we write to the file?
  3052.             $ignore AKFactory::get('kickstart.setup.ignoreerrors'false);
  3053.             if( ($outfp === false&& (!$ignore) ) {
  3054.                 // An error occured
  3055.                 $this->setErrorAKText::sprintf('COULDNT_WRITE_FILE'$this->fileHeader->realFile) );
  3056.                 return false;
  3057.             }
  3058.         }
  3059.  
  3060.         // Does the file have any data, at all?
  3061.         if$this->fileHeader->compressed == )
  3062.         {
  3063.             // No file data!
  3064.             if!AKFactory::get('kickstart.setup.dryrun','0') )
  3065.                 if(is_resource($outfp)) @fclose($outfp);
  3066.             $this->runState AK_STATE_DATAREAD;
  3067.             return true;
  3068.         }
  3069.  
  3070.         // Simple compressed files are processed as a whole; we can't do chunk processing
  3071.         $zipData $this->fread$this->fp$this->fileHeader->compressed );
  3072.         whileakstringlen($zipData$this->fileHeader->compressed )
  3073.         {
  3074.             // End of local file before reading all data, but have more archive parts?
  3075.             if($this->isEOF(true&& !$this->isEOF(false))
  3076.             {
  3077.                 // Yeap. Read from the next file
  3078.                 $this->nextFile();
  3079.                 $bytes_left $this->fileHeader->compressed akstringlen($zipData);
  3080.                 $zipData .= $this->fread$this->fp$bytes_left );
  3081.             }
  3082.             else
  3083.             {
  3084.                 $this->setErrorAKText::_('ERR_CORRUPT_ARCHIVE') );
  3085.                 return false;
  3086.             }
  3087.         }
  3088.  
  3089.         if($this->fileHeader->compression == 'gzip')
  3090.         {
  3091.             $unzipData gzinflate$zipData );
  3092.         }
  3093.         elseif($this->fileHeader->compression == 'bzip2')
  3094.         {
  3095.             $unzipData bzdecompress$zipData );
  3096.         }
  3097.         unset($zipData);
  3098.  
  3099.         // Write to the file.
  3100.         if!AKFactory::get('kickstart.setup.dryrun','0'&& is_resource($outfp) )
  3101.         {
  3102.             @fwrite$outfp$unzipData$this->fileHeader->uncompressed );
  3103.             @fclose$outfp );
  3104.         }
  3105.         unset($unzipData);
  3106.  
  3107.         $this->runState AK_STATE_DATAREAD;
  3108.         return true;
  3109.     }
  3110.  
  3111.     /**
  3112.      * Process the file data of a link entry
  3113.      * @return bool 
  3114.      */
  3115.     private function processTypeLink()
  3116.     {
  3117.         $readBytes 0;
  3118.         $toReadBytes 0;
  3119.         $leftBytes $this->fileHeader->compressed;
  3120.         $data '';
  3121.  
  3122.         while$leftBytes 0)
  3123.         {
  3124.             $toReadBytes ($leftBytes $this->chunkSize$this->chunkSize $leftBytes;
  3125.             $mydata $this->fread$this->fp$toReadBytes );
  3126.             $reallyReadBytes akstringlen($mydata);
  3127.             $data .= $mydata;
  3128.             $leftBytes -= $reallyReadBytes;
  3129.             if($reallyReadBytes $toReadBytes)
  3130.             {
  3131.                 // We read less than requested! Why? Did we hit local EOF?
  3132.                 if$this->isEOF(true&& !$this->isEOF(false) )
  3133.                 {
  3134.                     // Yeap. Let's go to the next file
  3135.                     $this->nextFile();
  3136.                 }
  3137.                 else
  3138.                 {
  3139.                     // Nope. The archive is corrupt
  3140.                     $this->setErrorAKText::_('ERR_CORRUPT_ARCHIVE') );
  3141.                     return false;
  3142.                 }
  3143.             }
  3144.         }
  3145.  
  3146.         // Try to remove an existing file or directory by the same name
  3147.         if(file_exists($this->fileHeader->realFile)) @unlink($this->fileHeader->realFile)@rmdir($this->fileHeader->realFile)}
  3148.         // Remove any trailing slash
  3149.         if(substr($this->fileHeader->realFile-1== '/'$this->fileHeader->realFile substr($this->fileHeader->realFile0-1);
  3150.         // Create the symlink - only possible within PHP context. There's no support built in the FTP protocol, so no postproc use is possible here :(
  3151.         if!AKFactory::get('kickstart.setup.dryrun','0') )
  3152.             @symlink($data$this->fileHeader->realFile);
  3153.  
  3154.         $this->runState AK_STATE_DATAREAD;
  3155.  
  3156.         return true// No matter if the link was created!
  3157.     }
  3158.  
  3159.     /**
  3160.      * Process the file data of a directory entry
  3161.      * @return bool 
  3162.      */
  3163.     private function processTypeDir()
  3164.     {
  3165.         // Directory entries in the JPA do not have file data, therefore we're done processing the entry
  3166.         $this->runState AK_STATE_DATAREAD;
  3167.         return true;
  3168.     }
  3169.  
  3170.     /**
  3171.      * Creates the directory this file points to
  3172.      */
  3173.     protected function createDirectory()
  3174.     {
  3175.         ifAKFactory::get('kickstart.setup.dryrun','0') ) return true;
  3176.  
  3177.         // Do we need to create a directory?
  3178.         if(empty($this->fileHeader->realFile)) $this->fileHeader->realFile $this->fileHeader->file;
  3179.         $lastSlash strrpos($this->fileHeader->realFile'/');
  3180.         $dirName substr$this->fileHeader->realFile0$lastSlash);
  3181.         $perms $this->flagRestorePermissions $retArray['permissions'0755;
  3182.         $ignore AKFactory::get('kickstart.setup.ignoreerrors'false);
  3183.         if( ($this->postProcEngine->createDirRecursive($dirName$perms== false&& (!$ignore) ) {
  3184.             $this->setErrorAKText::sprintf('COULDNT_CREATE_DIR'$dirName) );
  3185.             return false;
  3186.         }
  3187.         else
  3188.         {
  3189.             return true;
  3190.         }
  3191.     }
  3192. }
  3193.  
  3194. /**
  3195.  * ZIP archive extraction class
  3196.  *
  3197.  * Since the file data portion of ZIP and JPA are similarly structured (it's empty for dirs,
  3198.  * linked node name for symlinks, dumped binary data for no compressions and dumped gzipped
  3199.  * binary data for gzip compression) we just have to subclass AKUnarchiverJPA and change the
  3200.  * header reading bits. Reusable code ;)
  3201.  */
  3202. {
  3203.     var $expectDataDescriptor = false;
  3204.  
  3205.     protected function readArchiveHeader()
  3206.     {
  3207.         // Initialize header data array
  3208.         $this->archiveHeaderData new stdClass();
  3209.  
  3210.         // Open the first part
  3211.         $this->nextFile();
  3212.  
  3213.         // Fail for unreadable files
  3214.         if$this->fp === false return false;
  3215.  
  3216.         // Read a possible multipart signature
  3217.         $sigBinary fread$this->fp);
  3218.         $headerData unpack('Vsig'$sigBinary);
  3219.  
  3220.         // Roll back if it's not a multipart archive
  3221.         if$headerData['sig'== 0x04034b50 fseek($this->fp-4SEEK_CUR);
  3222.  
  3223.         $multiPartSigs array(
  3224.             0x08074b50,        // Multi-part ZIP
  3225.             0x30304b50,        // Multi-part ZIP (alternate)
  3226.             0x04034b50        // Single file
  3227.         );
  3228.         if!in_array($headerData['sig']$multiPartSigs) )
  3229.         {
  3230.             $this->setError(AKText::_('ERR_CORRUPT_ARCHIVE'));
  3231.             return false;
  3232.         }
  3233.  
  3234.         $this->currentPartOffset = @ftell($this->fp);
  3235.  
  3236.         $this->dataReadLength = 0;
  3237.  
  3238.         return true;
  3239.     }
  3240.  
  3241.     /**
  3242.      * Concrete classes must use this method to read the file header
  3243.      * @return bool True if reading the file was successful, false if an error occured or we reached end of archive
  3244.      */
  3245.     protected function readFileHeader()
  3246.     {
  3247.         // If the current part is over, proceed to the next part please
  3248.         if$this->isEOF(true) ) {
  3249.             $this->nextFile();
  3250.         }
  3251.  
  3252.         if($this->expectDataDescriptor)
  3253.         {
  3254.             // The last file had bit 3 of the general purpose bit flag set. This means that we have a
  3255.             // 12 byte data descriptor we need to skip. To make things worse, there might also be a 4
  3256.             // byte optional data descriptor header (0x08074b50).
  3257.             $junk @fread($this->fp4);
  3258.             $junk unpack('Vsig'$junk);
  3259.             if($junk['sig'== 0x08074b50{
  3260.                 // Yes, there was a signature
  3261.                 $junk @fread($this->fp12);
  3262.                 if(defined('KSDEBUG')) {
  3263.                     debugMsg('Data descriptor (w/ header) skipped at '.(ftell($this->fp)-12));
  3264.                 }
  3265.             else {
  3266.                 // No, there was no signature, just read another 8 bytes
  3267.                 $junk @fread($this->fp8);
  3268.                 if(defined('KSDEBUG')) {
  3269.                     debugMsg('Data descriptor (w/out header) skipped at '.(ftell($this->fp)-8));
  3270.                 }
  3271.             }
  3272.  
  3273.             // And check for EOF, too
  3274.             if$this->isEOF(true) ) {
  3275.                 if(defined('KSDEBUG')) {
  3276.                     debugMsg('EOF before reading header');
  3277.                 }
  3278.                 
  3279.                 $this->nextFile();
  3280.             }
  3281.         }
  3282.  
  3283.         // Get and decode Local File Header
  3284.         $headerBinary fread($this->fp30);
  3285.         $headerData unpack('Vsig/C2ver/vbitflag/vcompmethod/vlastmodtime/vlastmoddate/Vcrc/Vcompsize/Vuncomp/vfnamelen/veflen'$headerBinary);
  3286.  
  3287.         // Check signature
  3288.         if(!$headerData['sig'== 0x04034b50 ))
  3289.         {
  3290.             if(defined('KSDEBUG')) {
  3291.                 debugMsg('Not a file signature at '.(ftell($this->fp)-4));
  3292.             }
  3293.             
  3294.             // The signature is not the one used for files. Is this a central directory record (i.e. we're done)?
  3295.             if($headerData['sig'== 0x02014b50)
  3296.             {
  3297.                 if(defined('KSDEBUG')) {
  3298.                     debugMsg('EOCD signature at '.(ftell($this->fp)-4));
  3299.                 }
  3300.                 // End of ZIP file detected. We'll just skip to the end of file...
  3301.                 while$this->nextFile() ) {};
  3302.                 @fseek($this->fp0SEEK_END)// Go to EOF
  3303.                 return false;
  3304.             }
  3305.             else
  3306.             {
  3307.                 if(defined('KSDEBUG')) {
  3308.                     debugMsg'Invalid signature ' dechex($headerData['sig']' at '.ftell($this->fp) );
  3309.                 }
  3310.                 $this->setError(AKText::_('ERR_CORRUPT_ARCHIVE'));
  3311.                 return false;
  3312.             }
  3313.         }
  3314.  
  3315.         // If bit 3 of the bitflag is set, expectDataDescriptor is true
  3316.         $this->expectDataDescriptor = ($headerData['bitflag'4== 4;
  3317.  
  3318.         $this->fileHeader = new stdClass();
  3319.         $this->fileHeader->timestamp 0;
  3320.  
  3321.         // Read the last modified data and time
  3322.         $lastmodtime $headerData['lastmodtime'];
  3323.         $lastmoddate $headerData['lastmoddate'];
  3324.         
  3325.         if($lastmoddate && $lastmodtime)
  3326.         {
  3327.             // ----- Extract time
  3328.             $v_hour ($lastmodtime 0xF800>> 11;
  3329.             $v_minute ($lastmodtime 0x07E0>> 5;
  3330.             $v_seconde ($lastmodtime 0x001F)*2;
  3331.             
  3332.             // ----- Extract date
  3333.             $v_year (($lastmoddate 0xFE00>> 91980;
  3334.             $v_month ($lastmoddate 0x01E0>> 5;
  3335.             $v_day $lastmoddate 0x001F;
  3336.             
  3337.             // ----- Get UNIX date format
  3338.             $this->fileHeader->timestamp @mktime($v_hour$v_minute$v_seconde$v_month$v_day$v_year);
  3339.         }
  3340.         
  3341.         $isBannedFile false;
  3342.  
  3343.         $this->fileHeader->compressed    $headerData['compsize'];
  3344.         $this->fileHeader->uncompressed    $headerData['uncomp'];
  3345.         $nameFieldLength                $headerData['fnamelen'];
  3346.         $extraFieldLength                $headerData['eflen'];
  3347.  
  3348.         // Read filename field
  3349.         $this->fileHeader->file            fread$this->fp$nameFieldLength );
  3350.  
  3351.         // Handle file renaming
  3352.         $isRenamed false;
  3353.         if(is_array($this->renameFiles&& (count($this->renameFiles0) )
  3354.         {
  3355.             if(array_key_exists($this->fileHeader->file$this->renameFiles))
  3356.             {
  3357.                 $this->fileHeader->file $this->renameFiles[$this->fileHeader->file];
  3358.                 $isRenamed true;
  3359.             }
  3360.         }
  3361.         
  3362.         // Handle directory renaming
  3363.         $isDirRenamed false;
  3364.         if(is_array($this->renameDirs&& (count($this->renameDirs0)) {
  3365.             if(array_key_exists(dirname($file)$this->renameDirs)) {
  3366.                 $file rtrim($this->renameDirs[dirname($file)],'/').'/'.basename($file);
  3367.                 $isRenamed true;
  3368.                 $isDirRenamed true;
  3369.             }
  3370.         }
  3371.  
  3372.         // Read extra field if present
  3373.         if($extraFieldLength 0{
  3374.             $extrafield fread$this->fp$extraFieldLength );
  3375.         }
  3376.         
  3377.         if(defined('KSDEBUG')) {
  3378.             debugMsg'*'.ftell($this->fp).' IS START OF '.$this->fileHeader->file' ('.$this->fileHeader->compressed.' bytes)' );
  3379.         }
  3380.         
  3381.  
  3382.         // Decide filetype -- Check for directories
  3383.         $this->fileHeader->type 'file';
  3384.         ifstrrpos($this->fileHeader->file'/'== strlen($this->fileHeader->file$this->fileHeader->type 'dir';
  3385.         // Decide filetype -- Check for symbolic links
  3386.         if( ($headerData['ver1'== 10&& ($headerData['ver2'== 3) )$this->fileHeader->type 'link';
  3387.  
  3388.         switch$headerData['compmethod')
  3389.         {
  3390.             case 0:
  3391.                 $this->fileHeader->compression 'none';
  3392.                 break;
  3393.             case 8:
  3394.                 $this->fileHeader->compression 'gzip';
  3395.                 break;
  3396.         }
  3397.  
  3398.         // Find hard-coded banned files
  3399.         if( (basename($this->fileHeader->file== "."|| (basename($this->fileHeader->file== "..") )
  3400.         {
  3401.             $isBannedFile true;
  3402.         }
  3403.  
  3404.         // Also try to find banned files passed in class configuration
  3405.         if((count($this->skipFiles0&& (!$isRenamed))
  3406.         {
  3407.             if(in_array($this->fileHeader->file$this->skipFiles))
  3408.             {
  3409.                 $isBannedFile true;
  3410.             }
  3411.         }
  3412.  
  3413.         // If we have a banned file, let's skip it
  3414.         if($isBannedFile)
  3415.         {
  3416.             // Advance the file pointer, skipping exactly the size of the compressed data
  3417.             $seekleft $this->fileHeader->compressed;
  3418.             while($seekleft 0)
  3419.             {
  3420.                 // Ensure that we can seek past archive part boundaries
  3421.                 $curSize @filesize($this->archiveList[$this->currentPartNumber]);
  3422.                 $curPos @ftell($this->fp);
  3423.                 $canSeek $curSize $curPos;
  3424.                 if($canSeek $seekleft$canSeek $seekleft;
  3425.                 @fseek$this->fp$canSeekSEEK_CUR );
  3426.                 $seekleft -= $canSeek;
  3427.                 if($seekleft$this->nextFile();
  3428.             }
  3429.  
  3430.             $this->currentPartOffset = @ftell($this->fp);
  3431.             $this->runState = AK_STATE_DONE;
  3432.             return true;
  3433.         }
  3434.  
  3435.         // Last chance to prepend a path to the filename
  3436.         if(!empty($this->addPath&& !$isDirRenamed)
  3437.         {
  3438.             $this->fileHeader->file $this->addPath.$this->fileHeader->file;
  3439.         }
  3440.  
  3441.         // Get the translated path name
  3442.         if($this->fileHeader->type == 'file')
  3443.         {
  3444.             $this->fileHeader->realFile $this->postProcEngine->processFilename$this->fileHeader->file );
  3445.         }
  3446.         elseif($this->fileHeader->type == 'dir')
  3447.         {
  3448.             $this->fileHeader->timestamp 0;
  3449.  
  3450.             $dir $this->fileHeader->file;
  3451.  
  3452.             $this->postProcEngine->createDirRecursive$this->fileHeader->file0755 );
  3453.             $this->postProcEngine->processFilename(null);
  3454.         }
  3455.         else
  3456.         {
  3457.             // Symlink; do not post-process
  3458.             $this->fileHeader->timestamp 0;
  3459.             $this->postProcEngine->processFilename(null);
  3460.         }
  3461.  
  3462.         $this->createDirectory();
  3463.  
  3464.         // Header is read
  3465.         $this->runState = AK_STATE_HEADER;
  3466.  
  3467.         return true;
  3468.     }
  3469.  
  3470. }
  3471.  
  3472. /**
  3473.  * Timer class
  3474.  */
  3475. class AKCoreTimer extends AKAbstractObject
  3476. {
  3477.     /** @var int Maximum execution time allowance per step */
  3478.     private $max_exec_time null;
  3479.  
  3480.     /** @var int Timestamp of execution start */
  3481.     private $start_time null;
  3482.  
  3483.     /**
  3484.      * Public constructor, creates the timer object and calculates the execution time limits
  3485.      * @return AECoreTimer 
  3486.      */
  3487.     public function __construct()
  3488.     {
  3489.         parent::__construct();
  3490.  
  3491.         // Initialize start time
  3492.         $this->start_time $this->microtime_float();
  3493.  
  3494.         // Get configured max time per step and bias
  3495.         $config_max_exec_time    AKFactory::get('kickstart.tuning.max_exec_time'14);
  3496.         $bias                    AKFactory::get('kickstart.tuning.run_time_bias'75)/100;
  3497.  
  3498.         // Get PHP's maximum execution time (our upper limit)
  3499.         if(@function_exists('ini_get'))
  3500.         {
  3501.             $php_max_exec_time @ini_get("maximum_execution_time");
  3502.             if ( (!is_numeric($php_max_exec_time)) || ($php_max_exec_time == 0) ) {
  3503.                 // If we have no time limit, set a hard limit of about 10 seconds
  3504.                 // (safe for Apache and IIS timeouts, verbose enough for users)
  3505.                 $php_max_exec_time 14;
  3506.             }
  3507.         }
  3508.         else
  3509.         {
  3510.             // If ini_get is not available, use a rough default
  3511.             $php_max_exec_time 14;
  3512.         }
  3513.  
  3514.         // Apply an arbitrary correction to counter CMS load time
  3515.         $php_max_exec_time--;
  3516.  
  3517.         // Apply bias
  3518.         $php_max_exec_time $php_max_exec_time $bias;
  3519.         $config_max_exec_time $config_max_exec_time $bias;
  3520.  
  3521.         // Use the most appropriate time limit value
  3522.         if$config_max_exec_time $php_max_exec_time )
  3523.         {
  3524.             $this->max_exec_time $php_max_exec_time;
  3525.         }
  3526.         else
  3527.         {
  3528.             $this->max_exec_time $config_max_exec_time;
  3529.         }
  3530.     }
  3531.  
  3532.     /**
  3533.      * Wake-up function to reset internal timer when we get unserialized
  3534.      */
  3535.     public function __wakeup()
  3536.     {
  3537.         // Re-initialize start time on wake-up
  3538.         $this->start_time $this->microtime_float();
  3539.     }
  3540.  
  3541.     /**
  3542.      * Gets the number of seconds left, before we hit the "must break" threshold
  3543.      * @return float 
  3544.      */
  3545.     public function getTimeLeft()
  3546.     {
  3547.         return $this->max_exec_time $this->getRunningTime();
  3548.     }
  3549.  
  3550.     /**
  3551.      * Gets the time elapsed since object creation/unserialization, effectively how
  3552.      * long Akeeba Engine has been processing data
  3553.      * @return float 
  3554.      */
  3555.     public function getRunningTime()
  3556.     {
  3557.         return $this->microtime_float($this->start_time;
  3558.     }
  3559.  
  3560.     /**
  3561.      * Returns the current timestampt in decimal seconds
  3562.      */
  3563.     private function microtime_float()
  3564.     {
  3565.         list($usec$secexplode(" "microtime());
  3566.         return ((float)$usec + (float)$sec);
  3567.     }
  3568.  
  3569.     /**
  3570.      * Enforce the minimum execution time
  3571.      */
  3572.     public function enforce_min_exec_time()
  3573.     {
  3574.         // Try to get a sane value for PHP's maximum_execution_time INI parameter
  3575.         if(@function_exists('ini_get'))
  3576.         {
  3577.             $php_max_exec @ini_get("maximum_execution_time");
  3578.         }
  3579.         else
  3580.         {
  3581.             $php_max_exec 10;
  3582.         }
  3583.         if ( ($php_max_exec == ""|| ($php_max_exec == 0) ) {
  3584.             $php_max_exec 10;
  3585.         }
  3586.         // Decrease $php_max_exec time by 500 msec we need (approx.) to tear down
  3587.         // the application, as well as another 500msec added for rounding
  3588.         // error purposes. Also make sure this is never gonna be less than 0.
  3589.         $php_max_exec max($php_max_exec 1000 10000);
  3590.  
  3591.         // Get the "minimum execution time per step" Akeeba Backup configuration variable
  3592.         $minexectime AKFactory::get('kickstart.tuning.min_exec_time',0);
  3593.         if(!is_numeric($minexectime)) $minexectime 0;
  3594.  
  3595.         // Make sure we are not over PHP's time limit!
  3596.         if($minexectime $php_max_exec$minexectime $php_max_exec;
  3597.  
  3598.         // Get current running time
  3599.         $elapsed_time $this->getRunningTime(1000;
  3600.  
  3601.             // Only run a sleep delay if we haven't reached the minexectime execution time
  3602.         if( ($minexectime $elapsed_time&& ($elapsed_time 0) )
  3603.         {
  3604.             $sleep_msec $minexectime $elapsed_time;
  3605.             if(function_exists('usleep'))
  3606.             {
  3607.                 usleep(1000 $sleep_msec);
  3608.             }
  3609.             elseif(function_exists('time_nanosleep'))
  3610.             {
  3611.                 $sleep_sec floor($sleep_msec 1000);
  3612.                 $sleep_nsec 1000000 ($sleep_msec ($sleep_sec 1000));
  3613.                 time_nanosleep($sleep_sec$sleep_nsec);
  3614.             }
  3615.             elseif(function_exists('time_sleep_until'))
  3616.             {
  3617.                 $until_timestamp time($sleep_msec 1000;
  3618.                 time_sleep_until($until_timestamp);
  3619.             }
  3620.             elseif(function_exists('sleep'))
  3621.             {
  3622.                 $sleep_sec ceil($sleep_msec/1000);
  3623.                 sleep$sleep_sec );
  3624.             }
  3625.         }
  3626.         elseif$elapsed_time )
  3627.         {
  3628.             // No sleep required, even if user configured us to be able to do so.
  3629.         }
  3630.     }
  3631.  
  3632.     /**
  3633.      * Reset the timer. It should only be used in CLI mode!
  3634.      */
  3635.     public function resetTime()
  3636.     {
  3637.         $this->start_time $this->microtime_float();
  3638.     }
  3639. }
  3640.  
  3641. /**
  3642.  * JPS archive extraction class
  3643.  */
  3644. {
  3645.     private $archiveHeaderData array();
  3646.  
  3647.     private $password '';
  3648.  
  3649.     public function __construct()
  3650.     {
  3651.         parent::__construct();
  3652.  
  3653.         $this->password AKFactory::get('kickstart.jps.password','');
  3654.     }
  3655.  
  3656.     protected function readArchiveHeader()
  3657.     {
  3658.         // Initialize header data array
  3659.         $this->archiveHeaderData new stdClass();
  3660.  
  3661.         // Open the first part
  3662.         $this->nextFile();
  3663.  
  3664.         // Fail for unreadable files
  3665.         if$this->fp === false return false;
  3666.  
  3667.         // Read the signature
  3668.         $sig fread$this->fp);
  3669.  
  3670.         if ($sig != 'JPS')
  3671.         {
  3672.             // Not a JPA file
  3673.             $this->setErrorAKText::_('ERR_NOT_A_JPS_FILE') );
  3674.             return false;
  3675.         }
  3676.  
  3677.         // Read and parse the known portion of header data (5 bytes)
  3678.         $bin_data fread($this->fp5);
  3679.         $header_data unpack('Cmajor/Cminor/cspanned/vextra'$bin_data);
  3680.  
  3681.         // Load any remaining header data (forward compatibility)
  3682.         $rest_length $header_data['extra'];
  3683.         if$rest_length )
  3684.             $junk fread($this->fp$rest_length);
  3685.         else
  3686.             $junk '';
  3687.  
  3688.         // Temporary array with all the data we read
  3689.         $temp array(
  3690.             'signature' =>             $sig,
  3691.             'major' =>                 $header_data['major'],
  3692.             'minor' =>                 $header_data['minor'],
  3693.             'spanned' =>             $header_data['spanned']
  3694.         );
  3695.         // Array-to-object conversion
  3696.         foreach($temp as $key => $value)
  3697.         {
  3698.             $this->archiveHeaderData->{$key$value;
  3699.         }
  3700.  
  3701.         $this->currentPartOffset = @ftell($this->fp);
  3702.  
  3703.         $this->dataReadLength = 0;
  3704.  
  3705.         return true;
  3706.     }
  3707.  
  3708.     /**
  3709.      * Concrete classes must use this method to read the file header
  3710.      * @return bool True if reading the file was successful, false if an error occured or we reached end of archive
  3711.      */
  3712.     protected function readFileHeader()
  3713.     {
  3714.         // If the current part is over, proceed to the next part please
  3715.         if$this->isEOF(true) ) {
  3716.             $this->nextFile();
  3717.         }
  3718.  
  3719.         // Get and decode Entity Description Block
  3720.         $signature fread($this->fp3);
  3721.  
  3722.         // Check for end-of-archive siganture
  3723.         if($signature == 'JPE')
  3724.         {
  3725.             $this->setState('postrun');
  3726.             return true;
  3727.         }
  3728.  
  3729.         $this->fileHeader new stdClass();
  3730.         $this->fileHeader->timestamp 0;
  3731.  
  3732.         // Check signature
  3733.         if$signature != 'JPF' )
  3734.         {
  3735.             if($this->isEOF(true))
  3736.             {
  3737.                 // This file is finished; make sure it's the last one
  3738.                 $this->nextFile();
  3739.                 if(!$this->isEOF(false))
  3740.                 {
  3741.                     $this->setError(AKText::sprintf('INVALID_FILE_HEADER'$this->currentPartNumber$this->currentPartOffset));
  3742.                     return false;
  3743.                 }
  3744.                 // We're just finished
  3745.                 return false;
  3746.             }
  3747.             else
  3748.             {
  3749.                 fseek($this->fp-6SEEK_CUR);
  3750.                 $signature fread($this->fp3);
  3751.                 if($signature == 'JPE')
  3752.                 {
  3753.                     return false;
  3754.                 }
  3755.  
  3756.                 $this->setError(AKText::sprintf('INVALID_FILE_HEADER'$this->currentPartNumber$this->currentPartOffset));
  3757.                 return false;
  3758.             }
  3759.         }
  3760.         // This a JPA Entity Block. Process the header.
  3761.  
  3762.         $isBannedFile false;
  3763.  
  3764.         // Read and decrypt the header
  3765.         $edbhData fread($this->fp4);
  3766.         $edbh unpack('vencsize/vdecsize'$edbhData);
  3767.         $bin_data fread($this->fp$edbh['encsize']);
  3768.  
  3769.         // Decrypt and truncate
  3770.         $bin_data AKEncryptionAES::AESDecryptCBC($bin_data$this->password128);
  3771.         $bin_data substr($bin_data,0,$edbh['decsize']);
  3772.  
  3773.         // Read length of EDB and of the Entity Path Data
  3774.         $length_array unpack('vpathsize'substr($bin_data,0,2) );
  3775.         // Read the path data
  3776.         $file substr($bin_data,2,$length_array['pathsize']);
  3777.  
  3778.         // Handle file renaming
  3779.         $isRenamed false;
  3780.         if(is_array($this->renameFiles&& (count($this->renameFiles0) )
  3781.         {
  3782.             if(array_key_exists($file$this->renameFiles))
  3783.             {
  3784.                 $file $this->renameFiles[$file];
  3785.                 $isRenamed true;
  3786.             }
  3787.         }
  3788.         
  3789.         // Handle directory renaming
  3790.         $isDirRenamed false;
  3791.         if(is_array($this->renameDirs&& (count($this->renameDirs0)) {
  3792.             if(array_key_exists(dirname($file)$this->renameDirs)) {
  3793.                 $file rtrim($this->renameDirs[dirname($file)],'/').'/'.basename($file);
  3794.                 $isRenamed true;
  3795.                 $isDirRenamed true;
  3796.             }
  3797.         }
  3798.  
  3799.         // Read and parse the known data portion
  3800.         $bin_data substr($bin_data$length_array['pathsize']);
  3801.         $header_data unpack('Ctype/Ccompression/Vuncompsize/Vperms/Vfilectime'$bin_data);
  3802.  
  3803.         $this->fileHeader->timestamp $header_data['filectime'];
  3804.         $compressionType $header_data['compression'];
  3805.  
  3806.         // Populate the return array
  3807.         $this->fileHeader->file = $file;
  3808.         $this->fileHeader->uncompressed $header_data['uncompsize'];
  3809.         switch($header_data['type'])
  3810.         {
  3811.             case 0:
  3812.                 $this->fileHeader->type 'dir';
  3813.                 break;
  3814.  
  3815.             case 1:
  3816.                 $this->fileHeader->type 'file';
  3817.                 break;
  3818.  
  3819.             case 2:
  3820.                 $this->fileHeader->type 'link';
  3821.                 break;
  3822.         }
  3823.         switch$compressionType )
  3824.         {
  3825.             case 0:
  3826.                 $this->fileHeader->compression 'none';
  3827.                 break;
  3828.             case 1:
  3829.                 $this->fileHeader->compression 'gzip';
  3830.                 break;
  3831.             case 2:
  3832.                 $this->fileHeader->compression 'bzip2';
  3833.                 break;
  3834.         }
  3835.         $this->fileHeader->permissions $header_data['perms'];
  3836.  
  3837.         // Find hard-coded banned files
  3838.         if( (basename($this->fileHeader->file== "."|| (basename($this->fileHeader->file== "..") )
  3839.         {
  3840.             $isBannedFile true;
  3841.         }
  3842.  
  3843.         // Also try to find banned files passed in class configuration
  3844.         if((count($this->skipFiles0&& (!$isRenamed) )
  3845.         {
  3846.             if(in_array($this->fileHeader->file$this->skipFiles))
  3847.             {
  3848.                 $isBannedFile true;
  3849.             }
  3850.         }
  3851.  
  3852.         // If we have a banned file, let's skip it
  3853.         if($isBannedFile)
  3854.         {
  3855.             $done false;
  3856.             while(!$done)
  3857.             {
  3858.                 // Read the Data Chunk Block header
  3859.                 $binMiniHead fread($this->fp8);
  3860.                 ifin_arraysubstr($binMiniHead,0,3)array('JPF','JPE') ) )
  3861.                 {
  3862.                     // Not a Data Chunk Block header, I am done skipping the file
  3863.                     @fseek($this->fp,-8,SEEK_CUR)// Roll back the file pointer
  3864.                     $done true// Mark as done
  3865.                     continue// Exit loop
  3866.                 }
  3867.                 else
  3868.                 {
  3869.                     // Skip forward by the amount of compressed data
  3870.                     $miniHead unpack('Vencsize/Vdecsize');
  3871.                     @fseek($this->fp$miniHead['encsize']SEEK_CUR);
  3872.                 }
  3873.             }
  3874.  
  3875.             $this->currentPartOffset @ftell($this->fp);
  3876.             $this->runState AK_STATE_DONE;
  3877.             return true;
  3878.         }
  3879.  
  3880.         // Last chance to prepend a path to the filename
  3881.         if(!empty($this->addPath&& !$isDirRenamed)
  3882.         {
  3883.             $this->fileHeader->file = $this->addPath.$this->fileHeader->file;
  3884.         }
  3885.  
  3886.         // Get the translated path name
  3887.         $restorePerms AKFactory::get('kickstart.setup.restoreperms'false);
  3888.         if($this->fileHeader->type == 'file')
  3889.         {
  3890.             // Regular file; ask the postproc engine to process its filename
  3891.             if($restorePerms)
  3892.             {
  3893.                 $this->fileHeader->realFile $this->postProcEngine->processFilename$this->fileHeader->file$this->fileHeader->permissions );
  3894.             }
  3895.             else
  3896.             {
  3897.                 $this->fileHeader->realFile $this->postProcEngine->processFilename$this->fileHeader->file );
  3898.             }
  3899.         }
  3900.         elseif($this->fileHeader->type == 'dir')
  3901.         {
  3902.             $dir $this->fileHeader->file;
  3903.             $this->fileHeader->realFile $dir;
  3904.  
  3905.             // Directory; just create it
  3906.             if($restorePerms)
  3907.             {
  3908.                 $this->postProcEngine->createDirRecursive$this->fileHeader->file$this->fileHeader->permissions );
  3909.             }
  3910.             else
  3911.             {
  3912.                 $this->postProcEngine->createDirRecursive$this->fileHeader->file0755 );
  3913.             }
  3914.             $this->postProcEngine->processFilename(null);
  3915.         }
  3916.         else
  3917.         {
  3918.             // Symlink; do not post-process
  3919.             $this->postProcEngine->processFilename(null);
  3920.         }
  3921.  
  3922.         $this->createDirectory();
  3923.  
  3924.         // Header is read
  3925.         $this->runState AK_STATE_HEADER;
  3926.  
  3927.         $this->dataReadLength 0;
  3928.  
  3929.         return true;
  3930.     }
  3931.  
  3932.     /**
  3933.      * Concrete classes must use this method to process file data. It must set $runState to AK_STATE_DATAREAD when
  3934.      * it's finished processing the file data.
  3935.      * @return bool True if processing the file data was successful, false if an error occured
  3936.      */
  3937.     protected function processFileData()
  3938.     {
  3939.         switch$this->fileHeader->type )
  3940.         {
  3941.             case 'dir':
  3942.                 return $this->processTypeDir();
  3943.                 break;
  3944.  
  3945.             case 'link':
  3946.                 return $this->processTypeLink();
  3947.                 break;
  3948.  
  3949.             case 'file':
  3950.                 switch($this->fileHeader->compression)
  3951.                 {
  3952.                     case 'none':
  3953.                         return $this->processTypeFileUncompressed();
  3954.                         break;
  3955.  
  3956.                     case 'gzip':
  3957.                     case 'bzip2':
  3958.                         return $this->processTypeFileCompressedSimple();
  3959.                         break;
  3960.  
  3961.                 }
  3962.                 break;
  3963.         }
  3964.     }
  3965.  
  3966.     private function processTypeFileUncompressed()
  3967.     {
  3968.         // Uncompressed files are being processed in small chunks, to avoid timeouts
  3969.         if( ($this->dataReadLength == 0&& !AKFactory::get('kickstart.setup.dryrun','0') )
  3970.         {
  3971.             // Before processing file data, ensure permissions are adequate
  3972.             $this->setCorrectPermissions$this->fileHeader->file );
  3973.         }
  3974.  
  3975.         // Open the output file
  3976.         if!AKFactory::get('kickstart.setup.dryrun','0') )
  3977.         {
  3978.             $ignore AKFactory::get('kickstart.setup.ignoreerrors'false);
  3979.             if ($this->dataReadLength == 0{
  3980.                 $outfp @fopen$this->fileHeader->realFile'wb' );
  3981.             else {
  3982.                 $outfp @fopen$this->fileHeader->realFile'ab' );
  3983.             }
  3984.  
  3985.             // Can we write to the file?
  3986.             if( ($outfp === false&& (!$ignore) ) {
  3987.                 // An error occured
  3988.                 $this->setErrorAKText::sprintf('COULDNT_WRITE_FILE'$this->fileHeader->realFile) );
  3989.                 return false;
  3990.             }
  3991.         }
  3992.  
  3993.         // Does the file have any data, at all?
  3994.         if$this->fileHeader->uncompressed == )
  3995.         {
  3996.             // No file data!
  3997.             if!AKFactory::get('kickstart.setup.dryrun','0'&& is_resource($outfp) ) @fclose($outfp);
  3998.             $this->runState AK_STATE_DATAREAD;
  3999.             return true;
  4000.         }
  4001.         else
  4002.         {
  4003.             $this->setError('An uncompressed file was detected; this is not supported by this archive extraction utility');
  4004.             return false;
  4005.         }
  4006.  
  4007.         return true;
  4008.     }
  4009.  
  4010.     private function processTypeFileCompressedSimple()
  4011.     {
  4012.         $timer AKFactory::getTimer();
  4013.  
  4014.         // Files are being processed in small chunks, to avoid timeouts
  4015.         if( ($this->dataReadLength == 0&& !AKFactory::get('kickstart.setup.dryrun','0') )
  4016.         {
  4017.             // Before processing file data, ensure permissions are adequate
  4018.             $this->setCorrectPermissions$this->fileHeader->file );
  4019.         }
  4020.  
  4021.         // Open the output file
  4022.         if!AKFactory::get('kickstart.setup.dryrun','0') )
  4023.         {
  4024.             // Open the output file
  4025.             $outfp @fopen$this->fileHeader->realFile'wb' );
  4026.  
  4027.             // Can we write to the file?
  4028.             $ignore AKFactory::get('kickstart.setup.ignoreerrors'false);
  4029.             if( ($outfp === false&& (!$ignore) ) {
  4030.                 // An error occured
  4031.                 $this->setErrorAKText::sprintf('COULDNT_WRITE_FILE'$this->fileHeader->realFile) );
  4032.                 return false;
  4033.             }
  4034.         }
  4035.  
  4036.         // Does the file have any data, at all?
  4037.         if$this->fileHeader->uncompressed == )
  4038.         {
  4039.             // No file data!
  4040.             if!AKFactory::get('kickstart.setup.dryrun','0') )
  4041.                 if(is_resource($outfp)) @fclose($outfp);
  4042.             $this->runState AK_STATE_DATAREAD;
  4043.             return true;
  4044.         }
  4045.  
  4046.         $leftBytes $this->fileHeader->uncompressed $this->dataReadLength;
  4047.  
  4048.         // Loop while there's data to write and enough time to do it
  4049.         while( ($leftBytes 0&& ($timer->getTimeLeft(0) )
  4050.         {
  4051.             // Read the mini header
  4052.             $binMiniHeader fread($this->fp8);
  4053.             $reallyReadBytes akstringlen($binMiniHeader);
  4054.             if($reallyReadBytes 8)
  4055.             {
  4056.                 // We read less than requested! Why? Did we hit local EOF?
  4057.                 if$this->isEOF(true&& !$this->isEOF(false) )
  4058.                 {
  4059.                     // Yeap. Let's go to the next file
  4060.                     $this->nextFile();
  4061.                     // Retry reading the header
  4062.                     $binMiniHeader fread($this->fp8);
  4063.                     $reallyReadBytes akstringlen($binMiniHeader);
  4064.                     // Still not enough data? If so, the archive is corrupt or missing parts.
  4065.                     if($reallyReadBytes 8{
  4066.                         $this->setErrorAKText::_('ERR_CORRUPT_ARCHIVE') );
  4067.                         return false;
  4068.                     }
  4069.                 }
  4070.                 else
  4071.                 {
  4072.                     // Nope. The archive is corrupt
  4073.                     $this->setErrorAKText::_('ERR_CORRUPT_ARCHIVE') );
  4074.                     return false;
  4075.                 }
  4076.             }
  4077.  
  4078.             // Read the encrypted data
  4079.             $miniHeader unpack('Vencsize/Vdecsize'$binMiniHeader);
  4080.             $toReadBytes $miniHeader['encsize'];
  4081.             $data $this->fread$this->fp$toReadBytes );
  4082.             $reallyReadBytes akstringlen($data);
  4083.             if($reallyReadBytes $toReadBytes)
  4084.             {
  4085.                 // We read less than requested! Why? Did we hit local EOF?
  4086.                 if$this->isEOF(true&& !$this->isEOF(false) )
  4087.                 {
  4088.                     // Yeap. Let's go to the next file
  4089.                     $this->nextFile();
  4090.                     // Read the rest of the data
  4091.                     $toReadBytes -= $reallyReadBytes;
  4092.                     $restData $this->fread$this->fp$toReadBytes );
  4093.                     $reallyReadBytes akstringlen($restData);
  4094.                     if($reallyReadBytes $toReadBytes{
  4095.                         $this->setErrorAKText::_('ERR_CORRUPT_ARCHIVE') );
  4096.                         return false;
  4097.                     }
  4098.                     if(akstringlen($data== 0{
  4099.                         $data $restData;
  4100.                     else {
  4101.                         $data .= $restData;
  4102.                     }
  4103.                 }
  4104.                 else
  4105.                 {
  4106.                     // Nope. The archive is corrupt
  4107.                     $this->setErrorAKText::_('ERR_CORRUPT_ARCHIVE') );
  4108.                     return false;
  4109.                 }
  4110.             }
  4111.  
  4112.             // Decrypt the data
  4113.             $data AKEncryptionAES::AESDecryptCBC($data$this->password128);
  4114.  
  4115.             // Is the length of the decrypted data less than expected?
  4116.             $data_length akstringlen($data);
  4117.             if($data_length $miniHeader['decsize']{
  4118.                 $this->setError(AKText::_('ERR_INVALID_JPS_PASSWORD'));
  4119.                 return false;
  4120.             }
  4121.  
  4122.             // Trim the data
  4123.             $data substr($data,0,$miniHeader['decsize']);
  4124.  
  4125.             // Decompress
  4126.             $data gzinflate($data);
  4127.             $unc_len akstringlen($data);
  4128.  
  4129.             // Write the decrypted data
  4130.             if!AKFactory::get('kickstart.setup.dryrun','0') )
  4131.                 if(is_resource($outfp)) @fwrite$outfp$dataakstringlen($data) );
  4132.  
  4133.             // Update the read length
  4134.             $this->dataReadLength += $unc_len;
  4135.             $leftBytes $this->fileHeader->uncompressed $this->dataReadLength;
  4136.         }
  4137.  
  4138.         // Close the file pointer
  4139.         if!AKFactory::get('kickstart.setup.dryrun','0') )
  4140.             if(is_resource($outfp)) @fclose($outfp);
  4141.  
  4142.         // Was this a pre-timeout bail out?
  4143.         if$leftBytes )
  4144.         {
  4145.             $this->runState AK_STATE_DATA;
  4146.         }
  4147.         else
  4148.         {
  4149.             // Oh! We just finished!
  4150.             $this->runState AK_STATE_DATAREAD;
  4151.             $this->dataReadLength 0;
  4152.         }
  4153.     }
  4154.  
  4155.     /**
  4156.      * Process the file data of a link entry
  4157.      * @return bool 
  4158.      */
  4159.     private function processTypeLink()
  4160.     {
  4161.  
  4162.         // Does the file have any data, at all?
  4163.         if$this->fileHeader->uncompressed == )
  4164.         {
  4165.             // No file data!
  4166.             $this->runState AK_STATE_DATAREAD;
  4167.             return true;
  4168.         }
  4169.  
  4170.         // Read the mini header
  4171.         $binMiniHeader fread($this->fp8);
  4172.         $reallyReadBytes akstringlen($binMiniHeader);
  4173.         if($reallyReadBytes 8)
  4174.         {
  4175.             // We read less than requested! Why? Did we hit local EOF?
  4176.             if$this->isEOF(true&& !$this->isEOF(false) )
  4177.             {
  4178.                 // Yeap. Let's go to the next file
  4179.                 $this->nextFile();
  4180.                 // Retry reading the header
  4181.                 $binMiniHeader fread($this->fp8);
  4182.                 $reallyReadBytes akstringlen($binMiniHeader);
  4183.                 // Still not enough data? If so, the archive is corrupt or missing parts.
  4184.                 if($reallyReadBytes 8{
  4185.                     $this->setErrorAKText::_('ERR_CORRUPT_ARCHIVE') );
  4186.                     return false;
  4187.                 }
  4188.             }
  4189.             else
  4190.             {
  4191.                 // Nope. The archive is corrupt
  4192.                 $this->setErrorAKText::_('ERR_CORRUPT_ARCHIVE') );
  4193.                 return false;
  4194.             }
  4195.         }
  4196.  
  4197.         // Read the encrypted data
  4198.         $miniHeader unpack('Vencsize/Vdecsize'$binMiniHeader);
  4199.         $toReadBytes $miniHeader['encsize'];
  4200.         $data $this->fread$this->fp$toReadBytes );
  4201.         $reallyReadBytes akstringlen($data);
  4202.         if($reallyReadBytes $toReadBytes)
  4203.         {
  4204.             // We read less than requested! Why? Did we hit local EOF?
  4205.             if$this->isEOF(true&& !$this->isEOF(false) )
  4206.             {
  4207.                 // Yeap. Let's go to the next file
  4208.                 $this->nextFile();
  4209.                 // Read the rest of the data
  4210.                 $toReadBytes -= $reallyReadBytes;
  4211.                 $restData $this->fread$this->fp$toReadBytes );
  4212.                 $reallyReadBytes akstringlen($data);
  4213.                 if($reallyReadBytes $toReadBytes{
  4214.                     $this->setErrorAKText::_('ERR_CORRUPT_ARCHIVE') );
  4215.                     return false;
  4216.                 }
  4217.                 $data .= $restData;
  4218.             }
  4219.             else
  4220.             {
  4221.                 // Nope. The archive is corrupt
  4222.                 $this->setErrorAKText::_('ERR_CORRUPT_ARCHIVE') );
  4223.                 return false;
  4224.             }
  4225.         }
  4226.  
  4227.         // Decrypt the data
  4228.         $data AKEncryptionAES::AESDecryptCBC($data$this->password128);
  4229.  
  4230.         // Is the length of the decrypted data less than expected?
  4231.         $data_length akstringlen($data);
  4232.         if($data_length $miniHeader['decsize']{
  4233.             $this->setError(AKText::_('ERR_INVALID_JPS_PASSWORD'));
  4234.             return false;
  4235.         }
  4236.  
  4237.         // Trim the data
  4238.         $data substr($data,0,$miniHeader['decsize']);
  4239.  
  4240.         // Try to remove an existing file or directory by the same name
  4241.         if(file_exists($this->fileHeader->realFile)) @unlink($this->fileHeader->realFile)@rmdir($this->fileHeader->realFile)}
  4242.         // Remove any trailing slash
  4243.         if(substr($this->fileHeader->realFile-1== '/'$this->fileHeader->realFile substr($this->fileHeader->realFile0-1);
  4244.         // Create the symlink - only possible within PHP context. There's no support built in the FTP protocol, so no postproc use is possible here :(
  4245.         if!AKFactory::get('kickstart.setup.dryrun','0') )
  4246.             @symlink($data$this->fileHeader->realFile);
  4247.  
  4248.         $this->runState AK_STATE_DATAREAD;
  4249.  
  4250.         return true// No matter if the link was created!
  4251.     }
  4252.  
  4253.     /**
  4254.      * Process the file data of a directory entry
  4255.      * @return bool 
  4256.      */
  4257.     private function processTypeDir()
  4258.     {
  4259.         // Directory entries in the JPA do not have file data, therefore we're done processing the entry
  4260.         $this->runState AK_STATE_DATAREAD;
  4261.         return true;
  4262.     }
  4263.  
  4264.     /**
  4265.      * Creates the directory this file points to
  4266.      */
  4267.     protected function createDirectory()
  4268.     {
  4269.         ifAKFactory::get('kickstart.setup.dryrun','0') ) return true;
  4270.  
  4271.         // Do we need to create a directory?
  4272.         $lastSlash strrpos($this->fileHeader->realFile'/');
  4273.         $dirName substr$this->fileHeader->realFile0$lastSlash);
  4274.         $perms $this->flagRestorePermissions $retArray['permissions'0755;
  4275.         $ignore AKFactory::get('kickstart.setup.ignoreerrors'false);
  4276.         if( ($this->postProcEngine->createDirRecursive($dirName$perms== false&& (!$ignore) ) {
  4277.             $this->setErrorAKText::sprintf('COULDNT_CREATE_DIR'$dirName) );
  4278.             return false;
  4279.         }
  4280.         else
  4281.         {
  4282.             return true;
  4283.         }
  4284.     }
  4285. }
  4286.  
  4287. /**
  4288.  * A filesystem scanner which uses opendir()
  4289.  */
  4290. {
  4291.     public function &getFiles($folder$pattern '*')
  4292.     {
  4293.         // Initialize variables
  4294.         $arr array();
  4295.         $false false;
  4296.  
  4297.         if(!is_dir($folder)) return $false;
  4298.  
  4299.         $handle @opendir($folder);
  4300.         // If directory is not accessible, just return FALSE
  4301.         if ($handle === FALSE{
  4302.             $this->setWarning'Unreadable directory '.$folder);
  4303.             return $false;
  4304.         }
  4305.  
  4306.         while (($file @readdir($handle)) !== false)
  4307.         {
  4308.             if!fnmatch($pattern$file) ) continue;
  4309.  
  4310.             if (($file != '.'&& ($file != '..'))
  4311.             {
  4312.                 $ds ($folder == ''|| ($folder == '/'|| (@substr($folder-1== '/'|| (@substr($folder-1== DIRECTORY_SEPARATOR'' DIRECTORY_SEPARATOR;
  4313.                 $dir $folder $ds $file;
  4314.                 $isDir is_dir($dir);
  4315.                 if (!$isDir{
  4316.                     $arr[$dir;
  4317.                 }
  4318.             }
  4319.         }
  4320.         @closedir($handle);
  4321.  
  4322.         return $arr;
  4323.     }
  4324.  
  4325.     public function &getFolders($folder$pattern '*')
  4326.     {
  4327.         // Initialize variables
  4328.         $arr array();
  4329.         $false false;
  4330.  
  4331.         if(!is_dir($folder)) return $false;
  4332.  
  4333.         $handle @opendir($folder);
  4334.         // If directory is not accessible, just return FALSE
  4335.         if ($handle === FALSE{
  4336.             $this->setWarning'Unreadable directory '.$folder);
  4337.             return $false;
  4338.         }
  4339.  
  4340.         while (($file @readdir($handle)) !== false)
  4341.         {
  4342.             if!fnmatch($pattern$file) ) continue;
  4343.  
  4344.             if (($file != '.'&& ($file != '..'))
  4345.             {
  4346.                 $ds ($folder == ''|| ($folder == '/'|| (@substr($folder-1== '/'|| (@substr($folder-1== DIRECTORY_SEPARATOR'' DIRECTORY_SEPARATOR;
  4347.                 $dir $folder $ds $file;
  4348.                 $isDir is_dir($dir);
  4349.                 if ($isDir{
  4350.                     $arr[$dir;
  4351.                 }
  4352.             }
  4353.         }
  4354.         @closedir($handle);
  4355.  
  4356.         return $arr;
  4357.     }
  4358. }
  4359.  
  4360. /**
  4361.  * A simple INI-based i18n engine
  4362.  */
  4363.  
  4364. class AKText extends AKAbstractObject
  4365. {
  4366.     /**
  4367.      * The default (en_GB) translation used when no other translation is available
  4368.      * @var array 
  4369.      */
  4370.     private $default_translation array(
  4371.         'AUTOMODEON' => 'Auto-mode enabled',
  4372.         'ERR_NOT_A_JPA_FILE' => 'The file is not a JPA archive',
  4373.         'ERR_CORRUPT_ARCHIVE' => 'The archive file is corrupt, truncated or archive parts are missing',
  4374.         'ERR_INVALID_LOGIN' => 'Invalid login',
  4375.         'COULDNT_CREATE_DIR' => 'Could not create %s folder',
  4376.         'COULDNT_WRITE_FILE' => 'Could not open %s for writing.',
  4377.         'WRONG_FTP_HOST' => 'Wrong FTP host or port',
  4378.         'WRONG_FTP_USER' => 'Wrong FTP username or password',
  4379.         'WRONG_FTP_PATH1' => 'Wrong FTP initial directory - the directory doesn\'t exist',
  4380.         'FTP_CANT_CREATE_DIR' => 'Could not create directory %s',
  4381.         'FTP_TEMPDIR_NOT_WRITABLE' => 'Could not find or create a writable temporary directory',
  4382.         'FTP_COULDNT_UPLOAD' => 'Could not upload %s',
  4383.         'THINGS_HEADER' => 'Things you should know about Akeeba Kickstart',
  4384.         'THINGS_01' => 'Kickstart is not an installer. It is an archive extraction tool. The actual installer was put inside the archive file at backup time.',
  4385.         'THINGS_02' => 'Kickstart is not the only way to extract the backup archive. You can use Akeeba eXtract Wizard and upload the extracted files using FTP instead.',
  4386.         'THINGS_03' => 'Kickstart is bound by your server\'s configuration. As such, it may not work at all.',
  4387.         'THINGS_04' => 'You should download and upload your archive files using FTP in Binary transfer mode. Any other method could lead to a corrupt backup archive and restoration failure.',
  4388.         'THINGS_05' => 'Post-restoration site load errors are usually caused by .htaccess or php.ini directives. You should understand that blank pages, 404 and 500 errors can usually be worked around by editing the aforementioned files. It is not our job to mess with your configuration files, because this could be dangerous for your site.',
  4389.         'THINGS_06' => 'Kickstart overwrites files without a warning. If you are not sure that you are OK with that do not continue.',
  4390.         'THINGS_07' => 'Trying to restore to the temporary URL of a cPanel host (e.g. http://1.2.3.4/~username) will lead to restoration failure and your site will appear to be not working. This is normal and it\'s just how your server and CMS software work.',
  4391.         'THINGS_08' => 'You are supposed to read the documentation before using this software. Most issues can be avoided, or easily worked around, by understanding how this software works.',
  4392.         'THINGS_09' => 'This text does not imply that there is a problem detected. It is standard text displayed every time you launch Kickstart.',
  4393.         'CLOSE_LIGHTBOX' => 'Click here or press ESC to close this message',
  4394.         'SELECT_ARCHIVE' => 'Select a backup archive',
  4395.         'ARCHIVE_FILE' => 'Archive file:',
  4396.         'SELECT_EXTRACTION' => 'Select an extraction method',
  4397.         'WRITE_TO_FILES' => 'Write to files:',
  4398.         'WRITE_DIRECTLY' => 'Directly',
  4399.         'WRITE_FTP' => 'Use FTP',
  4400.         'FTP_HOST' => 'FTP host name:',
  4401.         'FTP_PORT' => 'FTP port:',
  4402.         'FTP_FTPS' => 'Use FTP over SSL (FTPS)',
  4403.         'FTP_PASSIVE' => 'Use FTP Passive Mode',
  4404.         'FTP_USER' => 'FTP user name:',
  4405.         'FTP_PASS' => 'FTP password:',
  4406.         'FTP_DIR' => 'FTP directory:',
  4407.         'FTP_TEMPDIR' => 'Temporary directory:',
  4408.         'FTP_CONNECTION_OK' => 'FTP Connection Established',
  4409.         'FTP_CONNECTION_FAILURE' => 'The FTP Connection Failed',
  4410.         'FTP_TEMPDIR_WRITABLE' => 'The temporary directory is writable.',
  4411.         'FTP_TEMPDIR_UNWRITABLE' => 'The temporary directory is not writable. Please check the permissions.',
  4412.         'BTN_CHECK' => 'Check',
  4413.         'BTN_RESET' => 'Reset',
  4414.         'BTN_TESTFTPCON' => 'Test FTP connection',
  4415.         'BTN_GOTOSTART' => 'Start over',
  4416.         'FINE_TUNE' => 'Fine tune',
  4417.         'MIN_EXEC_TIME' => 'Minimum execution time:',
  4418.         'MAX_EXEC_TIME' => 'Maximum execution time:',
  4419.         'SECONDS_PER_STEP' => 'seconds per step',
  4420.         'EXTRACT_FILES' => 'Extract files',
  4421.         'BTN_START' => 'Start',
  4422.         'EXTRACTING' => 'Extracting',
  4423.         'DO_NOT_CLOSE_EXTRACT' => 'Do not close this window while the extraction is in progress',
  4424.         'RESTACLEANUP' => 'Restoration and Clean Up',
  4425.         'BTN_RUNINSTALLER' => 'Run the Installer',
  4426.         'BTN_CLEANUP' => 'Clean Up',
  4427.         'BTN_SITEFE' => 'Visit your site\'s front-end',
  4428.         'BTN_SITEBE' => 'Visit your site\'s back-end',
  4429.         'WARNINGS' => 'Extraction Warnings',
  4430.         'ERROR_OCCURED' => 'An error occured',
  4431.         'STEALTH_MODE' => 'Stealth mode',
  4432.         'STEALTH_URL' => 'HTML file to show to web visitors',
  4433.         'ERR_NOT_A_JPS_FILE' => 'The file is not a JPA archive',
  4434.         'ERR_INVALID_JPS_PASSWORD' => 'The password you gave is wrong or the archive is corrupt',
  4435.         'JPS_PASSWORD' => 'Archive Password (for JPS files)',
  4436.         'INVALID_FILE_HEADER' => 'Invalid header in archive file, part %s, offset %s',
  4437.         'NEEDSOMEHELPKS' => 'Want some help to use this tool? Read this first:',
  4438.         'QUICKSTART' => 'Quick Start Guide',
  4439.         'CANTGETITTOWORK' => 'Can\'t get it to work? Click me!',
  4440.         'NOARCHIVESCLICKHERE' => 'No archives detected. Click here for troubleshooting instructions.',
  4441.         'POSTRESTORATIONTROUBLESHOOTING' => 'Something not working after the restoration? Click here for troubleshooting instructions.',
  4442.         'UPDATE_HEADER' => 'An updated version of Akeeba Kickstart (<span id="update-version">unknown</span>) is available!',
  4443.         'UPDATE_NOTICE' => 'You are advised to always use the latest version of Akeeba Kickstart available. Older versions may be subject to bugs and will not be supported.',
  4444.         'UPDATE_DLNOW' => 'Download now',
  4445.         'UPDATE_MOREINFO' => 'More information'
  4446.     );
  4447.  
  4448.     /**
  4449.      * The array holding the translation keys
  4450.      * @var array 
  4451.      */
  4452.     private $strings;
  4453.  
  4454.     /**
  4455.      * The currently detected language (ISO code)
  4456.      * @var string 
  4457.      */
  4458.     private $language;
  4459.  
  4460.     /*
  4461.      * Initializes the translation engine
  4462.      * @return AKText
  4463.      */
  4464.     public function __construct()
  4465.     {
  4466.         // Start with the default translation
  4467.         $this->strings $this->default_translation;
  4468.         // Try loading the translation file in English, if it exists
  4469.         $this->loadTranslation('en-GB');
  4470.         // Try loading the translation file in the browser's preferred language, if it exists
  4471.         $this->getBrowserLanguage();
  4472.         if(!is_null($this->language))
  4473.         {
  4474.             $this->loadTranslation();
  4475.         }
  4476.     }
  4477.  
  4478.     /**
  4479.      * Singleton pattern for Language
  4480.      * @return Language The global Language instance
  4481.      */
  4482.     public static function &getInstance()
  4483.     {
  4484.         static $instance;
  4485.  
  4486.         if(!is_object($instance))
  4487.         {
  4488.             $instance new AKText();
  4489.         }
  4490.  
  4491.         return $instance;
  4492.     }
  4493.  
  4494.     public static function _($string)
  4495.     {
  4496.         $text self::getInstance();
  4497.  
  4498.         $key strtoupper($string);
  4499.         $key substr($key01== '_' substr($key1$key;
  4500.  
  4501.         if (isset ($text->strings[$key]))
  4502.         {
  4503.             $string $text->strings[$key];
  4504.         }
  4505.         else
  4506.         {
  4507.             if (defined($string))
  4508.             {
  4509.                 $string constant($string);
  4510.             }
  4511.         }
  4512.  
  4513.         return $string;
  4514.     }
  4515.  
  4516.     public static function sprintf($key)
  4517.     {
  4518.         $text self::getInstance();
  4519.         $args func_get_args();
  4520.         if (count($args0{
  4521.             $args[0$text->_($args[0]);
  4522.             return @call_user_func_array('sprintf'$args);
  4523.         }
  4524.         return '';
  4525.     }
  4526.  
  4527.     public function dumpLanguage()
  4528.     {
  4529.         $out '';
  4530.         foreach($this->strings as $key => $value)
  4531.         {
  4532.             $out .= "$key=$value\n";
  4533.         }
  4534.         return $out;
  4535.     }
  4536.  
  4537.     public function asJavascript()
  4538.     {
  4539.         $out '';
  4540.         foreach($this->strings as $key => $value)
  4541.         {
  4542.             $key addcslashes($key'\\\'"');
  4543.             $value addcslashes($value'\\\'"');
  4544.             if(!empty($out)) $out .= ",\n";
  4545.             $out .= "'$key':\t'$value'";
  4546.         }
  4547.         return $out;
  4548.     }
  4549.  
  4550.     public function resetTranslation()
  4551.     {
  4552.         $this->strings $this->default_translation;
  4553.     }
  4554.  
  4555.     public function getBrowserLanguage()
  4556.     {
  4557.         // Detection code from Full Operating system language detection, by Harald Hope
  4558.         // Retrieved from http://techpatterns.com/downloads/php_language_detection.php
  4559.         $user_languages array();
  4560.         //check to see if language is set
  4561.         if isset$_SERVER["HTTP_ACCEPT_LANGUAGE") )
  4562.         {
  4563.             $languages strtolower$_SERVER["HTTP_ACCEPT_LANGUAGE");
  4564.             // $languages = ' fr-ch;q=0.3, da, en-us;q=0.8, en;q=0.5, fr;q=0.3';
  4565.             // need to remove spaces from strings to avoid error
  4566.             $languages str_replace' '''$languages );
  4567.             $languages explode","$languages );
  4568.  
  4569.             foreach $languages as $language_list )
  4570.             {
  4571.                 // pull out the language, place languages into array of full and primary
  4572.                 // string structure:
  4573.                 $temp_array array();
  4574.                 // slice out the part before ; on first step, the part before - on second, place into array
  4575.                 $temp_array[0substr$language_list0strcspn$language_list';' ) );//full language
  4576.                 $temp_array[1substr$language_list0);// cut out primary language
  4577.                 if( (strlen($temp_array[0]== 5&& ( (substr($temp_array[0],2,1== '-'|| (substr($temp_array[0],2,1== '_') ) )
  4578.                 {
  4579.                     $langLocation strtoupper(substr($temp_array[0],3,2));
  4580.                     $temp_array[0$temp_array[1].'-'.$langLocation;
  4581.                 }
  4582.                 //place this array into main $user_languages language array
  4583.                 $user_languages[$temp_array;
  4584.             }
  4585.         }
  4586.         else// if no languages found
  4587.         {
  4588.             $user_languages[0array'','' )//return blank array.
  4589.         }
  4590.  
  4591.         $this->language null;
  4592.         $basename=basename(__FILE__'.php''.ini';
  4593.         
  4594.         // Try to match main language part of the filename, irrespective of the location, e.g. de_DE will do if de_CH doesn't exist.
  4595.         $fs new AKUtilsLister();
  4596.         $iniFiles $fs->getFilesdirname(__FILE__)'*.'.$basename );
  4597.         if(empty($iniFiles&& ($basename != 'kickstart.ini')) {
  4598.             $basename 'kickstart.ini';
  4599.             $iniFiles $fs->getFilesdirname(__FILE__)'*.'.$basename );
  4600.         }
  4601.         if (is_array($iniFiles)) {
  4602.             foreach($user_languages as $languageStruct)
  4603.             {
  4604.                 if(is_null($this->language))
  4605.                 {
  4606.                     // Get files matching the main lang part
  4607.                     $iniFiles $fs->getFilesdirname(__FILE__)$languageStruct[1].'-??.'.$basename );
  4608.                     if (count($iniFiles0{
  4609.                         $filename $iniFiles[0];
  4610.                         $filename substr($filenamestrlen(dirname(__FILE__))+1);
  4611.                         $this->language substr($filename05);
  4612.                     else {
  4613.                         $this->language null;
  4614.                     }
  4615.                 }
  4616.             }
  4617.         }
  4618.         
  4619.         if(is_null($this->language)) {
  4620.             // Try to find a full language match
  4621.             foreach($user_languages as $languageStruct)
  4622.             {
  4623.                 if (@file_exists($languageStruct[0].'.'.$basename&& is_null($this->language)) {
  4624.                     $this->language $languageStruct[0];
  4625.                 else {
  4626.  
  4627.                 }
  4628.             }
  4629.         else {
  4630.             // Do we have an exact match?
  4631.             foreach($user_languages as $languageStruct)
  4632.             {
  4633.                 if(substr($this->language,0,strlen($languageStruct[1])) == $languageStruct[1]{
  4634.                     if(file_exists($languageStruct[0].'.'.$basename)) {
  4635.                         $this->language $languageStruct[0];
  4636.                     }
  4637.                 }
  4638.             }
  4639.         }
  4640.         
  4641.         // Now, scan for full language based on the partial match
  4642.         
  4643.     }
  4644.  
  4645.     private function loadTranslation$lang null )
  4646.     {
  4647.         $dirname function_exists('getcwd'getcwd(dirname(__FILE__);
  4648.         $basename=basename(__FILE__'.php''.ini';
  4649.         ifempty($lang) ) $lang $this->language;
  4650.  
  4651.         $translationFilename $dirname.DIRECTORY_SEPARATOR.$lang.'.'.$basename;
  4652.         if(!@file_exists($translationFilename&& ($basename != 'kickstart.ini')) {
  4653.             $basename 'kickstart.ini';
  4654.             $translationFilename $dirname.DIRECTORY_SEPARATOR.$lang.'.'.$basename;
  4655.         }
  4656.         if(!@file_exists($translationFilename)) return;
  4657.         $temp self::parse_ini_file($translationFilenamefalse);
  4658.  
  4659.         if(!is_array($this->strings)) $this->strings array();
  4660.         if(empty($temp)) {
  4661.             $this->strings array_merge($this->default_translation$this->strings);
  4662.         else {
  4663.             $this->strings array_merge($this->strings$temp);
  4664.         }
  4665.     }
  4666.  
  4667.     /**
  4668.      * A PHP based INI file parser.
  4669.      *
  4670.      * Thanks to asohn ~at~ aircanopy ~dot~ net for posting this handy function on
  4671.      * the parse_ini_file page on http://gr.php.net/parse_ini_file
  4672.      *
  4673.      * @param string $file Filename to process
  4674.      * @param bool $process_sections True to also process INI sections
  4675.      * @return array An associative array of sections, keys and values
  4676.      * @access private
  4677.      */
  4678.     public static function parse_ini_file($file$process_sections false$raw_data false)
  4679.     {
  4680.         $process_sections ($process_sections !== truefalse true;
  4681.  
  4682.         if(!$raw_data)
  4683.         {
  4684.             $ini @file($file);
  4685.         }
  4686.         else
  4687.         {
  4688.             $ini $file;
  4689.         }
  4690.         if (count($ini== 0{return array();}
  4691.  
  4692.         $sections array();
  4693.         $values array();
  4694.         $result array();
  4695.         $globals array();
  4696.         $i 0;
  4697.         if(!empty($ini)) foreach ($ini as $line{
  4698.             $line trim($line);
  4699.             $line str_replace("\t"" "$line);
  4700.  
  4701.             // Comments
  4702.             if (!preg_match('/^[a-zA-Z0-9[]/'$line)) {continue;}
  4703.  
  4704.             // Sections
  4705.             if ($line{0== '['{
  4706.                 $tmp explode(']'$line);
  4707.                 $sections[trim(substr($tmp[0]1));
  4708.                 $i++;
  4709.                 continue;
  4710.             }
  4711.  
  4712.             // Key-value pair
  4713.             list($key$valueexplode('='$line2);
  4714.             $key trim($key);
  4715.             $value trim($value);
  4716.             if (strstr($value";")) {
  4717.                 $tmp explode(';'$value);
  4718.                 if (count($tmp== 2{
  4719.                     if ((($value{0!= '"'&& ($value{0!= "'")) ||
  4720.                     preg_match('/^".*"\s*;/'$value|| preg_match('/^".*;[^"]*$/'$value||
  4721.                     preg_match("/^'.*'\s*;/"$value|| preg_match("/^'.*;[^']*$/"$value) ){
  4722.                         $value $tmp[0];
  4723.                     }
  4724.                 else {
  4725.                     if ($value{0== '"'{
  4726.                         $value preg_replace('/^"(.*)".*/''$1'$value);
  4727.                     elseif ($value{0== "'"{
  4728.                         $value preg_replace("/^'(.*)'.*/"'$1'$value);
  4729.                     else {
  4730.                         $value $tmp[0];
  4731.                     }
  4732.                 }
  4733.             }
  4734.             $value trim($value);
  4735.             $value trim($value"'\"");
  4736.  
  4737.             if ($i == 0{
  4738.                 if (substr($line-12== '[]'{
  4739.                     $globals[$key][$value;
  4740.                 else {
  4741.                     $globals[$key$value;
  4742.                 }
  4743.             else {
  4744.                 if (substr($line-12== '[]'{
  4745.                     $values[$i-1][$key][$value;
  4746.                 else {
  4747.                     $values[$i-1][$key$value;
  4748.                 }
  4749.             }
  4750.         }
  4751.  
  4752.         for($j 0$j $i$j++{
  4753.             if ($process_sections === true{
  4754.                 $result[$sections[$j]] $values[$j];
  4755.             else {
  4756.                 $result[$values[$j];
  4757.             }
  4758.         }
  4759.  
  4760.         return $result $globals;
  4761.     }
  4762. }
  4763.  
  4764. /**
  4765.  * The Akeeba Kickstart Factory class
  4766.  * This class is reponssible for instanciating all Akeeba Kicsktart classes
  4767.  */
  4768. class AKFactory {
  4769.     /** @var array A list of instanciated objects */
  4770.     private $objectlist array();
  4771.  
  4772.     /** @var array Simple hash data storage */
  4773.     private $varlist array();
  4774.  
  4775.     /** Private constructor makes sure we can't directly instanciate the class */
  4776.     private function __construct({}
  4777.  
  4778.     /**
  4779.      * Gets a single, internally used instance of the Factory
  4780.      * @param string $serialized_data [optional] Serialized data to spawn the instance from
  4781.      * @return AKFactory A reference to the unique Factory object instance
  4782.      */
  4783.     protected static function &getInstance$serialized_data null {
  4784.         static $myInstance;
  4785.         if(!is_object($myInstance|| !is_null($serialized_data))
  4786.             if(!is_null($serialized_data))
  4787.             {
  4788.                 $myInstance unserialize($serialized_data);
  4789.             }
  4790.             else
  4791.             {
  4792.                 $myInstance new self();
  4793.             }
  4794.         return $myInstance;
  4795.     }
  4796.  
  4797.     /**
  4798.      * Internal function which instanciates a class named $class_name.
  4799.      * The autoloader
  4800.      * @param object $class_name 
  4801.      * @return 
  4802.      */
  4803.     protected static function &getClassInstance($class_name{
  4804.         $self self::getInstance();
  4805.         if(!isset($self->objectlist[$class_name]))
  4806.         {
  4807.             $self->objectlist[$class_namenew $class_name;
  4808.         }
  4809.         return $self->objectlist[$class_name];
  4810.     }
  4811.  
  4812.     // ========================================================================
  4813.     // Public factory interface
  4814.     // ========================================================================
  4815.  
  4816.     /**
  4817.      * Gets a serialized snapshot of the Factory for safekeeping (hibernate)
  4818.      * @return string The serialized snapshot of the Factory
  4819.      */
  4820.     public static function serialize({
  4821.         $engine self::getUnarchiver();
  4822.         $engine->shutdown();
  4823.         $serialized serialize(self::getInstance());
  4824.  
  4825.         if(function_exists('base64_encode'&& function_exists('base64_decode'))
  4826.         {
  4827.             $serialized base64_encode($serialized);
  4828.         }
  4829.         return $serialized;
  4830.     }
  4831.  
  4832.     /**
  4833.      * Regenerates the full Factory state from a serialized snapshot (resume)
  4834.      * @param string $serialized_data The serialized snapshot to resume from
  4835.      */
  4836.     public static function unserialize($serialized_data{
  4837.         if(function_exists('base64_encode'&& function_exists('base64_decode'))
  4838.         {
  4839.             $serialized_data base64_decode($serialized_data);
  4840.         }
  4841.         self::getInstance($serialized_data);
  4842.     }
  4843.  
  4844.     /**
  4845.      * Reset the internal factory state, freeing all previously created objects
  4846.      */
  4847.     public static function nuke()
  4848.     {
  4849.         $self self::getInstance();
  4850.         foreach($self->objectlist as $key => $object)
  4851.         {
  4852.             $self->objectlist[$keynull;
  4853.         }
  4854.         $self->objectlist array();
  4855.     }
  4856.  
  4857.     // ========================================================================
  4858.     // Public hash data storage interface
  4859.     // ========================================================================
  4860.  
  4861.     public static function set($key$value)
  4862.     {
  4863.         $self self::getInstance();
  4864.         $self->varlist[$key$value;
  4865.     }
  4866.  
  4867.     public static function get($key$default null)
  4868.     {
  4869.         $self self::getInstance();
  4870.         ifarray_key_exists($key$self->varlist) )
  4871.         {
  4872.             return $self->varlist[$key];
  4873.         }
  4874.         else
  4875.         {
  4876.             return $default;
  4877.         }
  4878.     }
  4879.  
  4880.     // ========================================================================
  4881.     // Akeeba Kickstart classes
  4882.     // ========================================================================
  4883.  
  4884.     /**
  4885.      * Gets the post processing engine
  4886.      * @param string $proc_engine 
  4887.      */
  4888.     public static function &getPostProc($proc_engine null)
  4889.     {
  4890.         static $class_name;
  4891.         ifempty($class_name) )
  4892.         {
  4893.             if(empty($proc_engine))
  4894.             {
  4895.                 $proc_engine self::get('kickstart.procengine','direct');
  4896.             }
  4897.             $class_name 'AKPostproc'.ucfirst($proc_engine);
  4898.         }
  4899.         return self::getClassInstance($class_name);
  4900.     }
  4901.  
  4902.     /**
  4903.      * Gets the unarchiver engine
  4904.      */
  4905.     public static function &getUnarchiver$configOverride null )
  4906.     {
  4907.         static $class_name;
  4908.  
  4909.         if(!empty($configOverride))
  4910.         {
  4911.             if($configOverride['reset']{
  4912.                 $class_name null;
  4913.             }
  4914.         }
  4915.  
  4916.         ifempty($class_name) )
  4917.         {
  4918.             $filetype self::get('kickstart.setup.filetype'null);
  4919.  
  4920.             if(empty($filetype))
  4921.             {
  4922.                 $filename self::get('kickstart.setup.sourcefile'null);
  4923.                 $basename basename($filename);
  4924.                 $baseextension strtoupper(substr($basename,-3));
  4925.                 switch($baseextension)
  4926.                 {
  4927.                     case 'JPA':
  4928.                         $filetype 'JPA';
  4929.                         break;
  4930.  
  4931.                     case 'JPS':
  4932.                         $filetype 'JPS';
  4933.                         break;
  4934.  
  4935.                     case 'ZIP':
  4936.                         $filetype 'ZIP';
  4937.                         break;
  4938.  
  4939.                     default:
  4940.                         die('Invalid archive type or extension in file '.$filename);
  4941.                         break;
  4942.                 }
  4943.             }
  4944.  
  4945.             $class_name 'AKUnarchiver'.ucfirst($filetype);
  4946.         }
  4947.  
  4948.         $destdir self::get('kickstart.setup.destdir'null);
  4949.         if(empty($destdir))
  4950.         {
  4951.             $destdir function_exists('getcwd'getcwd(dirname(__FILE__);
  4952.         }
  4953.  
  4954.         $object self::getClassInstance($class_name);
  4955.         if$object->getState(== 'init')
  4956.         {
  4957.             // Initialize the object
  4958.             $config array(
  4959.                 'filename'                => self::get('kickstart.setup.sourcefile'''),
  4960.                 'restore_permissions'    => self::get('kickstart.setup.restoreperms'0),
  4961.                 'post_proc'                => self::get('kickstart.procengine''direct'),
  4962.                 'add_path'                => $destdir,
  4963.                 'rename_files'            => array'.htaccess' => 'htaccess.bak''php.ini' => 'php.ini.bak' ),
  4964.                 'skip_files'            => arraybasename(__FILE__)'kickstart.php''abiautomation.ini''htaccess.bak''php.ini.bak' )
  4965.             );
  4966.  
  4967.             if(!defined('KICKSTART'))
  4968.             {
  4969.                 // In restore.php mode we have to exclude some more files
  4970.                 $config['skip_files']['administrator/components/com_akeeba/restore.php';
  4971.                 $config['skip_files']['administrator/components/com_akeeba/restoration.php';
  4972.             }
  4973.  
  4974.             if(!empty($configOverride))
  4975.             {
  4976.                 foreach($configOverride as $key => $value)
  4977.                 {
  4978.                     $config[$key$value;
  4979.                 }
  4980.             }
  4981.  
  4982.             $object->setup($config);
  4983.         }
  4984.  
  4985.         return $object;
  4986.     }
  4987.  
  4988.     /**
  4989.      * Get the a reference to the Akeeba Engine's timer
  4990.      * @return AKCoreTimer 
  4991.      */
  4992.     public static function &getTimer()
  4993.     {
  4994.         return self::getClassInstance('AKCoreTimer');
  4995.     }
  4996.  
  4997. }
  4998.  
  4999. /**
  5000.  * AES implementation in PHP (c) Chris Veness 2005-2011
  5001.  * (http://www.movable-type.co.uk/scripts/aes-php.html)
  5002.  * I offer these formulæ & scripts for free use and adaptation as my contribution to the
  5003.  * open-source info-sphere from which I have received so much. You are welcome to re-use these
  5004.  * scripts [under a simple attribution license or a GPL licence, without any warranty express or implied]
  5005.  * provided solely that you retain my copyright notice and a link to this page.
  5006.  * licence. No warranty of any form is offered.
  5007.  *
  5008.  * Modified for Akeeba Backup by Nicholas K. Dionysopoulos
  5009.  */
  5010. {
  5011.     // Sbox is pre-computed multiplicative inverse in GF(2^8) used in SubBytes and KeyExpansion [�5.1.1]
  5012.     protected static $Sbox =
  5013.              array(0x63,0x7c,0x77,0x7b,0xf2,0x6b,0x6f,0xc5,0x30,0x01,0x67,0x2b,0xfe,0xd7,0xab,0x76,
  5014.                    0xca,0x82,0xc9,0x7d,0xfa,0x59,0x47,0xf0,0xad,0xd4,0xa2,0xaf,0x9c,0xa4,0x72,0xc0,
  5015.                    0xb7,0xfd,0x93,0x26,0x36,0x3f,0xf7,0xcc,0x34,0xa5,0xe5,0xf1,0x71,0xd8,0x31,0x15,
  5016.                    0x04,0xc7,0x23,0xc3,0x18,0x96,0x05,0x9a,0x07,0x12,0x80,0xe2,0xeb,0x27,0xb2,0x75,
  5017.                    0x09,0x83,0x2c,0x1a,0x1b,0x6e,0x5a,0xa0,0x52,0x3b,0xd6,0xb3,0x29,0xe3,0x2f,0x84,
  5018.                    0x53,0xd1,0x00,0xed,0x20,0xfc,0xb1,0x5b,0x6a,0xcb,0xbe,0x39,0x4a,0x4c,0x58,0xcf,
  5019.                    0xd0,0xef,0xaa,0xfb,0x43,0x4d,0x33,0x85,0x45,0xf9,0x02,0x7f,0x50,0x3c,0x9f,0xa8,
  5020.                    0x51,0xa3,0x40,0x8f,0x92,0x9d,0x38,0xf5,0xbc,0xb6,0xda,0x21,0x10,0xff,0xf3,0xd2,
  5021.                    0xcd,0x0c,0x13,0xec,0x5f,0x97,0x44,0x17,0xc4,0xa7,0x7e,0x3d,0x64,0x5d,0x19,0x73,
  5022.                    0x60,0x81,0x4f,0xdc,0x22,0x2a,0x90,0x88,0x46,0xee,0xb8,0x14,0xde,0x5e,0x0b,0xdb,
  5023.                    0xe0,0x32,0x3a,0x0a,0x49,0x06,0x24,0x5c,0xc2,0xd3,0xac,0x62,0x91,0x95,0xe4,0x79,
  5024.                    0xe7,0xc8,0x37,0x6d,0x8d,0xd5,0x4e,0xa9,0x6c,0x56,0xf4,0xea,0x65,0x7a,0xae,0x08,
  5025.                    0xba,0x78,0x25,0x2e,0x1c,0xa6,0xb4,0xc6,0xe8,0xdd,0x74,0x1f,0x4b,0xbd,0x8b,0x8a,
  5026.                    0x70,0x3e,0xb5,0x66,0x48,0x03,0xf6,0x0e,0x61,0x35,0x57,0xb9,0x86,0xc1,0x1d,0x9e,
  5027.                    0xe1,0xf8,0x98,0x11,0x69,0xd9,0x8e,0x94,0x9b,0x1e,0x87,0xe9,0xce,0x55,0x28,0xdf,
  5028.                    0x8c,0xa1,0x89,0x0d,0xbf,0xe6,0x42,0x68,0x41,0x99,0x2d,0x0f,0xb0,0x54,0xbb,0x16);
  5029.  
  5030.     // Rcon is Round Constant used for the Key Expansion [1st col is 2^(r-1) in GF(2^8)] [�5.2]
  5031.     protected static $Rcon array(
  5032.                    array(0x000x000x000x00),
  5033.                    array(0x010x000x000x00),
  5034.                    array(0x020x000x000x00),
  5035.                    array(0x040x000x000x00),
  5036.                    array(0x080x000x000x00),
  5037.                    array(0x100x000x000x00),
  5038.                    array(0x200x000x000x00),
  5039.                    array(0x400x000x000x00),
  5040.                    array(0x800x000x000x00),
  5041.                    array(0x1b0x000x000x00),
  5042.                    array(0x360x000x000x00) );
  5043.  
  5044.     protected static $passwords array();
  5045.  
  5046.     /**
  5047.      * AES Cipher function: encrypt 'input' with Rijndael algorithm
  5048.      *
  5049.      * @param input message as byte-array (16 bytes)
  5050.      * @param     key schedule as 2D byte-array (Nr+1 x Nb bytes) -
  5051.      *               generated from the cipher key by KeyExpansion()
  5052.      * @return      ciphertext as byte-array (16 bytes)
  5053.      */
  5054.     protected static function Cipher($input$w{    // main Cipher function [�5.1]
  5055.       $Nb 4;                 // block size (in words): no of columns in state (fixed at 4 for AES)
  5056.       $Nr count($w)/$Nb 1// no of rounds: 10/12/14 for 128/192/256-bit keys
  5057.  
  5058.       $state array();  // initialise 4xNb byte-array 'state' with input [�3.4]
  5059.       for ($i=0$i<4*$Nb$i++$state[$i%4][floor($i/4)$input[$i];
  5060.  
  5061.       $state self::AddRoundKey($state$w0$Nb);
  5062.  
  5063.       for ($round=1$round<$Nr$round++{  // apply Nr rounds
  5064.         $state self::SubBytes($state$Nb);
  5065.         $state self::ShiftRows($state$Nb);
  5066.         $state self::MixColumns($state$Nb);
  5067.         $state self::AddRoundKey($state$w$round$Nb);
  5068.       }
  5069.  
  5070.       $state self::SubBytes($state$Nb);
  5071.       $state self::ShiftRows($state$Nb);
  5072.       $state self::AddRoundKey($state$w$Nr$Nb);
  5073.  
  5074.       $output array(4*$Nb);  // convert state to 1-d array before returning [�3.4]
  5075.       for ($i=0$i<4*$Nb$i++$output[$i$state[$i%4][floor($i/4)];
  5076.       return $output;
  5077.     }
  5078.  
  5079.     protected static function AddRoundKey($state$w$rnd$Nb{  // xor Round Key into state S [�5.1.4]
  5080.       for ($r=0$r<4$r++{
  5081.         for ($c=0$c<$Nb$c++$state[$r][$c^= $w[$rnd*4+$c][$r];
  5082.       }
  5083.       return $state;
  5084.     }
  5085.  
  5086.     protected static function SubBytes($s$Nb{    // apply SBox to state S [�5.1.1]
  5087.       for ($r=0$r<4$r++{
  5088.         for ($c=0$c<$Nb$c++$s[$r][$cself::$Sbox[$s[$r][$c]];
  5089.       }
  5090.       return $s;
  5091.     }
  5092.  
  5093.     protected static function ShiftRows($s$Nb{    // shift row r of state S left by r bytes [�5.1.2]
  5094.       $t array(4);
  5095.       for ($r=1$r<4$r++{
  5096.         for ($c=0$c<4$c++$t[$c$s[$r][($c+$r)%$Nb];  // shift into temp copy
  5097.         for ($c=0$c<4$c++$s[$r][$c$t[$c];         // and copy back
  5098.       }          // note that this will work for Nb=4,5,6, but not 7,8 (always 4 for AES):
  5099.       return $s;  // see fp.gladman.plus.com/cryptography_technology/rijndael/aes.spec.311.pdf
  5100.     }
  5101.  
  5102.     protected static function MixColumns($s$Nb{   // combine bytes of each col of state S [�5.1.3]
  5103.       for ($c=0$c<4$c++{
  5104.         $a array(4);  // 'a' is a copy of the current column from 's'
  5105.         $b array(4);  // 'b' is a�{02} in GF(2^8)
  5106.         for ($i=0$i<4$i++{
  5107.           $a[$i$s[$i][$c];
  5108.           $b[$i$s[$i][$c]&0x80 $s[$i][$c]<<0x011b $s[$i][$c]<<1;
  5109.         }
  5110.         // a[n] ^ b[n] is a�{03} in GF(2^8)
  5111.         $s[0][$c$b[0$a[1$b[1$a[2$a[3]// 2*a0 + 3*a1 + a2 + a3
  5112.         $s[1][$c$a[0$b[1$a[2$b[2$a[3]// a0 * 2*a1 + 3*a2 + a3
  5113.         $s[2][$c$a[0$a[1$b[2$a[3$b[3]// a0 + a1 + 2*a2 + 3*a3
  5114.         $s[3][$c$a[0$b[0$a[1$a[2$b[3]// 3*a0 + a1 + a2 + 2*a3
  5115.       }
  5116.       return $s;
  5117.     }
  5118.  
  5119.     /**
  5120.      * Key expansion for Rijndael Cipher(): performs key expansion on cipher key
  5121.      * to generate a key schedule
  5122.      *
  5123.      * @param key cipher key byte-array (16 bytes)
  5124.      * @return    key schedule as 2D byte-array (Nr+1 x Nb bytes)
  5125.      */
  5126.     protected static function KeyExpansion($key{  // generate Key Schedule from Cipher Key [�5.2]
  5127.       $Nb 4;              // block size (in words): no of columns in state (fixed at 4 for AES)
  5128.       $Nk count($key)/4;  // key length (in words): 4/6/8 for 128/192/256-bit keys
  5129.       $Nr $Nk 6;        // no of rounds: 10/12/14 for 128/192/256-bit keys
  5130.  
  5131.       $w array();
  5132.       $temp array();
  5133.  
  5134.       for ($i=0$i<$Nk$i++{
  5135.         $r array($key[4*$i]$key[4*$i+1]$key[4*$i+2]$key[4*$i+3]);
  5136.         $w[$i$r;
  5137.       }
  5138.  
  5139.       for ($i=$Nk$i<($Nb*($Nr+1))$i++{
  5140.         $w[$iarray();
  5141.         for ($t=0$t<4$t++$temp[$t$w[$i-1][$t];
  5142.         if ($i $Nk == 0{
  5143.           $temp self::SubWord(self::RotWord($temp));
  5144.           for ($t=0$t<4$t++$temp[$t^= self::$Rcon[$i/$Nk][$t];
  5145.         else if ($Nk && $i%$Nk == 4{
  5146.           $temp self::SubWord($temp);
  5147.         }
  5148.         for ($t=0$t<4$t++$w[$i][$t$w[$i-$Nk][$t$temp[$t];
  5149.       }
  5150.       return $w;
  5151.     }
  5152.  
  5153.     protected static function SubWord($w{    // apply SBox to 4-byte word w
  5154.       for ($i=0$i<4$i++$w[$iself::$Sbox[$w[$i]];
  5155.       return $w;
  5156.     }
  5157.  
  5158.     protected static function RotWord($w{    // rotate 4-byte word w left by one byte
  5159.       $tmp $w[0];
  5160.       for ($i=0$i<3$i++$w[$i$w[$i+1];
  5161.       $w[3$tmp;
  5162.       return $w;
  5163.     }
  5164.  
  5165.     /*
  5166.      * Unsigned right shift function, since PHP has neither >>> operator nor unsigned ints
  5167.      *
  5168.      * @param a  number to be shifted (32-bit integer)
  5169.      * @param b  number of bits to shift a to the right (0..31)
  5170.      * @return   a right-shifted and zero-filled by b bits
  5171.      */
  5172.     protected static function urs($a$b{
  5173.       $a &= 0xffffffff$b &= 0x1f;  // (bounds check)
  5174.       if ($a&0x80000000 && $b>0{   // if left-most bit set
  5175.         $a ($a>>10x7fffffff;   //   right-shift one bit & clear left-most bit
  5176.         $a $a >> ($b-1);           //   remaining right-shifts
  5177.       else {                       // otherwise
  5178.         $a ($a>>$b);               //   use normal right-shift
  5179.       }
  5180.       return $a;
  5181.     }
  5182.  
  5183.     /**
  5184.      * Encrypt a text using AES encryption in Counter mode of operation
  5185.      *  - see http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf
  5186.      *
  5187.      * Unicode multi-byte character safe
  5188.      *
  5189.      * @param plaintext source text to be encrypted
  5190.      * @param password  the password to use to generate a key
  5191.      * @param nBits     number of bits to be used in the key (128, 192, or 256)
  5192.      * @return          encrypted text
  5193.      */
  5194.     public static function AESEncryptCtr($plaintext$password$nBits{
  5195.       $blockSize 16;  // block size fixed at 16 bytes / 128 bits (Nb=4) for AES
  5196.       if (!($nBits==128 || $nBits==192 || $nBits==256)) return '';  // standard allows 128/192/256 bit keys
  5197.       // note PHP (5) gives us plaintext and password in UTF8 encoding!
  5198.  
  5199.       // use AES itself to encrypt password to get cipher key (using plain password as source for
  5200.       // key expansion) - gives us well encrypted key
  5201.       $nBytes $nBits/8;  // no bytes in key
  5202.       $pwBytes array();
  5203.       for ($i=0$i<$nBytes$i++$pwBytes[$iord(substr($password,$i,1)) 0xff;
  5204.       $key self::Cipher($pwBytesself::KeyExpansion($pwBytes));
  5205.       $key array_merge($keyarray_slice($key0$nBytes-16));  // expand key to 16/24/32 bytes long
  5206.  
  5207.       // initialise counter block (NIST SP800-38A Ã¯Â¿Â½B.2): millisecond time-stamp for nonce in
  5208.       // 1st 8 bytes, block counter in 2nd 8 bytes
  5209.       $counterBlock array();
  5210.       $nonce floor(microtime(true)*1000);   // timestamp: milliseconds since 1-Jan-1970
  5211.       $nonceSec floor($nonce/1000);
  5212.       $nonceMs $nonce%1000;
  5213.       // encode nonce with seconds in 1st 4 bytes, and (repeated) ms part filling 2nd 4 bytes
  5214.       for ($i=0$i<4$i++$counterBlock[$iself::urs($nonceSec$i*80xff;
  5215.       for ($i=0$i<4$i++$counterBlock[$i+4$nonceMs 0xff;
  5216.       // and convert it to a string to go on the front of the ciphertext
  5217.       $ctrTxt '';
  5218.       for ($i=0$i<8$i++$ctrTxt .= chr($counterBlock[$i]);
  5219.  
  5220.       // generate key schedule - an expansion of the key into distinct Key Rounds for each round
  5221.       $keySchedule self::KeyExpansion($key);
  5222.  
  5223.       $blockCount ceil(strlen($plaintext)/$blockSize);
  5224.       $ciphertxt array();  // ciphertext as array of strings
  5225.  
  5226.       for ($b=0$b<$blockCount$b++{
  5227.         // set counter (block #) in last 8 bytes of counter block (leaving nonce in 1st 8 bytes)
  5228.         // done in two stages for 32-bit ops: using two words allows us to go past 2^32 blocks (68GB)
  5229.         for ($c=0$c<4$c++$counterBlock[15-$cself::urs($b$c*80xff;
  5230.         for ($c=0$c<4$c++$counterBlock[15-$c-4self::urs($b/0x100000000$c*8);
  5231.  
  5232.         $cipherCntr self::Cipher($counterBlock$keySchedule);  // -- encrypt counter block --
  5233.  
  5234.         // block size is reduced on final block
  5235.         $blockLength $b<$blockCount-$blockSize (strlen($plaintext)-1)%$blockSize+1;
  5236.         $cipherByte array();
  5237.  
  5238.         for ($i=0$i<$blockLength$i++{  // -- xor plaintext with ciphered counter byte-by-byte --
  5239.           $cipherByte[$i$cipherCntr[$iord(substr($plaintext$b*$blockSize+$i1));
  5240.           $cipherByte[$ichr($cipherByte[$i]);
  5241.         }
  5242.         $ciphertxt[$bimplode(''$cipherByte);  // escape troublesome characters in ciphertext
  5243.       }
  5244.  
  5245.       // implode is more efficient than repeated string concatenation
  5246.       $ciphertext $ctrTxt implode(''$ciphertxt);
  5247.       $ciphertext base64_encode($ciphertext);
  5248.       return $ciphertext;
  5249.     }
  5250.  
  5251.     /**
  5252.      * Decrypt a text encrypted by AES in counter mode of operation
  5253.      *
  5254.      * @param ciphertext source text to be decrypted
  5255.      * @param password   the password to use to generate a key
  5256.      * @param nBits      number of bits to be used in the key (128, 192, or 256)
  5257.      * @return           decrypted text
  5258.      */
  5259.     public static function AESDecryptCtr($ciphertext$password$nBits{
  5260.       $blockSize 16;  // block size fixed at 16 bytes / 128 bits (Nb=4) for AES
  5261.       if (!($nBits==128 || $nBits==192 || $nBits==256)) return '';  // standard allows 128/192/256 bit keys
  5262.       $ciphertext base64_decode($ciphertext);
  5263.  
  5264.       // use AES to encrypt password (mirroring encrypt routine)
  5265.       $nBytes $nBits/8;  // no bytes in key
  5266.       $pwBytes array();
  5267.       for ($i=0$i<$nBytes$i++$pwBytes[$iord(substr($password,$i,1)) 0xff;
  5268.       $key self::Cipher($pwBytesself::KeyExpansion($pwBytes));
  5269.       $key array_merge($keyarray_slice($key0$nBytes-16));  // expand key to 16/24/32 bytes long
  5270.  
  5271.       // recover nonce from 1st element of ciphertext
  5272.       $counterBlock array();
  5273.       $ctrTxt substr($ciphertext08);
  5274.       for ($i=0$i<8$i++$counterBlock[$iord(substr($ctrTxt,$i,1));
  5275.  
  5276.       // generate key schedule
  5277.       $keySchedule self::KeyExpansion($key);
  5278.  
  5279.       // separate ciphertext into blocks (skipping past initial 8 bytes)
  5280.       $nBlocks ceil((strlen($ciphertext)-8$blockSize);
  5281.       $ct array();
  5282.       for ($b=0$b<$nBlocks$b++$ct[$bsubstr($ciphertext8+$b*$blockSize16);
  5283.       $ciphertext $ct;  // ciphertext is now array of block-length strings
  5284.  
  5285.       // plaintext will get generated block-by-block into array of block-length strings
  5286.       $plaintxt array();
  5287.  
  5288.       for ($b=0$b<$nBlocks$b++{
  5289.         // set counter (block #) in last 8 bytes of counter block (leaving nonce in 1st 8 bytes)
  5290.         for ($c=0$c<4$c++$counterBlock[15-$cself::urs($b$c*80xff;
  5291.         for ($c=0$c<4$c++$counterBlock[15-$c-4self::urs(($b+1)/0x100000000-1$c*80xff;
  5292.  
  5293.         $cipherCntr self::Cipher($counterBlock$keySchedule);  // encrypt counter block
  5294.  
  5295.         $plaintxtByte array();
  5296.         for ($i=0$i<strlen($ciphertext[$b])$i++{
  5297.           // -- xor plaintext with ciphered counter byte-by-byte --
  5298.           $plaintxtByte[$i$cipherCntr[$iord(substr($ciphertext[$b],$i,1));
  5299.           $plaintxtByte[$ichr($plaintxtByte[$i]);
  5300.  
  5301.         }
  5302.         $plaintxt[$bimplode(''$plaintxtByte);
  5303.       }
  5304.  
  5305.       // join array of blocks into single plaintext string
  5306.       $plaintext implode('',$plaintxt);
  5307.  
  5308.       return $plaintext;
  5309.     }
  5310.  
  5311.     /**
  5312.      * AES decryption in CBC mode. This is the standard mode (the CTR methods
  5313.      * actually use Rijndael-128 in CTR mode, which - technically - isn't AES).
  5314.      *
  5315.      * Supports AES-128, AES-192 and AES-256. It supposes that the last 4 bytes
  5316.      * contained a little-endian unsigned long integer representing the unpadded
  5317.      * data length.
  5318.      *
  5319.      * @since 3.0.1
  5320.      * @author Nicholas K. Dionysopoulos
  5321.      *
  5322.      * @param string $ciphertext The data to encrypt
  5323.      * @param string $password Encryption password
  5324.      * @param int $nBits Encryption key size. Can be 128, 192 or 256
  5325.      * @return string The plaintext
  5326.      */
  5327.     public static function AESDecryptCBC($ciphertext$password$nBits 128)
  5328.     {
  5329.         if (!($nBits==128 || $nBits==192 || $nBits==256)) return false;  // standard allows 128/192/256 bit keys
  5330.         if(!function_exists('mcrypt_module_open')) return false;
  5331.  
  5332.         // Try to fetch cached key/iv or create them if they do not exist
  5333.         $lookupKey $password.'-'.$nBits;
  5334.         if(array_key_exists($lookupKeyself::$passwords))
  5335.         {
  5336.             $key    self::$passwords[$lookupKey]['key'];
  5337.             $iv        self::$passwords[$lookupKey]['iv'];
  5338.         }
  5339.         else
  5340.         {
  5341.             // use AES itself to encrypt password to get cipher key (using plain password as source for
  5342.             // key expansion) - gives us well encrypted key
  5343.             $nBytes $nBits/8;  // no bytes in key
  5344.             $pwBytes array();
  5345.             for ($i=0$i<$nBytes$i++$pwBytes[$iord(substr($password,$i,1)) 0xff;
  5346.             $key self::Cipher($pwBytesself::KeyExpansion($pwBytes));
  5347.             $key array_merge($keyarray_slice($key0$nBytes-16));  // expand key to 16/24/32 bytes long
  5348.             $newKey '';
  5349.             foreach($key as $int$newKey .= chr($int)}
  5350.             $key $newKey;
  5351.  
  5352.             // Create an Initialization Vector (IV) based on the password, using the same technique as for the key
  5353.             $nBytes 16;  // AES uses a 128 -bit (16 byte) block size, hence the IV size is always 16 bytes
  5354.             $pwBytes array();
  5355.             for ($i=0$i<$nBytes$i++$pwBytes[$iord(substr($password,$i,1)) 0xff;
  5356.             $iv self::Cipher($pwBytesself::KeyExpansion($pwBytes));
  5357.             $newIV '';
  5358.             foreach($iv as $int$newIV .= chr($int)}
  5359.             $iv $newIV;
  5360.  
  5361.             self::$passwords[$lookupKey]['key'$key;
  5362.             self::$passwords[$lookupKey]['iv'$iv;
  5363.         }
  5364.  
  5365.         // Read the data size
  5366.         $data_size unpack('V'substr($ciphertext,-4) );
  5367.  
  5368.         // Decrypt
  5369.         $td mcrypt_module_open(MCRYPT_RIJNDAEL_128''MCRYPT_MODE_CBC'');
  5370.         mcrypt_generic_init($td$key$iv);
  5371.         $plaintext mdecrypt_generic($tdsubstr($ciphertext,0,-4));
  5372.         mcrypt_generic_deinit($td);
  5373.  
  5374.         // Trim padding, if necessary
  5375.         if(strlen($plaintext$data_size)
  5376.         {
  5377.             $plaintext substr($plaintext0$data_size);
  5378.         }
  5379.  
  5380.         return $plaintext;
  5381.     }
  5382. }
  5383.  
  5384. /**
  5385.  * The Master Setup will read the configuration parameters from restoration.php, abiautomation.ini, or
  5386.  * the JSON-encoded "configuration" input variable and return the status.
  5387.  * @return bool True if the master configuration was applied to the Factory object
  5388.  */
  5389. function masterSetup()
  5390. {
  5391.     // ------------------------------------------------------------
  5392.     // 1. Import basic setup parameters
  5393.     // ------------------------------------------------------------
  5394.  
  5395.     $ini_data null;
  5396.  
  5397.     // In restore.php mode, require restoration.php or fail
  5398.     if(!defined('KICKSTART'))
  5399.     {
  5400.         // This is the standalone mode, used by Akeeba Backup Professional. It looks for a restoration.php
  5401.         // file to perform its magic. If the file is not there, we will abort.
  5402.         $setupFile 'restoration.php';
  5403.  
  5404.         if!file_exists($setupFile) )
  5405.         {
  5406.             // Uh oh... Somebody tried to pooh on our back yard. Lock the gates! Don't let the traitor inside!
  5407.             AKFactory::set('kickstart.enabled'false);
  5408.             return false;
  5409.         }
  5410.  
  5411.         // Load restoration.php. It creates a global variable named $restoration_setup
  5412.         require_once $setupFile;
  5413.         $ini_data $restoration_setup;
  5414.         if(empty($ini_data))
  5415.         {
  5416.             // No parameters fetched. Darn, how am I supposed to work like that?!
  5417.             AKFactory::set('kickstart.enabled'false);
  5418.             return false;
  5419.         }
  5420.  
  5421.         AKFactory::set('kickstart.enabled'true);
  5422.     }
  5423.     else
  5424.     {
  5425.         // Maybe we have $restoration_setup defined in the head of kickstart.php
  5426.         global $restoration_setup;
  5427.         if(!empty($restoration_setup&& !is_array($restoration_setup)) {
  5428.             $ini_data AKText::parse_ini_file($restoration_setupfalsetrue);
  5429.         elseif(is_array($restoration_setup)) {
  5430.             $ini_data $restoration_setup;
  5431.         }
  5432.     }
  5433.  
  5434.     // Import any data from $restoration_setup
  5435.     if(!empty($ini_data))
  5436.     {
  5437.         foreach($ini_data as $key => $value)
  5438.         {
  5439.             AKFactory::set($key$value);
  5440.         }
  5441.         AKFactory::set('kickstart.enabled'true);
  5442.     }
  5443.  
  5444.     // Reinitialize $ini_data
  5445.     $ini_data null;
  5446.  
  5447.     // ------------------------------------------------------------
  5448.     // 2. Explode JSON parameters into $_REQUEST scope
  5449.     // ------------------------------------------------------------
  5450.  
  5451.     // Detect a JSON string in the request variable and store it.
  5452.     $json getQueryParam('json'null);
  5453.  
  5454.     // Remove everything from the request array
  5455.     if(!empty($_REQUEST))
  5456.     {
  5457.         foreach($_REQUEST as $key => $value)
  5458.         {
  5459.             unset($_REQUEST[$key]);
  5460.         }
  5461.     }
  5462.     // Decrypt a possibly encrypted JSON string
  5463.     if(!empty($json))
  5464.     {
  5465.         $password AKFactory::get('kickstart.security.password'null);
  5466.         if(!empty($password))
  5467.         {
  5468.             $json AKEncryptionAES::AESDecryptCtr($json$password128);
  5469.         }
  5470.  
  5471.         // Get the raw data
  5472.         $raw json_decode$jsontrue );
  5473.         // Pass all JSON data to the request array
  5474.         if(!empty($raw))
  5475.         {
  5476.             foreach($raw as $key => $value)
  5477.             {
  5478.                 $_REQUEST[$key$value;
  5479.             }
  5480.         }
  5481.     }
  5482.  
  5483.     // ------------------------------------------------------------
  5484.     // 3. Try the "factory" variable
  5485.     // ------------------------------------------------------------
  5486.     // A "factory" variable will override all other settings.
  5487.     $serialized getQueryParam('factory'null);
  5488.     if!is_null($serialized) )
  5489.     {
  5490.         // Get the serialized factory
  5491.         AKFactory::unserialize($serialized);
  5492.         AKFactory::set('kickstart.enabled'true);
  5493.         return true;
  5494.     }
  5495.  
  5496.     // ------------------------------------------------------------
  5497.     // 4. Try abiautomation.ini and the configuration variable for Kickstart
  5498.     // ------------------------------------------------------------
  5499.     if(defined('KICKSTART'))
  5500.     {
  5501.         // We are in Kickstart mode. abiautomation.ini has precedence.
  5502.         $setupFile 'abiautomation.ini';
  5503.         iffile_exists($setupFile) )
  5504.         {
  5505.             // abiautomation.ini was found
  5506.             $ini_data AKText::parse_ini_file('restoration.ini'false);
  5507.         }
  5508.         else
  5509.         {
  5510.             // abiautomation.ini was not found. Let's try input parameters.
  5511.             $configuration getQueryParam('configuration');
  5512.             if!is_null($configuration) )
  5513.             {
  5514.                 // Let's decode the configuration from JSON to array
  5515.                 $ini_data json_decode($configurationtrue);
  5516.             }
  5517.             else
  5518.             {
  5519.                 // Neither exists. Enable Kickstart's interface anyway.
  5520.                 $ini_data array('kickstart.enabled'=>true);
  5521.             }
  5522.         }
  5523.  
  5524.         // Import any INI data we might have from other sources
  5525.         if(!empty($ini_data))
  5526.         {
  5527.             foreach($ini_data as $key => $value)
  5528.             {
  5529.                 AKFactory::set($key$value);
  5530.             }
  5531.             AKFactory::set('kickstart.enabled'true);
  5532.             return true;
  5533.         }
  5534.     }
  5535. }
  5536.  
  5537. // Mini-controller for restore.php
  5538. if(!defined('KICKSTART'))
  5539. {
  5540.     // The observer class, used to report number of files and bytes processed
  5541.     {
  5542.         public $compressedTotal = 0;
  5543.         public $uncompressedTotal = 0;
  5544.         public $filesProcessed = 0;
  5545.  
  5546.         public function update($object$message)
  5547.         {
  5548.             if(!is_object($message)) return;
  5549.  
  5550.             if!array_key_exists('type'get_object_vars($message)) ) return;
  5551.  
  5552.             if$message->type == 'startfile' )
  5553.             {
  5554.                 $this->filesProcessed++;
  5555.                 $this->compressedTotal += $message->content->compressed;
  5556.                 $this->uncompressedTotal += $message->content->uncompressed;
  5557.             }
  5558.         }
  5559.  
  5560.         public function __toString()
  5561.         {
  5562.             return __CLASS__;
  5563.         }
  5564.  
  5565.     }
  5566.  
  5567.     // Import configuration
  5568.     masterSetup();
  5569.  
  5570.     $retArray array(
  5571.         'status'    => true,
  5572.         'message'    => null
  5573.     );
  5574.  
  5575.     $enabled AKFactory::get('kickstart.enabled'false);
  5576.  
  5577.     if($enabled)
  5578.     {
  5579.         $task getQueryParam('task');
  5580.  
  5581.         switch($task)
  5582.         {
  5583.             case 'ping':
  5584.                 // ping task - realy does nothing!
  5585.                 $timer AKFactory::getTimer();
  5586.                 $timer->enforce_min_exec_time();
  5587.                 break;
  5588.  
  5589.             case 'startRestore':
  5590.                 AKFactory::nuke()// Reset the factory
  5591.  
  5592.                 // Let the control flow to the next step (the rest of the code is common!!)
  5593.  
  5594.             case 'stepRestore':
  5595.                 $engine AKFactory::getUnarchiver()// Get the engine
  5596.                 $observer new RestorationObserver()// Create a new observer
  5597.                 $engine->attach($observer)// Attach the observer
  5598.                 $engine->tick();
  5599.                 $ret $engine->getStatusArray();
  5600.  
  5601.                 if$ret['Error'!= '' )
  5602.                 {
  5603.                     $retArray['status'false;
  5604.                     $retArray['done'true;
  5605.                     $retArray['message'$ret['Error'];
  5606.                 }
  5607.                 elseif!$ret['HasRun')
  5608.                 {
  5609.                     $retArray['files'$observer->filesProcessed;
  5610.                     $retArray['bytesIn'$observer->compressedTotal;
  5611.                     $retArray['bytesOut'$observer->uncompressedTotal;
  5612.                     $retArray['status'true;
  5613.                     $retArray['done'true;
  5614.                 }
  5615.                 else
  5616.                 {
  5617.                     $retArray['files'$observer->filesProcessed;
  5618.                     $retArray['bytesIn'$observer->compressedTotal;
  5619.                     $retArray['bytesOut'$observer->uncompressedTotal;
  5620.                     $retArray['status'true;
  5621.                     $retArray['done'false;
  5622.                     $retArray['factory'AKFactory::serialize();
  5623.                 }
  5624.                 break;
  5625.  
  5626.             case 'finalizeRestore':
  5627.                 $root AKFactory::get('kickstart.setup.destdir');
  5628.                 // Remove the installation directory
  5629.                 recursive_remove_directory$root.'/installation' );
  5630.  
  5631.                 $postproc AKFactory::getPostProc();
  5632.  
  5633.                 // Rename htaccess.bak to .htaccess
  5634.                 if(file_exists($root.'/htaccess.bak'))
  5635.                 {
  5636.                     iffile_exists($root.'/.htaccess')  )
  5637.                     {
  5638.                         $postproc->unlink($root.'/.htaccess');
  5639.                     }
  5640.                     $postproc->rename$root.'/htaccess.bak'$root.'/.htaccess' );
  5641.                 }
  5642.  
  5643.                 // Remove restoration.php
  5644.                 $basepath dirname(__FILE__);
  5645.                 $basepath rtrimstr_replace('\\','/',$basepath)'/' );
  5646.                 if(!empty($basepath)) $basepath .= '/';
  5647.                 $postproc->unlink$basepath.'restoration.php' );
  5648.                 break;
  5649.  
  5650.             default:
  5651.                 // Invalid task!
  5652.                 $enabled false;
  5653.                 break;
  5654.         }
  5655.     }
  5656.  
  5657.     // Maybe we weren't authorized or the task was invalid?
  5658.     if(!$enabled)
  5659.     {
  5660.         // Maybe the user failed to enter any information
  5661.         $retArray['status'false;
  5662.         $retArray['message'AKText::_('ERR_INVALID_LOGIN');
  5663.     }
  5664.  
  5665.     // JSON encode the message
  5666.     $json json_encode($retArray);
  5667.     // Do I have to encrypt?
  5668.     $password AKFactory::get('kickstart.security.password'null);
  5669.     if(!empty($password))
  5670.     {
  5671.         $json AKEncryptionAES::AESEncryptCtr($json$password128);
  5672.     }
  5673.  
  5674.     // Return the message
  5675.     echo "###$json###";
  5676.  
  5677. }
  5678.  
  5679. // ------------ lixlpixel recursive PHP functions -------------
  5680. // recursive_remove_directory( directory to delete, empty )
  5681. // expects path to directory and optional TRUE / FALSE to empty
  5682. // of course PHP has to have the rights to delete the directory
  5683. // you specify and all files and folders inside the directory
  5684. // ------------------------------------------------------------
  5685. function recursive_remove_directory($directory)
  5686. {
  5687.     // if the path has a slash at the end we remove it here
  5688.     if(substr($directory,-1== '/')
  5689.     {
  5690.         $directory substr($directory,0,-1);
  5691.     }
  5692.     // if the path is not valid or is not a directory ...
  5693.     if(!file_exists($directory|| !is_dir($directory))
  5694.     {
  5695.         // ... we return false and exit the function
  5696.         return FALSE;
  5697.     // ... if the path is not readable
  5698.     }elseif(!is_readable($directory))
  5699.     {
  5700.         // ... we return false and exit the function
  5701.         return FALSE;
  5702.     // ... else if the path is readable
  5703.     }else{
  5704.         // we open the directory
  5705.         $handle opendir($directory);
  5706.         $postproc AKFactory::getPostProc();
  5707.         // and scan through the items inside
  5708.         while (FALSE !== ($item readdir($handle)))
  5709.         {
  5710.             // if the filepointer is not the current directory
  5711.             // or the parent directory
  5712.             if($item != '.' && $item != '..')
  5713.             {
  5714.                 // we build the new path to delete
  5715.                 $path $directory.'/'.$item;
  5716.                 // if the new path is a directory
  5717.                 if(is_dir($path))
  5718.                 {
  5719.                     // we call this function with the new path
  5720.                     recursive_remove_directory($path);
  5721.                 // if the new path is a file
  5722.                 }else{
  5723.                     // we remove the file
  5724.                     $postproc->unlink($path);
  5725.                 }
  5726.             }
  5727.         }
  5728.         // close the directory
  5729.         closedir($handle);
  5730.         // try to delete the now empty directory
  5731.         if(!$postproc->rmdir($directory))
  5732.         {
  5733.             // return false if not possible
  5734.             return FALSE;
  5735.         }
  5736.         // return success
  5737.         return TRUE;
  5738.     }
  5739. }
  5740. ?>

Documentation generated on Tue, 19 Nov 2013 15:12:15 +0100 by phpDocumentor 1.4.3