PageRenderTime 119ms CodeModel.GetById 33ms app.highlight 51ms RepoModel.GetById 1ms app.codeStats 3ms

/administrator/components/com_joomlaupdate/restore.php

https://bitbucket.org/asosso/joomla25
PHP | 5744 lines | 3936 code | 669 blank | 1139 comment | 678 complexity | ec97629bbbfd771985a3bbd699df18e5 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1<?php
   2/**
   3 * Akeeba Restore
   4 * A JSON-powered JPA, JPS and ZIP archive extraction library
   5 *
   6 * @copyright 	Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
   7 * @license 	GNU GPL v2 or - at your option - any later version
   8 */
   9
  10define('_AKEEBA_RESTORATION', 1);
  11defined('DS') or define('DS', DIRECTORY_SEPARATOR);
  12
  13// Unarchiver run states
  14define('AK_STATE_NOFILE',	0); // File header not read yet
  15define('AK_STATE_HEADER',	1); // File header read; ready to process data
  16define('AK_STATE_DATA',		2); // Processing file data
  17define('AK_STATE_DATAREAD',	3); // Finished processing file data; ready to post-process
  18define('AK_STATE_POSTPROC',	4); // Post-processing
  19define('AK_STATE_DONE',		5); // Done with post-processing
  20
  21/* Windows system detection */
  22if(!defined('_AKEEBA_IS_WINDOWS'))
  23{
  24	if (function_exists('php_uname'))
  25		define('_AKEEBA_IS_WINDOWS', stristr(php_uname(), 'windows'));
  26	else
  27		define('_AKEEBA_IS_WINDOWS', DIRECTORY_SEPARATOR == '\\');
  28}
  29
  30// Make sure the locale is correct for basename() to work
  31if(function_exists('setlocale'))
  32{
  33	@setlocale(LC_ALL, 'en_US.UTF8');
  34}
  35
  36// fnmatch not available on non-POSIX systems
  37// Thanks to soywiz@php.net for this usefull alternative function [http://gr2.php.net/fnmatch]
  38if (!function_exists('fnmatch')) {
  39	function fnmatch($pattern, $string) {
  40		return @preg_match(
  41			'/^' . strtr(addcslashes($pattern, '/\\.+^$(){}=!<>|'),
  42		array('*' => '.*', '?' => '.?')) . '$/i', $string
  43		);
  44	}
  45}
  46
  47// Unicode-safe binary data length function
  48if(function_exists('mb_strlen')) {
  49	function akstringlen($string) { return mb_strlen($string,'8bit'); }
  50} else {
  51	function akstringlen($string) { return strlen($string); }
  52}
  53
  54/**
  55 * Gets a query parameter from GET or POST data
  56 * @param $key
  57 * @param $default
  58 */
  59function getQueryParam( $key, $default = null )
  60{
  61	$value = null;
  62
  63	if(array_key_exists($key, $_REQUEST)) {
  64		$value = $_REQUEST[$key];
  65	} elseif(array_key_exists($key, $_POST)) {
  66		$value = $_POST[$key];
  67	} elseif(array_key_exists($key, $_GET)) {
  68		$value = $_GET[$key];
  69	} else {
  70		return $default;
  71	}
  72
  73	if(get_magic_quotes_gpc() && !is_null($value)) $value=stripslashes($value);
  74
  75	return $value;
  76}
  77
  78/**
  79 * Akeeba Backup's JSON compatibility layer
  80 *
  81 * On systems where json_encode and json_decode are not available, Akeeba
  82 * Backup will attempt to use PEAR's Services_JSON library to emulate them.
  83 * A copy of this library is included in this file and will be used if and
  84 * only if it isn't already loaded, e.g. due to PEAR's auto-loading, or a
  85 * 3PD extension loading it for its own purposes.
  86 */
  87
  88/**
  89 * Converts to and from JSON format.
  90 *
  91 * JSON (JavaScript Object Notation) is a lightweight data-interchange
  92 * format. It is easy for humans to read and write. It is easy for machines
  93 * to parse and generate. It is based on a subset of the JavaScript
  94 * Programming Language, Standard ECMA-262 3rd Edition - December 1999.
  95 * This feature can also be found in  Python. JSON is a text format that is
  96 * completely language independent but uses conventions that are familiar
  97 * to programmers of the C-family of languages, including C, C++, C#, Java,
  98 * JavaScript, Perl, TCL, and many others. These properties make JSON an
  99 * ideal data-interchange language.
 100 *
 101 * This package provides a simple encoder and decoder for JSON notation. It
 102 * is intended for use with client-side Javascript applications that make
 103 * use of HTTPRequest to perform server communication functions - data can
 104 * be encoded into JSON notation for use in a client-side javascript, or
 105 * decoded from incoming Javascript requests. JSON format is native to
 106 * Javascript, and can be directly eval()'ed with no further parsing
 107 * overhead
 108 *
 109 * All strings should be in ASCII or UTF-8 format!
 110 *
 111 * LICENSE: Redistribution and use in source and binary forms, with or
 112 * without modification, are permitted provided that the following
 113 * conditions are met: Redistributions of source code must retain the
 114 * above copyright notice, this list of conditions and the following
 115 * disclaimer. Redistributions in binary form must reproduce the above
 116 * copyright notice, this list of conditions and the following disclaimer
 117 * in the documentation and/or other materials provided with the
 118 * distribution.
 119 *
 120 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
 121 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 122 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
 123 * NO EVENT SHALL CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 124 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 125 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
 126 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 127 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
 128 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
 129 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
 130 * DAMAGE.
 131 *
 132 * @category
 133 * @package     Services_JSON
 134 * @author      Michal Migurski <mike-json@teczno.com>
 135 * @author      Matt Knapp <mdknapp[at]gmail[dot]com>
 136 * @author      Brett Stimmerman <brettstimmerman[at]gmail[dot]com>
 137 * @copyright   2005 Michal Migurski
 138 * @version     CVS: $Id: restore.php 612 2011-05-19 08:26:26Z nikosdion $
 139 * @license     http://www.opensource.org/licenses/bsd-license.php
 140 * @link        http://pear.php.net/pepr/pepr-proposal-show.php?id=198
 141 */
 142
 143if(!defined('JSON_FORCE_OBJECT'))
 144{
 145	define('JSON_FORCE_OBJECT', 1);
 146}
 147
 148if(!defined('SERVICES_JSON_SLICE'))
 149{
 150	/**
 151	 * Marker constant for Services_JSON::decode(), used to flag stack state
 152	 */
 153	define('SERVICES_JSON_SLICE',   1);
 154
 155	/**
 156	 * Marker constant for Services_JSON::decode(), used to flag stack state
 157	 */
 158	define('SERVICES_JSON_IN_STR',  2);
 159
 160	/**
 161	 * Marker constant for Services_JSON::decode(), used to flag stack state
 162	 */
 163	define('SERVICES_JSON_IN_ARR',  3);
 164
 165	/**
 166	 * Marker constant for Services_JSON::decode(), used to flag stack state
 167	 */
 168	define('SERVICES_JSON_IN_OBJ',  4);
 169
 170	/**
 171	 * Marker constant for Services_JSON::decode(), used to flag stack state
 172	 */
 173	define('SERVICES_JSON_IN_CMT', 5);
 174
 175	/**
 176	 * Behavior switch for Services_JSON::decode()
 177	 */
 178	define('SERVICES_JSON_LOOSE_TYPE', 16);
 179
 180	/**
 181	 * Behavior switch for Services_JSON::decode()
 182	 */
 183	define('SERVICES_JSON_SUPPRESS_ERRORS', 32);
 184}
 185
 186/**
 187 * Converts to and from JSON format.
 188 *
 189 * Brief example of use:
 190 *
 191 * <code>
 192 * // create a new instance of Services_JSON
 193 * $json = new Services_JSON();
 194 *
 195 * // convert a complexe value to JSON notation, and send it to the browser
 196 * $value = array('foo', 'bar', array(1, 2, 'baz'), array(3, array(4)));
 197 * $output = $json->encode($value);
 198 *
 199 * print($output);
 200 * // prints: ["foo","bar",[1,2,"baz"],[3,[4]]]
 201 *
 202 * // accept incoming POST data, assumed to be in JSON notation
 203 * $input = file_get_contents('php://input', 1000000);
 204 * $value = $json->decode($input);
 205 * </code>
 206 */
 207if(!class_exists('Akeeba_Services_JSON'))
 208{
 209	class Akeeba_Services_JSON
 210	{
 211	   /**
 212	    * constructs a new JSON instance
 213	    *
 214	    * @param    int     $use    object behavior flags; combine with boolean-OR
 215	    *
 216	    *                           possible values:
 217	    *                           - SERVICES_JSON_LOOSE_TYPE:  loose typing.
 218	    *                                   "{...}" syntax creates associative arrays
 219	    *                                   instead of objects in decode().
 220	    *                           - SERVICES_JSON_SUPPRESS_ERRORS:  error suppression.
 221	    *                                   Values which can't be encoded (e.g. resources)
 222	    *                                   appear as NULL instead of throwing errors.
 223	    *                                   By default, a deeply-nested resource will
 224	    *                                   bubble up with an error, so all return values
 225	    *                                   from encode() should be checked with isError()
 226	    */
 227	    function Akeeba_Services_JSON($use = 0)
 228	    {
 229	        $this->use = $use;
 230	    }
 231
 232	   /**
 233	    * convert a string from one UTF-16 char to one UTF-8 char
 234	    *
 235	    * Normally should be handled by mb_convert_encoding, but
 236	    * provides a slower PHP-only method for installations
 237	    * that lack the multibye string extension.
 238	    *
 239	    * @param    string  $utf16  UTF-16 character
 240	    * @return   string  UTF-8 character
 241	    * @access   private
 242	    */
 243	    function utf162utf8($utf16)
 244	    {
 245	        // oh please oh please oh please oh please oh please
 246	        if(function_exists('mb_convert_encoding')) {
 247	            return mb_convert_encoding($utf16, 'UTF-8', 'UTF-16');
 248	        }
 249
 250	        $bytes = (ord($utf16{0}) << 8) | ord($utf16{1});
 251
 252	        switch(true) {
 253	            case ((0x7F & $bytes) == $bytes):
 254	                // this case should never be reached, because we are in ASCII range
 255	                // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
 256	                return chr(0x7F & $bytes);
 257
 258	            case (0x07FF & $bytes) == $bytes:
 259	                // return a 2-byte UTF-8 character
 260	                // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
 261	                return chr(0xC0 | (($bytes >> 6) & 0x1F))
 262	                     . chr(0x80 | ($bytes & 0x3F));
 263
 264	            case (0xFFFF & $bytes) == $bytes:
 265	                // return a 3-byte UTF-8 character
 266	                // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
 267	                return chr(0xE0 | (($bytes >> 12) & 0x0F))
 268	                     . chr(0x80 | (($bytes >> 6) & 0x3F))
 269	                     . chr(0x80 | ($bytes & 0x3F));
 270	        }
 271
 272	        // ignoring UTF-32 for now, sorry
 273	        return '';
 274	    }
 275
 276	   /**
 277	    * convert a string from one UTF-8 char to one UTF-16 char
 278	    *
 279	    * Normally should be handled by mb_convert_encoding, but
 280	    * provides a slower PHP-only method for installations
 281	    * that lack the multibye string extension.
 282	    *
 283	    * @param    string  $utf8   UTF-8 character
 284	    * @return   string  UTF-16 character
 285	    * @access   private
 286	    */
 287	    function utf82utf16($utf8)
 288	    {
 289	        // oh please oh please oh please oh please oh please
 290	        if(function_exists('mb_convert_encoding')) {
 291	            return mb_convert_encoding($utf8, 'UTF-16', 'UTF-8');
 292	        }
 293
 294	        switch(strlen($utf8)) {
 295	            case 1:
 296	                // this case should never be reached, because we are in ASCII range
 297	                // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
 298	                return $utf8;
 299
 300	            case 2:
 301	                // return a UTF-16 character from a 2-byte UTF-8 char
 302	                // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
 303	                return chr(0x07 & (ord($utf8{0}) >> 2))
 304	                     . chr((0xC0 & (ord($utf8{0}) << 6))
 305	                         | (0x3F & ord($utf8{1})));
 306
 307	            case 3:
 308	                // return a UTF-16 character from a 3-byte UTF-8 char
 309	                // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
 310	                return chr((0xF0 & (ord($utf8{0}) << 4))
 311	                         | (0x0F & (ord($utf8{1}) >> 2)))
 312	                     . chr((0xC0 & (ord($utf8{1}) << 6))
 313	                         | (0x7F & ord($utf8{2})));
 314	        }
 315
 316	        // ignoring UTF-32 for now, sorry
 317	        return '';
 318	    }
 319
 320	   /**
 321	    * encodes an arbitrary variable into JSON format
 322	    *
 323	    * @param    mixed   $var    any number, boolean, string, array, or object to be encoded.
 324	    *                           see argument 1 to Services_JSON() above for array-parsing behavior.
 325	    *                           if var is a strng, note that encode() always expects it
 326	    *                           to be in ASCII or UTF-8 format!
 327	    *
 328	    * @return   mixed   JSON string representation of input var or an error if a problem occurs
 329	    * @access   public
 330	    */
 331	    function encode($var)
 332	    {
 333	        switch (gettype($var)) {
 334	            case 'boolean':
 335	                return $var ? 'true' : 'false';
 336
 337	            case 'NULL':
 338	                return 'null';
 339
 340	            case 'integer':
 341	                return (int) $var;
 342
 343	            case 'double':
 344	            case 'float':
 345	                return (float) $var;
 346
 347	            case 'string':
 348	                // STRINGS ARE EXPECTED TO BE IN ASCII OR UTF-8 FORMAT
 349	                $ascii = '';
 350	                $strlen_var = strlen($var);
 351
 352	               /*
 353	                * Iterate over every character in the string,
 354	                * escaping with a slash or encoding to UTF-8 where necessary
 355	                */
 356	                for ($c = 0; $c < $strlen_var; ++$c) {
 357
 358	                    $ord_var_c = ord($var{$c});
 359
 360	                    switch (true) {
 361	                        case $ord_var_c == 0x08:
 362	                            $ascii .= '\b';
 363	                            break;
 364	                        case $ord_var_c == 0x09:
 365	                            $ascii .= '\t';
 366	                            break;
 367	                        case $ord_var_c == 0x0A:
 368	                            $ascii .= '\n';
 369	                            break;
 370	                        case $ord_var_c == 0x0C:
 371	                            $ascii .= '\f';
 372	                            break;
 373	                        case $ord_var_c == 0x0D:
 374	                            $ascii .= '\r';
 375	                            break;
 376
 377	                        case $ord_var_c == 0x22:
 378	                        case $ord_var_c == 0x2F:
 379	                        case $ord_var_c == 0x5C:
 380	                            // double quote, slash, slosh
 381	                            $ascii .= '\\'.$var{$c};
 382	                            break;
 383
 384	                        case (($ord_var_c >= 0x20) && ($ord_var_c <= 0x7F)):
 385	                            // characters U-00000000 - U-0000007F (same as ASCII)
 386	                            $ascii .= $var{$c};
 387	                            break;
 388
 389	                        case (($ord_var_c & 0xE0) == 0xC0):
 390	                            // characters U-00000080 - U-000007FF, mask 110XXXXX
 391	                            // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
 392	                            $char = pack('C*', $ord_var_c, ord($var{$c + 1}));
 393	                            $c += 1;
 394	                            $utf16 = $this->utf82utf16($char);
 395	                            $ascii .= sprintf('\u%04s', bin2hex($utf16));
 396	                            break;
 397
 398	                        case (($ord_var_c & 0xF0) == 0xE0):
 399	                            // characters U-00000800 - U-0000FFFF, mask 1110XXXX
 400	                            // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
 401	                            $char = pack('C*', $ord_var_c,
 402	                                         ord($var{$c + 1}),
 403	                                         ord($var{$c + 2}));
 404	                            $c += 2;
 405	                            $utf16 = $this->utf82utf16($char);
 406	                            $ascii .= sprintf('\u%04s', bin2hex($utf16));
 407	                            break;
 408
 409	                        case (($ord_var_c & 0xF8) == 0xF0):
 410	                            // characters U-00010000 - U-001FFFFF, mask 11110XXX
 411	                            // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
 412	                            $char = pack('C*', $ord_var_c,
 413	                                         ord($var{$c + 1}),
 414	                                         ord($var{$c + 2}),
 415	                                         ord($var{$c + 3}));
 416	                            $c += 3;
 417	                            $utf16 = $this->utf82utf16($char);
 418	                            $ascii .= sprintf('\u%04s', bin2hex($utf16));
 419	                            break;
 420
 421	                        case (($ord_var_c & 0xFC) == 0xF8):
 422	                            // characters U-00200000 - U-03FFFFFF, mask 111110XX
 423	                            // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
 424	                            $char = pack('C*', $ord_var_c,
 425	                                         ord($var{$c + 1}),
 426	                                         ord($var{$c + 2}),
 427	                                         ord($var{$c + 3}),
 428	                                         ord($var{$c + 4}));
 429	                            $c += 4;
 430	                            $utf16 = $this->utf82utf16($char);
 431	                            $ascii .= sprintf('\u%04s', bin2hex($utf16));
 432	                            break;
 433
 434	                        case (($ord_var_c & 0xFE) == 0xFC):
 435	                            // characters U-04000000 - U-7FFFFFFF, mask 1111110X
 436	                            // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
 437	                            $char = pack('C*', $ord_var_c,
 438	                                         ord($var{$c + 1}),
 439	                                         ord($var{$c + 2}),
 440	                                         ord($var{$c + 3}),
 441	                                         ord($var{$c + 4}),
 442	                                         ord($var{$c + 5}));
 443	                            $c += 5;
 444	                            $utf16 = $this->utf82utf16($char);
 445	                            $ascii .= sprintf('\u%04s', bin2hex($utf16));
 446	                            break;
 447	                    }
 448	                }
 449
 450	                return '"'.$ascii.'"';
 451
 452	            case 'array':
 453	               /*
 454	                * As per JSON spec if any array key is not an integer
 455	                * we must treat the the whole array as an object. We
 456	                * also try to catch a sparsely populated associative
 457	                * array with numeric keys here because some JS engines
 458	                * will create an array with empty indexes up to
 459	                * max_index which can cause memory issues and because
 460	                * the keys, which may be relevant, will be remapped
 461	                * otherwise.
 462	                *
 463	                * As per the ECMA and JSON specification an object may
 464	                * have any string as a property. Unfortunately due to
 465	                * a hole in the ECMA specification if the key is a
 466	                * ECMA reserved word or starts with a digit the
 467	                * parameter is only accessible using ECMAScript's
 468	                * bracket notation.
 469	                */
 470
 471	                // treat as a JSON object
 472	                if (is_array($var) && count($var) && (array_keys($var) !== range(0, sizeof($var) - 1))) {
 473	                    $properties = array_map(array($this, 'name_value'),
 474	                                            array_keys($var),
 475	                                            array_values($var));
 476
 477	                    foreach($properties as $property) {
 478	                        if(Akeeba_Services_JSON::isError($property)) {
 479	                            return $property;
 480	                        }
 481	                    }
 482
 483	                    return '{' . join(',', $properties) . '}';
 484	                }
 485
 486	                // treat it like a regular array
 487	                $elements = array_map(array($this, 'encode'), $var);
 488
 489	                foreach($elements as $element) {
 490	                    if(Akeeba_Services_JSON::isError($element)) {
 491	                        return $element;
 492	                    }
 493	                }
 494
 495	                return '[' . join(',', $elements) . ']';
 496
 497	            case 'object':
 498	                $vars = get_object_vars($var);
 499
 500	                $properties = array_map(array($this, 'name_value'),
 501	                                        array_keys($vars),
 502	                                        array_values($vars));
 503
 504	                foreach($properties as $property) {
 505	                    if(Akeeba_Services_JSON::isError($property)) {
 506	                        return $property;
 507	                    }
 508	                }
 509
 510	                return '{' . join(',', $properties) . '}';
 511
 512	            default:
 513	                return ($this->use & SERVICES_JSON_SUPPRESS_ERRORS)
 514	                    ? 'null'
 515	                    : new Akeeba_Services_JSON_Error(gettype($var)." can not be encoded as JSON string");
 516	        }
 517	    }
 518
 519	   /**
 520	    * array-walking function for use in generating JSON-formatted name-value pairs
 521	    *
 522	    * @param    string  $name   name of key to use
 523	    * @param    mixed   $value  reference to an array element to be encoded
 524	    *
 525	    * @return   string  JSON-formatted name-value pair, like '"name":value'
 526	    * @access   private
 527	    */
 528	    function name_value($name, $value)
 529	    {
 530	        $encoded_value = $this->encode($value);
 531
 532	        if(Akeeba_Services_JSON::isError($encoded_value)) {
 533	            return $encoded_value;
 534	        }
 535
 536	        return $this->encode(strval($name)) . ':' . $encoded_value;
 537	    }
 538
 539	   /**
 540	    * reduce a string by removing leading and trailing comments and whitespace
 541	    *
 542	    * @param    $str    string      string value to strip of comments and whitespace
 543	    *
 544	    * @return   string  string value stripped of comments and whitespace
 545	    * @access   private
 546	    */
 547	    function reduce_string($str)
 548	    {
 549	        $str = preg_replace(array(
 550
 551	                // eliminate single line comments in '// ...' form
 552	                '#^\s*//(.+)$#m',
 553
 554	                // eliminate multi-line comments in '/* ... */' form, at start of string
 555	                '#^\s*/\*(.+)\*/#Us',
 556
 557	                // eliminate multi-line comments in '/* ... */' form, at end of string
 558	                '#/\*(.+)\*/\s*$#Us'
 559
 560	            ), '', $str);
 561
 562	        // eliminate extraneous space
 563	        return trim($str);
 564	    }
 565
 566	   /**
 567	    * decodes a JSON string into appropriate variable
 568	    *
 569	    * @param    string  $str    JSON-formatted string
 570	    *
 571	    * @return   mixed   number, boolean, string, array, or object
 572	    *                   corresponding to given JSON input string.
 573	    *                   See argument 1 to Akeeba_Services_JSON() above for object-output behavior.
 574	    *                   Note that decode() always returns strings
 575	    *                   in ASCII or UTF-8 format!
 576	    * @access   public
 577	    */
 578	    function decode($str)
 579	    {
 580	        $str = $this->reduce_string($str);
 581
 582	        switch (strtolower($str)) {
 583	            case 'true':
 584	                return true;
 585
 586	            case 'false':
 587	                return false;
 588
 589	            case 'null':
 590	                return null;
 591
 592	            default:
 593	                $m = array();
 594
 595	                if (is_numeric($str)) {
 596	                    // Lookie-loo, it's a number
 597
 598	                    // This would work on its own, but I'm trying to be
 599	                    // good about returning integers where appropriate:
 600	                    // return (float)$str;
 601
 602	                    // Return float or int, as appropriate
 603	                    return ((float)$str == (integer)$str)
 604	                        ? (integer)$str
 605	                        : (float)$str;
 606
 607	                } elseif (preg_match('/^("|\').*(\1)$/s', $str, $m) && $m[1] == $m[2]) {
 608	                    // STRINGS RETURNED IN UTF-8 FORMAT
 609	                    $delim = substr($str, 0, 1);
 610	                    $chrs = substr($str, 1, -1);
 611	                    $utf8 = '';
 612	                    $strlen_chrs = strlen($chrs);
 613
 614	                    for ($c = 0; $c < $strlen_chrs; ++$c) {
 615
 616	                        $substr_chrs_c_2 = substr($chrs, $c, 2);
 617	                        $ord_chrs_c = ord($chrs{$c});
 618
 619	                        switch (true) {
 620	                            case $substr_chrs_c_2 == '\b':
 621	                                $utf8 .= chr(0x08);
 622	                                ++$c;
 623	                                break;
 624	                            case $substr_chrs_c_2 == '\t':
 625	                                $utf8 .= chr(0x09);
 626	                                ++$c;
 627	                                break;
 628	                            case $substr_chrs_c_2 == '\n':
 629	                                $utf8 .= chr(0x0A);
 630	                                ++$c;
 631	                                break;
 632	                            case $substr_chrs_c_2 == '\f':
 633	                                $utf8 .= chr(0x0C);
 634	                                ++$c;
 635	                                break;
 636	                            case $substr_chrs_c_2 == '\r':
 637	                                $utf8 .= chr(0x0D);
 638	                                ++$c;
 639	                                break;
 640
 641	                            case $substr_chrs_c_2 == '\\"':
 642	                            case $substr_chrs_c_2 == '\\\'':
 643	                            case $substr_chrs_c_2 == '\\\\':
 644	                            case $substr_chrs_c_2 == '\\/':
 645	                                if (($delim == '"' && $substr_chrs_c_2 != '\\\'') ||
 646	                                   ($delim == "'" && $substr_chrs_c_2 != '\\"')) {
 647	                                    $utf8 .= $chrs{++$c};
 648	                                }
 649	                                break;
 650
 651	                            case preg_match('/\\\u[0-9A-F]{4}/i', substr($chrs, $c, 6)):
 652	                                // single, escaped unicode character
 653	                                $utf16 = chr(hexdec(substr($chrs, ($c + 2), 2)))
 654	                                       . chr(hexdec(substr($chrs, ($c + 4), 2)));
 655	                                $utf8 .= $this->utf162utf8($utf16);
 656	                                $c += 5;
 657	                                break;
 658
 659	                            case ($ord_chrs_c >= 0x20) && ($ord_chrs_c <= 0x7F):
 660	                                $utf8 .= $chrs{$c};
 661	                                break;
 662
 663	                            case ($ord_chrs_c & 0xE0) == 0xC0:
 664	                                // characters U-00000080 - U-000007FF, mask 110XXXXX
 665	                                //see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
 666	                                $utf8 .= substr($chrs, $c, 2);
 667	                                ++$c;
 668	                                break;
 669
 670	                            case ($ord_chrs_c & 0xF0) == 0xE0:
 671	                                // characters U-00000800 - U-0000FFFF, mask 1110XXXX
 672	                                // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
 673	                                $utf8 .= substr($chrs, $c, 3);
 674	                                $c += 2;
 675	                                break;
 676
 677	                            case ($ord_chrs_c & 0xF8) == 0xF0:
 678	                                // characters U-00010000 - U-001FFFFF, mask 11110XXX
 679	                                // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
 680	                                $utf8 .= substr($chrs, $c, 4);
 681	                                $c += 3;
 682	                                break;
 683
 684	                            case ($ord_chrs_c & 0xFC) == 0xF8:
 685	                                // characters U-00200000 - U-03FFFFFF, mask 111110XX
 686	                                // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
 687	                                $utf8 .= substr($chrs, $c, 5);
 688	                                $c += 4;
 689	                                break;
 690
 691	                            case ($ord_chrs_c & 0xFE) == 0xFC:
 692	                                // characters U-04000000 - U-7FFFFFFF, mask 1111110X
 693	                                // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
 694	                                $utf8 .= substr($chrs, $c, 6);
 695	                                $c += 5;
 696	                                break;
 697
 698	                        }
 699
 700	                    }
 701
 702	                    return $utf8;
 703
 704	                } elseif (preg_match('/^\[.*\]$/s', $str) || preg_match('/^\{.*\}$/s', $str)) {
 705	                    // array, or object notation
 706
 707	                    if ($str{0} == '[') {
 708	                        $stk = array(SERVICES_JSON_IN_ARR);
 709	                        $arr = array();
 710	                    } else {
 711	                        if ($this->use & SERVICES_JSON_LOOSE_TYPE) {
 712	                            $stk = array(SERVICES_JSON_IN_OBJ);
 713	                            $obj = array();
 714	                        } else {
 715	                            $stk = array(SERVICES_JSON_IN_OBJ);
 716	                            $obj = new stdClass();
 717	                        }
 718	                    }
 719
 720	                    array_push($stk, array('what'  => SERVICES_JSON_SLICE,
 721	                                           'where' => 0,
 722	                                           'delim' => false));
 723
 724	                    $chrs = substr($str, 1, -1);
 725	                    $chrs = $this->reduce_string($chrs);
 726
 727	                    if ($chrs == '') {
 728	                        if (reset($stk) == SERVICES_JSON_IN_ARR) {
 729	                            return $arr;
 730
 731	                        } else {
 732	                            return $obj;
 733
 734	                        }
 735	                    }
 736
 737	                    //print("\nparsing {$chrs}\n");
 738
 739	                    $strlen_chrs = strlen($chrs);
 740
 741	                    for ($c = 0; $c <= $strlen_chrs; ++$c) {
 742
 743	                        $top = end($stk);
 744	                        $substr_chrs_c_2 = substr($chrs, $c, 2);
 745
 746	                        if (($c == $strlen_chrs) || (($chrs{$c} == ',') && ($top['what'] == SERVICES_JSON_SLICE))) {
 747	                            // found a comma that is not inside a string, array, etc.,
 748	                            // OR we've reached the end of the character list
 749	                            $slice = substr($chrs, $top['where'], ($c - $top['where']));
 750	                            array_push($stk, array('what' => SERVICES_JSON_SLICE, 'where' => ($c + 1), 'delim' => false));
 751	                            //print("Found split at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
 752
 753	                            if (reset($stk) == SERVICES_JSON_IN_ARR) {
 754	                                // we are in an array, so just push an element onto the stack
 755	                                array_push($arr, $this->decode($slice));
 756
 757	                            } elseif (reset($stk) == SERVICES_JSON_IN_OBJ) {
 758	                                // we are in an object, so figure
 759	                                // out the property name and set an
 760	                                // element in an associative array,
 761	                                // for now
 762	                                $parts = array();
 763
 764	                                if (preg_match('/^\s*(["\'].*[^\\\]["\'])\s*:\s*(\S.*),?$/Uis', $slice, $parts)) {
 765	                                    // "name":value pair
 766	                                    $key = $this->decode($parts[1]);
 767	                                    $val = $this->decode($parts[2]);
 768
 769	                                    if ($this->use & SERVICES_JSON_LOOSE_TYPE) {
 770	                                        $obj[$key] = $val;
 771	                                    } else {
 772	                                        $obj->$key = $val;
 773	                                    }
 774	                                } elseif (preg_match('/^\s*(\w+)\s*:\s*(\S.*),?$/Uis', $slice, $parts)) {
 775	                                    // name:value pair, where name is unquoted
 776	                                    $key = $parts[1];
 777	                                    $val = $this->decode($parts[2]);
 778
 779	                                    if ($this->use & SERVICES_JSON_LOOSE_TYPE) {
 780	                                        $obj[$key] = $val;
 781	                                    } else {
 782	                                        $obj->$key = $val;
 783	                                    }
 784	                                }
 785
 786	                            }
 787
 788	                        } elseif ((($chrs{$c} == '"') || ($chrs{$c} == "'")) && ($top['what'] != SERVICES_JSON_IN_STR)) {
 789	                            // found a quote, and we are not inside a string
 790	                            array_push($stk, array('what' => SERVICES_JSON_IN_STR, 'where' => $c, 'delim' => $chrs{$c}));
 791	                            //print("Found start of string at {$c}\n");
 792
 793	                        } elseif (($chrs{$c} == $top['delim']) &&
 794	                                 ($top['what'] == SERVICES_JSON_IN_STR) &&
 795	                                 ((strlen(substr($chrs, 0, $c)) - strlen(rtrim(substr($chrs, 0, $c), '\\'))) % 2 != 1)) {
 796	                            // found a quote, we're in a string, and it's not escaped
 797	                            // we know that it's not escaped becase there is _not_ an
 798	                            // odd number of backslashes at the end of the string so far
 799	                            array_pop($stk);
 800	                            //print("Found end of string at {$c}: ".substr($chrs, $top['where'], (1 + 1 + $c - $top['where']))."\n");
 801
 802	                        } elseif (($chrs{$c} == '[') &&
 803	                                 in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) {
 804	                            // found a left-bracket, and we are in an array, object, or slice
 805	                            array_push($stk, array('what' => SERVICES_JSON_IN_ARR, 'where' => $c, 'delim' => false));
 806	                            //print("Found start of array at {$c}\n");
 807
 808	                        } elseif (($chrs{$c} == ']') && ($top['what'] == SERVICES_JSON_IN_ARR)) {
 809	                            // found a right-bracket, and we're in an array
 810	                            array_pop($stk);
 811	                            //print("Found end of array at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
 812
 813	                        } elseif (($chrs{$c} == '{') &&
 814	                                 in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) {
 815	                            // found a left-brace, and we are in an array, object, or slice
 816	                            array_push($stk, array('what' => SERVICES_JSON_IN_OBJ, 'where' => $c, 'delim' => false));
 817	                            //print("Found start of object at {$c}\n");
 818
 819	                        } elseif (($chrs{$c} == '}') && ($top['what'] == SERVICES_JSON_IN_OBJ)) {
 820	                            // found a right-brace, and we're in an object
 821	                            array_pop($stk);
 822	                            //print("Found end of object at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
 823
 824	                        } elseif (($substr_chrs_c_2 == '/*') &&
 825	                                 in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) {
 826	                            // found a comment start, and we are in an array, object, or slice
 827	                            array_push($stk, array('what' => SERVICES_JSON_IN_CMT, 'where' => $c, 'delim' => false));
 828	                            $c++;
 829	                            //print("Found start of comment at {$c}\n");
 830
 831	                        } elseif (($substr_chrs_c_2 == '*/') && ($top['what'] == SERVICES_JSON_IN_CMT)) {
 832	                            // found a comment end, and we're in one now
 833	                            array_pop($stk);
 834	                            $c++;
 835
 836	                            for ($i = $top['where']; $i <= $c; ++$i)
 837	                                $chrs = substr_replace($chrs, ' ', $i, 1);
 838
 839	                            //print("Found end of comment at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
 840
 841	                        }
 842
 843	                    }
 844
 845	                    if (reset($stk) == SERVICES_JSON_IN_ARR) {
 846	                        return $arr;
 847
 848	                    } elseif (reset($stk) == SERVICES_JSON_IN_OBJ) {
 849	                        return $obj;
 850
 851	                    }
 852
 853	                }
 854	        }
 855	    }
 856
 857	    function isError($data, $code = null)
 858	    {
 859	        if (class_exists('pear')) {
 860	            return PEAR::isError($data, $code);
 861	        } elseif (is_object($data) && (get_class($data) == 'services_json_error' ||
 862	                                 is_subclass_of($data, 'services_json_error'))) {
 863	            return true;
 864	        }
 865
 866	        return false;
 867	    }
 868	}
 869
 870    class Akeeba_Services_JSON_Error
 871    {
 872        function Akeeba_Services_JSON_Error($message = 'unknown error', $code = null,
 873                                     $mode = null, $options = null, $userinfo = null)
 874        {
 875
 876        }
 877    }
 878}
 879
 880if(!function_exists('json_encode'))
 881{
 882	function json_encode($value, $options = 0) {
 883		$flags = SERVICES_JSON_LOOSE_TYPE;
 884		if( $options & JSON_FORCE_OBJECT ) $flags = 0;
 885		$encoder = new Akeeba_Services_JSON($flags);
 886		return $encoder->encode($value);
 887	}
 888}
 889
 890if(!function_exists('json_decode'))
 891{
 892	function json_decode($value, $assoc = false)
 893	{
 894		$flags = 0;
 895		if($assoc) $flags = SERVICES_JSON_LOOSE_TYPE;
 896		$decoder = new Akeeba_Services_JSON($flags);
 897		return $decoder->decode($value);
 898	}
 899}
 900
 901/**
 902 * The base class of Akeeba Engine objects. Allows for error and warnings logging
 903 * and propagation. Largely based on the Joomla! 1.5 JObject class.
 904 */
 905abstract class AKAbstractObject
 906{
 907	/** @var	array	An array of errors */
 908	private $_errors = array();
 909
 910	/** @var	array	The queue size of the $_errors array. Set to 0 for infinite size. */
 911	protected $_errors_queue_size = 0;
 912
 913	/** @var	array	An array of warnings */
 914	private $_warnings = array();
 915
 916	/** @var	array	The queue size of the $_warnings array. Set to 0 for infinite size. */
 917	protected $_warnings_queue_size = 0;
 918
 919	/**
 920	 * Public constructor, makes sure we are instanciated only by the factory class
 921	 */
 922	public function __construct()
 923	{
 924		/*
 925		// Assisted Singleton pattern
 926		if(function_exists('debug_backtrace'))
 927		{
 928			$caller=debug_backtrace();
 929			if(
 930				($caller[1]['class'] != 'AKFactory') &&
 931				($caller[2]['class'] != 'AKFactory') &&
 932				($caller[3]['class'] != 'AKFactory') &&
 933				($caller[4]['class'] != 'AKFactory')
 934			) {
 935				var_dump(debug_backtrace());
 936				trigger_error("You can't create direct descendants of ".__CLASS__, E_USER_ERROR);
 937			}
 938		}
 939		*/
 940	}
 941
 942	/**
 943	 * Get the most recent error message
 944	 * @param	integer	$i Optional error index
 945	 * @return	string	Error message
 946	 */
 947	public function getError($i = null)
 948	{
 949		return $this->getItemFromArray($this->_errors, $i);
 950	}
 951
 952	/**
 953	 * Return all errors, if any
 954	 * @return	array	Array of error messages
 955	 */
 956	public function getErrors()
 957	{
 958		return $this->_errors;
 959	}
 960
 961	/**
 962	 * Add an error message
 963	 * @param	string $error Error message
 964	 */
 965	public function setError($error)
 966	{
 967		if($this->_errors_queue_size > 0)
 968		{
 969			if(count($this->_errors) >= $this->_errors_queue_size)
 970			{
 971				array_shift($this->_errors);
 972			}
 973		}
 974		array_push($this->_errors, $error);
 975	}
 976
 977	/**
 978	 * Resets all error messages
 979	 */
 980	public function resetErrors()
 981	{
 982		$this->_errors = array();
 983	}
 984
 985	/**
 986	 * Get the most recent warning message
 987	 * @param	integer	$i Optional warning index
 988	 * @return	string	Error message
 989	 */
 990	public function getWarning($i = null)
 991	{
 992		return $this->getItemFromArray($this->_warnings, $i);
 993	}
 994
 995	/**
 996	 * Return all warnings, if any
 997	 * @return	array	Array of error messages
 998	 */
 999	public function getWarnings()
1000	{
1001		return $this->_warnings;
1002	}
1003
1004	/**
1005	 * Add an error message
1006	 * @param	string $error Error message
1007	 */
1008	public function setWarning($warning)
1009	{
1010		if($this->_warnings_queue_size > 0)
1011		{
1012			if(count($this->_warnings) >= $this->_warnings_queue_size)
1013			{
1014				array_shift($this->_warnings);
1015			}
1016		}
1017
1018		array_push($this->_warnings, $warning);
1019	}
1020
1021	/**
1022	 * Resets all warning messages
1023	 */
1024	public function resetWarnings()
1025	{
1026		$this->_warnings = array();
1027	}
1028
1029	/**
1030	 * Propagates errors and warnings to a foreign object. The foreign object SHOULD
1031	 * implement the setError() and/or setWarning() methods but DOESN'T HAVE TO be of
1032	 * AKAbstractObject type. For example, this can even be used to propagate to a
1033	 * JObject instance in Joomla!. Propagated items will be removed from ourself.
1034	 * @param object $object The object to propagate errors and warnings to.
1035	 */
1036	public function propagateToObject(&$object)
1037	{
1038		// Skip non-objects
1039		if(!is_object($object)) return;
1040
1041		if( method_exists($object,'setError') )
1042		{
1043			if(!empty($this->_errors))
1044			{
1045				foreach($this->_errors as $error)
1046				{
1047					$object->setError($error);
1048				}
1049				$this->_errors = array();
1050			}
1051		}
1052
1053		if( method_exists($object,'setWarning') )
1054		{
1055			if(!empty($this->_warnings))
1056			{
1057				foreach($this->_warnings as $warning)
1058				{
1059					$object->setWarning($warning);
1060				}
1061				$this->_warnings = array();
1062			}
1063		}
1064	}
1065
1066	/**
1067	 * Propagates errors and warnings from a foreign object. Each propagated list is
1068	 * then cleared on the foreign object, as long as it implements resetErrors() and/or
1069	 * resetWarnings() methods.
1070	 * @param object $object The object to propagate errors and warnings from
1071	 */
1072	public function propagateFromObject(&$object)
1073	{
1074		if( method_exists($object,'getErrors') )
1075		{
1076			$errors = $object->getErrors();
1077			if(!empty($errors))
1078			{
1079				foreach($errors as $error)
1080				{
1081					$this->setError($error);
1082				}
1083			}
1084			if(method_exists($object,'resetErrors'))
1085			{
1086				$object->resetErrors();
1087			}
1088		}
1089
1090		if( method_exists($object,'getWarnings') )
1091		{
1092			$warnings = $object->getWarnings();
1093			if(!empty($warnings))
1094			{
1095				foreach($warnings as $warning)
1096				{
1097					$this->setWarning($warning);
1098				}
1099			}
1100			if(method_exists($object,'resetWarnings'))
1101			{
1102				$object->resetWarnings();
1103			}
1104		}
1105	}
1106
1107	/**
1108	 * Sets the size of the error queue (acts like a LIFO buffer)
1109	 * @param int $newSize The new queue size. Set to 0 for infinite length.
1110	 */
1111	protected function setErrorsQueueSize($newSize = 0)
1112	{
1113		$this->_errors_queue_size = (int)$newSize;
1114	}
1115
1116	/**
1117	 * Sets the size of the warnings queue (acts like a LIFO buffer)
1118	 * @param int $newSize The new queue size. Set to 0 for infinite length.
1119	 */
1120	protected function setWarningsQueueSize($newSize = 0)
1121	{
1122		$this->_warnings_queue_size = (int)$newSize;
1123	}
1124
1125	/**
1126	 * Returns the last item of a LIFO string message queue, or a specific item
1127	 * if so specified.
1128	 * @param array $array An array of strings, holding messages
1129	 * @param int $i Optional message index
1130	 * @return mixed The message string, or false if the key doesn't exist
1131	 */
1132	private function getItemFromArray($array, $i = null)
1133	{
1134		// Find the item
1135		if ( $i === null) {
1136			// Default, return the last item
1137			$item = end($array);
1138		}
1139		else
1140		if ( ! array_key_exists($i, $array) ) {
1141			// If $i has been specified but does not exist, return false
1142			return false;
1143		}
1144		else
1145		{
1146			$item	= $array[$i];
1147		}
1148
1149		return $item;
1150	}
1151
1152}
1153
1154/**
1155 * File post processor engines base class
1156 */
1157abstract class AKAbstractPostproc extends AKAbstractObject
1158{
1159	/** @var string The current (real) file path we'll have to process */
1160	protected $filename = null;
1161
1162	/** @var int The requested permissions */
1163	protected $perms = 0755;
1164
1165	/** @var string The temporary file path we gave to the unarchiver engine */
1166	protected $tempFilename = null;
1167
1168	/** @var int The UNIX timestamp of the file's desired modification date */
1169	public $timestamp = 0;
1170
1171	/**
1172	 * Processes the current file, e.g. moves it from temp to final location by FTP
1173	 */
1174	abstract public function process();
1175
1176	/**
1177	 * The unarchiver tells us the path to the filename it wants to extract and we give it
1178	 * a different path instead.
1179	 * @param string $filename The path to the real file
1180	 * @param int $perms The permissions we need the file to have
1181	 * @return string The path to the temporary file
1182	 */
1183	abstract public function processFilename($filename, $perms = 0755);
1184
1185	/**
1186	 * Recursively creates a directory if it doesn't exist
1187	 * @param string $dirName The directory to create
1188	 * @param int $perms The permissions to give to that directory
1189	 */
1190	abstract public function createDirRecursive( $dirName, $perms );
1191
1192	abstract public function chmod( $file, $perms );
1193
1194	abstract public function unlink( $file );
1195
1196	abstract public function rmdir( $directory );
1197
1198	abstract public function rename( $from, $to );
1199}
1200
1201/**
1202 * The base class of unarchiver classes
1203 */
1204abstract class AKAbstractUnarchiver extends AKAbstractPart
1205{
1206	/** @var string Archive filename */
1207	protected $filename = null;
1208
1209	/** @var array List of the names of all archive parts */
1210	public $archiveList = array();
1211
1212	/** @var int The total size of all archive parts */
1213	public $totalSize = array();
1214
1215	/** @var integer Current archive part number */
1216	protected $currentPartNumber = -1;
1217
1218	/** @var integer The offset inside the current part */
1219	protected $currentPartOffset = 0;
1220
1221	/** @var bool Should I restore permissions? */
1222	protected $flagRestorePermissions = false;
1223
1224	/** @var AKAbstractPostproc Post processing class */
1225	protected $postProcEngine = null;
1226
1227	/** @var string Absolute path to prepend to extracted files */
1228	protected $addPath = '';
1229
1230	/** @var array Which files to rename */
1231	public $renameFiles = array();
1232
1233	/** @var array Which directories to rename */
1234	public $renameDirs = array();
1235
1236	/** @var array Which files to skip */
1237	public $skipFiles = array();
1238
1239	/** @var integer Chunk size for processing */
1240	protected $chunkSize = 524288;
1241
1242	/** @var resource File pointer to the current archive part file */
1243	protected $fp = null;
1244
1245	/** @var int Run state when processing the current archive file */
1246	protected $runState = null;
1247
1248	/** @var stdClass File header data, as read by the readFileHeader() method */
1249	protected $fileHeader = null;
1250
1251	/** @var int How much of the uncompressed data we've read so far */
1252	protected $dataReadLength = 0;
1253
1254	/**
1255	 * Public constructor
1256	 */
1257	public function __construct()
1258	{
1259		parent::__construct();
1260	}
1261
1262	/**
1263	 * Wakeup function, called whenever the class is unserialized
1264	 */
1265	public function __wakeup()
1266	{
1267		if($this->currentPartNumber >= 0)
1268		{
1269			$this->fp = @fopen($this->archiveList[$this->currentPartNumber], 'rb');
1270			if( (is_resource($this->fp)) && ($this->currentPartOffset > 0) )
1271			{
1272				@fseek($this->fp, $this->currentPartOffset);
1273			}
1274		}
1275	}
1276
1277	/**
1278	 * Sleep function, called whenever the class is serialized
1279	 */
1280	public function shutdown()
1281	{
1282		if(is_resource($this->fp))
1283		{
1284			$this->currentPartOffset = @ftell($this->fp);
1285			@fclose($this->fp);
1286		}
1287	}
1288
1289	/**
1290	 * Implements the abstract _prepare() method
1291	 */
1292	final protected function _prepare()
1293	{
1294		parent::__construct();
1295
1296		if( count($this->_parametersArray) > 0 )
1297		{
1298			foreach($this->_parametersArray as $key => $value)
1299			{
1300				switch($key)
1301				{
1302					case 'filename': // Archive's absolute filename
1303						$this->filename = $value;
1304						break;
1305
1306					case 'restore_permissions': // Should I restore permissions?
1307						$this->flagRestorePermissions = $value;
1308						break;
1309
1310					case 'post_proc': // Should I use FTP?
1311						$this->postProcEngine = AKFactory::getpostProc($value);
1312						break;
1313
1314					case 'add_path': // Path to prepend
1315						$this->addPath = $value;
1316						$this->addPath = str_replace('\\','/',$this->addPath);
1317						$this->addPath = rtrim($this->addPath,'/');
1318						if(!empty($this->addPath)) $this->addPath .= '/';
1319						break;
1320
1321					case 'rename_files': // Which files to rename (hash array)
1322						$this->renameFiles = $value;
1323						break;
1324
1325					case 'rename_dirs': // Which files to rename (hash array)
1326						$this->renameDirs = $value;
1327						break;
1328
1329					case 'skip_files': // Which files to skip (indexed array)
1330						$this->skipFiles = $value;
1331						break;
1332				}
1333			}
1334		}
1335
1336		$this->scanArchives();
1337
1338		$this->readArchiveHeader();
1339		$errMessage = $this->getError();
1340		if(!empty($errMessage))
1341		{
1342			$this->setState('error', $errMessage);
1343		}
1344		else
1345		{
1346			$this->runState = AK_STATE_NOFILE;
1347			$this->setState('prepared');
1348		}
1349	}
1350
1351	protected function _run()
1352	{
1353		if($this->getState() == 'postrun') return;
1354
1355		$this->setState('running');
1356
1357		$timer = AKFactory::getTimer();
1358
1359		$status = true;
1360		while( $status && ($timer->getTimeLeft() > 0) )
1361		{
1362			switch( $this->runState )
1363			{
1364				case AK_STATE_NOFILE:
1365					$status = $this->readFileHeader();
1366					if($status)
1367					{
1368						// Send start of file notification
1369						$message = new stdClass;
1370						$message->type = 'startfile';
1371						$message->content = new stdClass;
1372						if( array_key_exists('realfile', get_object_vars($this->fileHeader)) ) {
1373							$message->content->realfile = $this->fileHeader->realFile;
1374						} else {
1375							$message->content->realfile = $this->fileHeader->file;
1376						}
1377						$message->content->file = $this->fileHeader->file;
1378						if( array_key_exists('compressed', get_object_vars($this->fileHeader)) ) {
1379							$message->content->compressed = $this->fileHeader->compressed;
1380						} else {
1381							$message->content->compressed = 0;
1382						}
1383						$message->content->uncompressed = $this->fileHeader->uncompressed;
1384						$this->notify($message);
1385					}
1386					break;
1387
1388				case AK_STATE_HEADER:
1389				case AK_STATE_DATA:
1390					$status = $this->processFileData();
1391					break;
1392
1393				case AK_STATE_DATAREAD:
1394				case AK_STATE_POSTPROC:
1395					$this->postProcEngine->timestamp = $this->fileHeader->timestamp;
1396					$status = $this->postProcEngine->process();
1397					$this->propagateFromObject( $this->postProcEngine );
1398					$this->runState = AK_STATE_DONE;
1399					break;
1400
1401				case AK_STATE_DONE:
1402				default:
1403					if($status)
1404					{
1405						// Send end of file notification
1406						$message = new stdClass;
1407						$message->type = 'endfile';
1408						$message->content = new stdClass;
1409						if( array_key_exists('realfile', get_object_vars($this->fileHeader)) ) {
1410							$message->content->realfile = $this->fileHeader->realFile;
1411						} else {
1412							$message->content->realfile = $this->fileHeader->file;
1413						}
1414						$message->content->file = $this->f…

Large files files are truncated, but you can click here to view the full file