Sindbad~EG File Manager

Current Path : /var/www/web3/modules/core/classes/
Upload File :
Current File : /var/www/web3/modules/core/classes/GalleryUtilities.class

<?php
/*
 * Gallery - a web based photo album viewer and editor
 * Copyright (C) 2000-2007 Bharat Mediratta
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or (at
 * your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA  02110-1301, USA.
 */

/**
 * A collection of useful utilities that have no obvious home.
 *
 * All of these utilities should be accessed in a static sense, ie.
 *   GalleryUtilities::getFileExtension($filename);
 *
 * Try not to jam too many methods into this class.  Only put methods here if they are of obvious
 * value to the class layer and there's no other home for them.
 *
 * @package GalleryCore
 * @subpackage Classes
 * @author Bharat Mediratta <bharat@menalto.com>
 * @version $Revision: 15513 $
 * @static
 */
class GalleryUtilities {

    /**
     * Get the type of the file from its filename.
     * eg. "foo.jpg" yields 'foo', 'jpg'
     *     "foo.bar.jpeg" yields 'foo.bar', 'jpeg'
     *
     * @param string $filename
     * @return array the file basename, the file extension
     */
    function getFileNameComponents($filename) {

	$pos = strrpos($filename, '.');

	/* No dot == it's all base, no extension */
	if ($pos === false) {
	    return array($filename, '');
	}

	$pos++;

	/* If it's the last char in the name, just return the base */
	if ($pos >= strlen($filename)) {
	    return array(substr($filename, 0, $pos - 1), '');
	}

	return array(substr($filename, 0, $pos - 1), substr($filename, $pos));
    }

    /**
     * Return the file's extension.
     * eg. "foo.jpg" yields "jpg"
     *
     * @param string $filename
     * @return array the file extension
     */
    function getFileExtension($filename) {
	list ($base, $extension) = GalleryUtilities::getFileNameComponents($filename);
	return $extension;
    }

    /**
     * Return the file's basename.
     * eg. "foo.jpg" yields "foo"
     *
     * @param string $filename
     * @return array the file base
     */
    function getFileBase($filename) {
	list ($base, $extension) = GalleryUtilities::getFileNameComponents($filename);
	return $base;
    }

    /**
     * Return data about file attached to request.
     * @param string $key
     * @param boolean $prefix (optional) false to omit Gallery variable prefix (not recommended)
     * @return array file data
     */
    function getFile($key, $prefix=true) {
	$file = array();
	if ($prefix) {
	    $key = GALLERY_FORM_VARIABLE_PREFIX . $key;
	}
	if (isset($_FILES[$key])) {
	    /*
	     * Later during our sanitization process we call stripslashes on our file name.  But it
	     * may legitimately have backslashes in it (eg. c:\apache\tmp\php195.jpg), so make sure
	     * those are escaped at this time.  There's gotta be a better way to handle this.
	     */
	    $file = $_FILES[$key];
	    if (get_magic_quotes_gpc()) {
		$file['tmp_name'] = addslashes($file['tmp_name']);
	    }

	    /* Perform any necessary transformations on our values */
	    GalleryUtilities::sanitizeInputValues($file);
	}

	return $file;
    }

    /**
     * Return all request variables that match the prefix.
     * @param string $key
     * @param boolean $prefix (optional) false to omit Gallery variable prefix (not recommended)
     * @return array key value pairs
     */
    function getFormVariables($key, $prefix=true) {
	if ($prefix) {
	    $key = GALLERY_FORM_VARIABLE_PREFIX . $key;
	}
	$form = array();
	if (isset($_POST[$key]) && is_array($_POST[$key])) {
	    $form = $_POST[$key];
	}

	if (isset($_FILES[$key]) && is_array($_FILES[$key])) {
	    /*
	     * Later during our sanitization process we call stripslashes on our file name.  But it
	     * may legitimately have backslashes in it (eg. c:\apache\tmp\php195.jpg), so make sure
	     * those are escaped at this time.  There's gotta be a better way to handle this.
	     */
	    $postForm = $_FILES[$key];
	    if (get_magic_quotes_gpc()) {
		foreach ($postForm['tmp_name'] as $i => $unused) {
		    $postForm['tmp_name'][$i] = addslashes($postForm['tmp_name'][$i]);
		}
	    }
	    $form = GalleryUtilities::array_merge_replace($form, $postForm);
	}

	if (isset($_GET[$key]) && is_array($_GET[$key])) {
	    $form = GalleryUtilities::array_merge_replace($form, $_GET[$key]);
	}

	/* Perform any necessary transformations on our values */
	GalleryUtilities::sanitizeInputValues($form);

	return $form;
    }

    /**
     * Return all request variables from the URL except the listed keys.
     * @param array $skip (optional) keys to skip
     * @param boolean $prefix (optional) if true, remove form variable prefix from keys in result
     * @return array unsanitized key value pairs
     */
    function getUrlVariablesFiltered($skip=array(), $prefix=false) {
	$filter = array();
	foreach ($skip as $key) {
	    $filter[GALLERY_FORM_VARIABLE_PREFIX . $key] = true;
	}

	$values = array();
	$prefixLength = strlen(GALLERY_FORM_VARIABLE_PREFIX);
	foreach ($_GET as $key => $value) {
	    if (empty($filter[$key])) {
		$values[$prefix ? substr($key, $prefixLength) : $key] = $value;
	    }
	}

	return $values;
    }

    /**
     * Merges two arrays and replace existing entries, like the PHP function array_merge_recursive.
     * The main difference is that existing keys will be replaced with new values, not combined in a
     * new sub array.
     *
     * Usage: $newArray = array_merge_replace( $array, $newValues );
     *
     * @param array $array first array with 'replaceable' values
     * @param array $newValues array which will be merged into first one
     * @return array resulting array
     * @author Tobias Tom <t.tom@succont.de>
     * @todo Verify that both arguments are arrays.
     */
    function array_merge_replace($array, $newValues) {
	foreach ($newValues as $key => $value) {
	    if (is_array($value)) {
		if (!isset($array[$key])) {
		    $array[$key] = array();
		}
		$array[$key] = GalleryUtilities::array_merge_replace($array[$key], $value);
	    } else {
		if (isset($array[$key]) && is_array($array[$key])) {
		    $array[$key][0] = $value;
		} else {
		    if (isset($array) && !is_array($array)) {
			$temp = $array;
			$array = array();
			$array[0] = $temp;
		    }
		    $array[$key] = $value;
		}
	    }
	}
	return $array;
    }

    /**
     * Remove all request variables that match the prefix.
     * @param string $key
     * @param boolean $prefix (optional) false to omit Gallery variable prefix (not recommended)
     */
    function removeFormVariables($key, $prefix=true) {
	/* Remove all matching GET and POST variables */
	if ($prefix) {
	    $key = GALLERY_FORM_VARIABLE_PREFIX . $key;
	}
	unset($_POST[$key]);
	unset($_FILES[$key]);
	unset($_GET[$key]);
    }

    /**
     * Return the specified request variables.  Accept any number of keys and return that number of
     * values, in order.
     * @param one or more string parameters
     * @return mixed a single string value or many values
     */
    function getRequestVariables() {
	$values = array();
	foreach (func_get_args() as $argName) {
	    $values[] = GalleryUtilities::_getRequestVariable(
		GALLERY_FORM_VARIABLE_PREFIX . $argName);
	}

	/* Sanitize the input */
	GalleryUtilities::sanitizeInputValues($values);

	if (func_num_args() == 1) {
	    return array_shift($values);
	}
	return $values;
    }

    /**
     * Return all request variables with the Gallery variable prefix.
     * @return array request variable name => value
     */
    function getAllRequestVariables() {
	$values = array();
	$prefixLength = strlen(GALLERY_FORM_VARIABLE_PREFIX);
	foreach ($_POST as $key => $value) {
	    if (substr($key, 0, $prefixLength) == GALLERY_FORM_VARIABLE_PREFIX) {
		$values[substr($key, $prefixLength)] = $value;
	    }
	}

	foreach ($_GET as $key => $value) {
	    if (substr($key, 0, $prefixLength) == GALLERY_FORM_VARIABLE_PREFIX) {
		$values[substr($key, $prefixLength)] = $value;
	    }
	}

	/* Sanitize the input */
	GalleryUtilities::sanitizeInputValues($values);

	return $values;
    }

    /**
     * Return the specified request variables (omit Gallery variable prefix).  Should be used only
     * when interacting with an external API where prefix can't be used.  Accept any number of keys
     * and return that number of values, in order.
     * @param one or more string parameters
     * @return mixed a single string value or many values
     */
    function getRequestVariablesNoPrefix() {
	$values = array();
	foreach (func_get_args() as $argName) {
	    $values[] = GalleryUtilities::_getRequestVariable($argName);
	}

	/* Sanitize the input */
	GalleryUtilities::sanitizeInputValues($values);

	if (func_num_args() == 1) {
	    return array_shift($values);
	}
	return $values;
    }

    /**
     * Push the given key => value pair back into the request.
     * @param string $key
     * @param string $value
     * @param boolean $prefix (optional) false to omit Gallery variable prefix (not recommended)
     */
    function putRequestVariable($key, $value, $prefix=true) {
	if ($prefix) {
	    $key = GALLERY_FORM_VARIABLE_PREFIX . $key;
	}

	/* Simulate the damage caused by magic_quotes */
	GalleryUtilities::unsanitizeInputValues($key);
	GalleryUtilities::unsanitizeInputValues($value);

	$keyPath = preg_split('/[\[\]]/', $key, -1, PREG_SPLIT_NO_EMPTY);
	GalleryUtilities::_internalPutRequestVariable($keyPath, $value, $_GET);
    }

    /**
     * Take a path in the form of ('foo', 'bar', 'baz') and a destination array and put the value
     * into it like this:
     *   $destination['foo']['bar']['baz'] = $value;
     *
     * @access private
     * @param array $keyPath the key path
     * @param mixed $value
     * @param array $array the destination
     */
    function _internalPutRequestVariable($keyPath, $value, &$array) {
	$key = array_shift($keyPath);
	while (!empty($keyPath)) {
	    $array =& $array[$key];
	    $key = array_shift($keyPath);
	}

	$array[$key] = $value;
    }

    /**
     * Check to see if the given key is in the request.
     * @param string $key
     * @param boolean $prefix (optional) false to omit Gallery variable prefix (not recommended)
     */
    function hasRequestVariable($key, $prefix=true) {
	if ($prefix) {
	    $key = GALLERY_FORM_VARIABLE_PREFIX . $key;
	}
	$value = GalleryUtilities::_getRequestVariable($key);
	return !empty($value);
    }

    /**
     * Remove a request variable.
     * @param string $key
     * @param boolean $prefix (optional) false to omit Gallery variable prefix (not recommended)
     */
    function removeRequestVariable($key, $prefix=true) {
	if ($prefix) {
	    $key = GALLERY_FORM_VARIABLE_PREFIX . $key;
	}
	$keyPath = preg_split('/[\[\]]/', $key, -1, PREG_SPLIT_NO_EMPTY);
	GalleryUtilities::_internalRemoveRequestVariable($keyPath, $_GET);
	GalleryUtilities::_internalRemoveRequestVariable($keyPath, $_POST);
    }

    /**
     * Take a path in the form of ('foo', 'bar', 'baz') and a source array and remove the value from
     * it like this:
     *   unset($source['foo']['bar']['baz']);
     *
     * @param array $keyPath the key path
     * @param array $array the source
     * @access private
     */
    function _internalRemoveRequestVariable($keyPath, &$array) {
	$key = array_shift($keyPath);
	while (!empty($keyPath)) {
	    if (empty($array[$key])) {
		return null;
	    }

	    $array =& $array[$key];
	    $key = array_shift($keyPath);
	}

	unset($array[$key]);
    }

    /**
     * Return prefixed form variable name.
     * @param string $key form variable name
     * @return string prefixed form variable name
     */
    function prefixFormVariable($key) {
	return GALLERY_FORM_VARIABLE_PREFIX . $key;
    }

    /**
     * Return a string of ? markers.
     * @param int $count the number of markers to return
     * @return string
     */
    function makeMarkers($count, $markerFragment='?') {
	if (is_array($count)) {
	    $count = count($count);
	}

	$markers = '';
	if ($count > 1) {
	    $markers = str_repeat($markerFragment . ',', $count - 1);
	}
	if ($count != 0) {
	    $markers .= $markerFragment;
	}

	return $markers;
    }

    /**
     * Convert a filesystem path inside the Gallery directory to an absolute URL.
     *
     * ie. /path/to/gallery/themes/classic/styles/style.css =>
     *     http://example.com/gallery/themes/classic/styles/style.css
     *
     * @param string $path path to a file in the Gallery directory tree
     * @param array $options (optional) options to pass to UrlGenerator
     * @return string a URL
     */
    function convertPathToUrl($path, $options=array()) {
	global $gallery;
	$platform =& $gallery->getPlatform();
	$dirbase = $platform->realpath(dirname(__FILE__) . '/../../..') . '/';

	/*
	 * Factor the Gallery code base out of the path, accounting for differences in directory
	 * separators between platforms
	 */
	$slash = $platform->getDirectorySeparator();
	if ($slash != '/') {
	    $dirbase = str_replace($slash, '/', $dirbase);
	    $path = str_replace($slash, '/', $path);
	}
	$relativePath = str_replace($dirbase, '', $path);

	/* Prepend the Gallery base URL */
	$urlGenerator =& $gallery->getUrlGenerator();
	return $urlGenerator->generateUrl(array('href' => $relativePath), $options);
    }

    /**
     * Scale the given width/height to a new target size, maintaining aspect ratio, but only if the
     * dimensions are already larger than the target (in other words, don't increase the
     * dimensions).
     * @param int $width
     * @param int $height
     * @param int $targetWidth target width
     * @param int $targetHeight (optional) target height, defaults to same as width
     * @return array(width, height)
     */
    function shrinkDimensionsToFit($width, $height, $targetWidth, $targetHeight=null) {
	if (!isset($targetHeight)) {
	    $targetHeight = $targetWidth;
	}
	if ($width > $targetWidth || $height > $targetHeight) {
	    list ($width, $height) = GalleryUtilities::scaleDimensionsToFit(
					    $width, $height, $targetWidth, $targetHeight);
	}
	return array($width, $height);
    }

    /**
     * Scale the given width/height to a new target size, maintaining aspect ratio.
     * @param int $width
     * @param int $height
     * @param int $targetWidth target width
     * @param int $targetHeight (optional) target height, defaults to same as width
     * @return array(width, height)
     */
    function scaleDimensionsToFit($width, $height, $targetWidth, $targetHeight=null) {
	if (!isset($targetHeight)) {
	    $targetHeight = $targetWidth;
	}
	$aspect = $height / $width;
	if ($aspect < $targetHeight / $targetWidth) {
	    $width = (int)$targetWidth;
	    $height = (int)round($targetWidth * $aspect);
	} else {
	    $width = (int)round($targetHeight / $aspect);
	    $height = (int)$targetHeight;
	}
	return array($width, $height);
    }

    /**
     * Round a float and convert to a string.  Replace , with . in case current locale uses comma as
     * fraction separator.
     * @param float $floatValue value to round
     * @param int $precision defaults to zero
     * @return string rounded value
     */
    function roundToString($floatValue, $precision=0) {
	return str_replace(',', '.', round($floatValue, $precision));
    }

    /**
     * Cast to float taking into account that older PHP versions will not treat "." as a decimal
     * separator if the current locale uses "," - when we stop supporting these older versions we
     * can ditch this method and just cast to (float).  (Note that newer PHP versions may accept
     * only "." even if locale uses ",").
     */
    function castToFloat($value) {
	if (is_string($value) && (float)'1.1' != 1.1
		&& ($test = (string)1.1) != '1.1' && strlen($test) == 3) {
	    return (float)str_replace('.', $test{1}, $value);
	}
	return (float)$value;
    }

    /**
     * Figure out if the object specified is an instance of or an instance of a sub class of the
     * class specified
     *
     * @param object $instance any kind of object
     * @param string $className
     * @return boolean
     */
    function isA($instance, $className) {
	return is_a($instance, $className);
    }

    /**
     * Figure out if the object specified is an instance of the class specified, excluding
     * subclasses
     *
     * @param object $instance any kind of object
     * @param string $className
     * @return boolean
     */
    function isExactlyA($instance, $className) {
	return (($instanceClass = get_class($instance)) == $className || $instanceClass ==
		strtr($className, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'));
    }

    /**
     * An entity-safe equivalent to substr (http://php.net/substr).
     * @param string $string the input string
     * @param int $start the 0 based start index (negative values mean subtract from the end)
     * @param int $length the desired length.  If negative negative then that many characters will
     *                be omitted from the end of string (after the start position has been
     *                calculated when a start is negative)
     * @param boolean $countEntitiesAsOne true if the final length be a count of entities, instead
     *                of characters. (default: true)
     * @return array int the number of entities in the string
     *               string the output string
     */
    function entitySubstr($string, $start, $length=null, $countEntitiesAsOne=true) {
	$stringLength = strlen($string);
	if ($stringLength < $start) {
	    return array(0, false);
	}

	if (!isset($length)) {
	    $length = $stringLength;
	}

	if (!$countEntitiesAsOne && $start == 0 && $length >= $stringLength) {
	    return array(strlen($string), $string);
	}

	if (preg_match_all('(&#x[A-Fa-f0-9]+;|&#[0-9]+;|&[A-Za-z0-9]+;|.|\n)', $string, $reg)) {
	    $charArray = $reg[0];
	    $charArrayLength = count($charArray);

	    /* if $length < 0, then it's really the end index */
	    $cookedStart = ($start < 0) ? $charArrayLength + $start : $start;
	    $cookedLength = ($length < 0) ? $charArrayLength - $cookedStart + $length : $length;

	    /* We now have the proper begin/end indices, so grab that slice */
	    if ($countEntitiesAsOne) {
		$slice = array_slice($charArray, $cookedStart, $cookedLength);
		return array(count($slice), join('', $slice));
	    } else {
		$cookedText = '';
		$actualLength = 0;
		for ($i = $cookedStart; $i < $cookedLength; $i++) {
		    if ($charArray[$i][0] == '&') {
			$size = strlen($charArray[$i]);
		    } else {
			$size = 1;
		    }

		    if ($actualLength + $size > $cookedLength) {
			/* We're done */
			break;
		    }
		    $cookedText .= $charArray[$i];
		    $actualLength += $size;
		}
		return array($actualLength, $cookedText);
	    }
	} else {
	    /* How could we get here?  Our regex should match everything */
	    $newString = substr($string, $start, $length);
	    return array(strlen($newString), $newString);
	}
    }

    /**
     * Takes a string of UTF-8 encoded characters and converts it to a string of unicode entities.
     * Each unicode entity has the form &#nnnnn; n={0..9} and can be displayed by UTF-8 supporting
     * browsers.
     *
     * This function was posted in a comment here:
     *   http://www.php.net/manual/en/function.utf8-decode.php
     * by "ronen at greyzone dot com".
     *
     * @param string $source encoded using UTF-8
     * @return string of unicode entities
     */
    function utf8ToUnicodeEntities($source) {
	/*
	 * Array used to figure what number to decrement from character order value according to
	 * number of characters used to map unicode to ASCII by UTF-8
	 */
	$decrement[4] = 240;
	$decrement[3] = 224;
	$decrement[2] = 192;
	$decrement[1] = 0;

	/* Number of bits to shift each charNum by */
	$shift[1][0] = 0;
	$shift[2][0] = 6;
	$shift[2][1] = 0;
	$shift[3][0] = 12;
	$shift[3][1] = 6;
	$shift[3][2] = 0;
	$shift[4][0] = 18;
	$shift[4][1] = 12;
	$shift[4][2] = 6;
	$shift[4][3] = 0;

	$pos = 0;
	$len = strlen($source);
	$encodedString = '';
	while ($pos < $len) {
	    $asciiPos = ord(substr($source, $pos, 1));
	    if (($asciiPos >= 240) && ($asciiPos <= 255)) {
		/* 4 chars representing one unicode character */
		$thisLetter = substr($source, $pos, 4);
		$pos += 4;
	    }
	    else if (($asciiPos >= 224) && ($asciiPos <= 239)) {
		/* 3 chars representing one unicode character */
		$thisLetter = substr($source, $pos, 3);
		$pos += 3;
	    }
	    else if (($asciiPos >= 192) && ($asciiPos <= 223)) {
		/* 2 chars representing one unicode character */
		$thisLetter = substr($source, $pos, 2);
		$pos += 2;
	    }
	    else {
		/* 1 char (lower ASCII) */
		$thisLetter = substr($source, $pos, 1);
		$pos += 1;
	    }

	    /* Process the string representing the letter to a unicode entity */
	    $thisLen = strlen ($thisLetter);
	    $thisPos = 0;
	    $decimalCode = 0;
	    while ($thisPos < $thisLen) {
		$thisCharOrd = ord(substr($thisLetter, $thisPos, 1));
		if ($thisPos == 0) {
		    $charNum = intval($thisCharOrd - $decrement[$thisLen]);
		    $decimalCode += ($charNum << $shift[$thisLen][$thisPos]);
		} else {
		    $charNum = intval($thisCharOrd - 128);
		    $decimalCode += ($charNum << $shift[$thisLen][$thisPos]);
		}
		$thisPos++;
	    }
	    if (($thisLen == 1) && ($decimalCode<=128)) {
		$encodedLetter = $thisLetter;
	    } else {
		$encodedLetter = '&#' . $decimalCode . ';';
	    }
	    $encodedString .= $encodedLetter;
	}
	return $encodedString;
    }

    /**
     * Perform necessary pre-processing on the value part of the incoming array (which may be an
     * associative array or a simple list of values).  We do the following:
     * 1.  Convert UTF-8 values to Unicode entities
     * 2.  Sanitize any input values to remove dangerous values
     *
     * @param mixed $value one value or many values
     * @param boolean $adaptForMagicQuotes (optional) false to skip undoing the damage caused
     *                by magic_quotes
     */
    function sanitizeInputValues(&$value, $adaptForMagicQuotes=true) {
	if (is_array($value)) {
	    foreach (array_keys($value) as $key) {
		$newKey = $key;
		GalleryUtilities::sanitizeInputValues($newKey);
		if ($key != $newKey) {
		    $value[$newKey] =& $value[$key];
		    unset($value[$key]);
		}

		GalleryUtilities::sanitizeInputValues($value[$newKey]);
	    }
	} else {
	    /*
	     * Simulate calling htmlspecialchars($value, ENT_COMPAT, 'UTF-8') We avoid using
	     * htmlspecialchars directly because on some versions of PHP (notable PHP 4.1.2) it
	     * changes the character set of the input data (in one environment it converted the
	     * UTF-8 data to ISO-8859-1)
	     */
	    $value = str_replace(array('&', '"', '<', '>'),
				 array('&amp;', '&quot;', '&lt;', '&gt;'),
				 $value);

	    /* Undo the damage caused by magic_quotes */
	    if ($adaptForMagicQuotes) {
		if (get_magic_quotes_gpc()) {
		    $value = stripslashes($value);
		}
	    }
	}
    }

    /**
     * Undo preprocessing from sanitizeInputValues (useful when we put values back in the request).
     * @param mixed $value one value or many values
     * @param boolean $adaptForMagicQuotes (optional) false to skip redoing the damage caused
     *                by magic_quotes
     */
    function unsanitizeInputValues(&$value, $adaptForMagicQuotes=true) {
	if (is_array($value)) {
	    foreach (array_keys($value) as $key) {
		GalleryUtilities::unsanitizeInputValues($value[$key], $adaptForMagicQuotes);
	    }
	} else {
	    /* Unsanitize dangerous html entities */
	    /* bugs.php.net/bug.php?id=22014 - TODO: remove empty check when min php is 4.3.2+ */
	    $value = empty($value) ? $value : html_entity_decode($value);

	    /* Redo the damage caused by magic_quotes */
	    if ($adaptForMagicQuotes) {
		if (get_magic_quotes_gpc()) {
		    $value = addslashes($value);
		}
	    }
	}
    }

    /**
     * Unescape embedded UTF-8 entities in the given string.
     * @param string $string the input string with UTF-8 entities
     * @return string the UTF-8 string
     */
    function unicodeEntitiesToUtf8($string) {
	$string = preg_replace('/&#([xa-f\d]+);/mei',
	    "GalleryUtilities::unicodeValueToUtf8Value('\\1')", $string);
	return $string;
    }

    /**
     * mb_substr() for UTF-8, with PHP fallback. Truncates incomplete HTML entity at end of result.
     * @param string $string the input string containing raw UTF-8
     * @param int $start
     * @param int $length
     * @return string a multibyte safe substring of input value
     * @deprecated Please use GalleryCoreApi::utf8Substring instead
     */
    function utf8Substring($string, $start, $length) {
	return GalleryCoreApi::utf8Substring($string, $start, $length);
    }

    /**
     * Convert a numerical unicode value to a multibyte UTF-8 string.  Adapted from code found here:
     * http://us2.php.net/utf8_encode
     *
     * @param int $num the unicode value
     * @return string the UTF-8 string
     */
    function unicodeValueToUtf8Value($num) {
	if ($num[0] == 'x') {
	    /* Convert hex to decimal */
	    $num = hexdec(substr($num, 1));
	}

	if ($num < 128) {
	    return chr($num);
	}

	if ($num < 2048) {
	    return (chr(192 + ($num >> 6))
		. chr(128 + ($num & 63)));
	}

	if ($num < 65535) {
	    return (chr(224 + ($num >> 12))
		. chr(128 + (($num >> 6 ) & 63))
		. chr(128 + ($num & 63)));
	}

	if ($num < 2097152) {
	    return (chr(240 + ($num >> 18))
		. chr(128 + (($num >> 12) & 63))
		. chr(128 + (($num >> 6) & 63))
		. chr(128 + ($num & 63)));
	}

	return '';
    }

    /**
     * Equivalent to html_entity_decode() for PHP < 4.3.0 which doesn't have it.
     * @param string $string with html entities
     * @return same string without them
     * @deprecated
     * @todo Remove at the next major version bump of core API
     */
    function htmlEntityDecode($string) {
	return empty($string) ? $string : html_entity_decode($string, ENT_COMPAT);
    }

    /**
     * Apply markup to given text.
     * @param string $text
     * @param string $markupType (optional) markup type, defaults from core markup parameter
     * @return string resulting text
     */
    function markup($text, $markupType=null) {
	GalleryCoreApi::requireOnce('lib/smarty_plugins/modifier.markup.php');
	return smarty_modifier_markup($text, $markupType);
    }

    /**
     * Strip out all potentially dangerous content within HTML.
     * @param string $html HTML
     * @param boolean $decode (optional) true to decode entities, process, then recode
     * @return string safe HTML
     */
    function htmlSafe($html, $decode=false) {
	GalleryCoreApi::requireOnce('lib/pear/Safe.php');
	static $parser;
	if (!isset($parser)) {
	    $parser =& new HTML_Safe();
	}
	if ($decode) {
	    GalleryUtilities::unsanitizeInputValues($html, false);
	}
	$html = $parser->parse($html);
	if ($decode) {
	    GalleryUtilities::sanitizeInputValues($html, false);
	}
	return $html;
    }

    /**
     * Return a specified request variable from the GET or POST vars.
     * @param string $key a single key
     * @return string a single value
     * @access private
     */
    function _getRequestVariable($key) {
	$keyPath = preg_split('/[\[\]]/', $key, -1, PREG_SPLIT_NO_EMPTY);
	$result = GalleryUtilities::_internalGetRequestVariable($keyPath, $_GET);
	if (isset($result)) {
	    return $result;
	}
	return GalleryUtilities::_internalGetRequestVariable($keyPath, $_POST);
    }

    /**
     * Take a path in the form of ('foo', 'bar', 'baz') and a source array and get the value from it
     * like this:
     *   return $source['foo']['bar']['baz'];
     *
     * @param array $keyPath the key path
     * @param array $array the source
     * @return the value or null if it does not exist
     * @access private
     */
    function _internalGetRequestVariable($keyPath, $array) {
	$key = array_shift($keyPath);
	while (!empty($keyPath)) {
	    if (!isset($array[$key])) {
		return null;
	    }

	    $array = $array[$key];
	    $key = array_shift($keyPath);
	}

	return isset($array[$key]) ? $array[$key] : null;
    }

    /**
     * Set HTTP response header.
     * @param string $header HTTP response header
     * @param boolean $replace (optional) avoid replacing HTTP response header if already set
     */
    function setResponseHeader($header, $replace=true) {
	/* Use our PHP VM for testability */
	global $gallery;
	$phpVm = $gallery->getPhpVm();

	$headers =& GalleryUtilities::_getResponseHeaders();

	$key = 'status';
	if (strncasecmp($header, 'HTTP/', 5)) {
	    $key = strtolower(substr($header, 0, strpos($header, ':')));
	}

	if ($replace || empty($headers[$key])) {
	    $phpVm->header($header);
	    $headers[$key] = $header;
	}
    }

    /**
     * Get HTTP response headers.
     * @return array key => value pairs of headers.
     * @access private
     */
    function &_getResponseHeaders() {
	static $headers = array();
	return $headers;
    }

    /**
     * Return true if the path exists and is in the given path list.  Make sure to pass paths in the
     * system charset to this method.
     * @param string $path
     * @param string $list the list of legal paths
     * @return boolean
     */
    function isPathInList($path, $list) {
	global $gallery;
	$platform =& $gallery->getPlatform();
	$slash = $platform->getDirectorySeparator();
	$path = $platform->realpath($path) . $slash;
	$compare = GalleryUtilities::isA($platform, 'WinNtPlatform') ? 'strncasecmp' : 'strncmp';

	foreach ($list as $element) {
	    if (($element = $platform->realpath($element)) === false) {
		continue;
	    }
	    /*
	     * Make sure the compare directory has a trailing slash so that /tmp doesn't
	     * accidentally match /tmpfoo
	     */
	    if ($element{strlen($element)-1} != $slash) {
		$element .= $slash;
	    }

	    if (!$compare($element, $path, strlen($element))) {
		return true;
	    }
	}
	return false;
    }

    /**
     * Is this address a trusted proxy?  Right now we consider any RFC1918 host trustworthy.
     * @param string $addr an address in dotted quad form
     * @return boolean
     */
    function isTrustedProxy($addr) {
	return (boolean)preg_match('/^((10\.\d{1,3}\.\d{1,3}\.\d{1,3})|'
	    . '(172\.(1[6-9]|2[0-9]|3[0-1])\.\d{1,3}\.\d{1,3})|'
	    . '(192\.168\.\d{1,3}\.\d{1,3}))$/', $addr);
    }

    /**
     * Return the address of the remote host.
     * @return string the remote host address (or null)
     */
    function getRemoteHostAddress() {
	$addr = null;
	if (isset($_SERVER['REMOTE_ADDR'])) {
	    $addr = $_SERVER['REMOTE_ADDR'];
	    if (GalleryUtilities::isTrustedProxy($addr)) {
		foreach (array('HTTP_X_FORWARDED_FOR', 'HTTP_CLIENT_IP') as $key) {
		    if (isset($_SERVER[$key]) &&
			    preg_match('/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/', $_SERVER[$key])) {
			$addr = $_SERVER[$key];
			break;
		    }
		}
	    }
	}
	return $addr;
    }

    /**
     * Make sure that the given directory exists (creating it and parent directories if necessary).
     * @param string $dir
     * @return array boolean true if dir exists or was created successfully
     *               array of directories that were created
     */
    function guaranteeDirExists($dir) {
	global $gallery;
	$platform =& $gallery->getPlatform();
	if ($platform->file_exists($dir)) {
	    return array($platform->is_dir($dir), array());
	}

	static $cacheKey = 'GalleryUtilities::guaranteeDirExists';
	if (GalleryDataCache::containsKey($cacheKey)) {
	    $dirPerms = GalleryDataCache::get($cacheKey);
	} else {
	    /* To avoid looping if getPluginParameter calls guaranteeDirExists */
	    GalleryDataCache::put($cacheKey, 0);
	    list ($ret, $dirPerms) =
		GalleryCoreApi::getPluginParameter('module', 'core', 'permissions.directory');
	    /* Ignore error here, then recheck $dir in case it was created in nested call */
	    GalleryDataCache::put($cacheKey, $dirPerms);
	    if ($platform->file_exists($dir)) {
		return array($platform->is_dir($dir), array());
	    }
	}

	$parentDir = dirname($dir);
	if ($parentDir == $dir) {
	    return array(false, array());
	}
	list ($success, $created) = GalleryUtilities::guaranteeDirExists($parentDir);
	if ($success) {
	    $success = !empty($dirPerms) ? $platform->mkdir($dir, $dirPerms)
					 : $platform->mkdir($dir);
	    if ($success) {
		$created[] = $dir;
	    }
	}
	return array($success, $created);
    }

    /**
     * Turn a set of albums into a depth tree suitable for display in a hierarchical format.
     * @param array $albums the GalleryAlbumItem instances
     * @return array an associative array of tree data.  Each node has a 'depth' element, and a
     *               'data' element that contains all the members of the current album item.
     */
    function createAlbumTree($albums) {
	if (empty($albums)) {
	    $tree = array(); return $tree;  /* Help CodeAudit match up returns */
	}

	/* Index the albums by id */
	$map = array();
	foreach ($albums as $album) {
	    $albumId = $album->getId();
	    $parentId = $album->getParentId();
	    $map[$albumId]['instance'] = $album;
	    if (!empty($parentId)) {
		$map[$albumId]['parent'] = $parentId;
		$map[$parentId]['children'][] = $albumId;
	    }
	}

	/*
	 * Prune parents that don't exist.  This can occur if we have multiple roots (unusual) or an
	 * album in the middle of the hierarchy that is not viewable.
	 */
	foreach ($map as $id => $info) {
	    if (isset($info['parent']) && !isset($map[$info['parent']]['instance'])) {
		unset($map[$info['parent']]);
	    }
	}

	/* Find root albums */
	foreach ($map as $id => $info) {
	    if (!isset($info['parent']) || !isset($map[$info['parent']])) {
		$roots[] = $id;
	    }
	}

	/* Walk the root albums */
	$tree = array();
	foreach ($roots as $id) {
	    $tree = array_merge($tree, GalleryUtilities::_createDepthTree($map, $id));
	}

	return $tree;
    }

    /**
     * Recursively walk a parent/child map and build the depth tree.
     * @param array $map parent/child map
     * @param int $id child id
     * @param int $depth (optional) current depth
     * @access private
     */
    function _createDepthTree(&$map, $id, $depth=0) {
	$data = array();
	$data[] = array('depth' => $depth, 'data' => (array)$map[$id]['instance']);
	if (isset($map[$id]['children'])) {
	    foreach ($map[$id]['children'] as $childId) {
		$data = array_merge($data,
				    GalleryUtilities::_createDepthTree($map, $childId, $depth + 1));
	    }
	}

	return $data;
    }

    /**
     * Return approximate filename of given GalleryEntity, or 'unknown' if we can't figure it out.
     * @param object GalleryEntity $entity
     * @return array object GalleryStatus a status code
     *               string pseudoFileName a filename
     */
    function getPseudoFileName($entity) {
	/*
	 * If our GalleryEntity is a GalleryFileSystemEntity, then we've got a path component so
	 * we're cool.  If it's a derivative, then get the pseudo filename of its parent and use
	 * that instead (but make sure the extension matches derivative, as parent mime type may
	 * differ).  If it's neither, then return 'unknown' for now.
	 */
	if (GalleryUtilities::isA($entity, 'GalleryFileSystemEntity')) {
	    $pseudoFileName = $entity->getPathComponent();
	} else if (GalleryUtilities::isA($entity, 'GalleryDerivative')) {
	    list ($ret, $parentEntity) = GalleryCoreApi::loadEntitiesById($entity->getParentId());
	    if ($ret) {
		return array($ret, null);
	    }

	    if (GalleryUtilities::isA($parentEntity, 'GalleryFileSystemEntity')) {
		$pseudoFileName = $parentEntity->getPathComponent();
		if (!method_exists($parentEntity, 'getMimeType') ||
			$parentEntity->getMimeType() != $entity->getMimeType()) {
		    list ($ret, $extensions) =
			GalleryCoreApi::convertMimeToExtensions($entity->getMimeType());
		    if ($ret) {
			return array($ret, null);
		    }
		    if (!empty($extensions)) {
			if (method_exists($parentEntity, 'getMimeType')) {
			    /* Change extension for mime type of this derivative */
			    $pseudoFileName =
				preg_replace('{\.[^.]+$}', '.' . $extensions[0], $pseudoFileName);
			} else {
			    /* Non-item parent, like an album.  Add extension for this mime type. */
			    $pseudoFileName .= '.' . $extensions[0];
			}
		    }
		}
	    }
	}
	return array(null,
		     isset($pseudoFileName) ? $pseudoFileName : 'unknown');
    }

    /**
     * Deprecated.  Use Gallery::getHttpDate instead.
     * @see Gallery::getHttpDate
     * @deprecated
     */
    function getHttpDate($time='') {
	global $gallery;
	return $gallery->getHttpDate($time);
    }

    /**
     * Get contents of MANIFEST files.
     * @return array (file => array('checksum'=>..,'size'=>..,'viewable'=>..), ..)
     */
    function readManifest() {
	/*
	 * Be careful not to reference $gallery here; this method is called from the installer.
	 * Look in (modules|themes)/.../MANIFEST and top level MANIFEST.
	 */
	$base = realpath(dirname(__FILE__) . '/../../..') . '/';
	$list = array();
	if (file_exists($base . 'MANIFEST')) {
	    $list[] = 'MANIFEST';
	}
	foreach (array('modules', 'themes') as $dir) {
	    $dh = opendir($base . $dir);
	    while (($file = readdir($dh)) !== false) {
		if ($file == '..' || $file == '.') {
		    continue;
		}
		if (file_exists($base . $dir . '/' . $file . '/MANIFEST')) {
		    $list[] = $dir . '/' . $file . '/MANIFEST';
		}
	    }
	    closedir($dh);
	}
	$manifest = array();
	foreach ($list as $file) {
	    GalleryUtilities::readIndividualManifest($base . $file, $manifest);
	}
	return $manifest;
    }

    /**
     * Read one manifest file.
     * @param string $filePath the path to the MANIFEST file
     * @return array(file => array('checksum'=>..,'size'=>..,'viewable'=>..), ...)
     */
    function readIndividualManifest($filePath, &$manifest) {
	$lines = file($filePath);
	if (!empty($lines)) {
	    foreach ($lines as $line) {
		$line = trim(preg_replace('/#.*/', '', $line));
		if (empty($line)) {
		    continue;
		}

		$line = explode("\t", $line);
		if (count($line) == 2 && $line[0] == 'R') {
		    $file = trim($line[1]);
		    $manifest[$file] = array('removed' => 1);
		} else {
		    list ($file, $cksum, $cksum_crlf, $size, $size_crlf) = $line;
		    $file = trim($file);
		    $manifest[$file] = array(
			'checksum' => $cksum,
			'checksum_crlf' => $cksum_crlf,
			'size' => $size,
			'size_crlf' => $size_crlf);
		}
	    }
	}
	return $manifest;
    }

    /**
     * Validate string is valid format for an email address.
     * @param string $email email address
     * @return boolean
     */
    function isValidEmailString($email) {
	return (preg_match('/^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9_.-]+\.[a-zA-Z]{2,4}$/', $email) > 0);
    }

    /**
     * Create a hashed password using md5 plus salt.
     * @param string $password plaintext password
     * @param string $salt (optional) salt or hash containing salt (randomly generated if omitted)
     * @return string hashed password
     */
    function md5Salt($password, $salt='') {
	if (empty($salt)) {
	    for ($i = 0; $i < 4; $i++) {
		$char = mt_rand(48, 109);
		$char += ($char > 90) ? 13 : ($char > 57) ? 7 : 0;
		$salt .= chr($char);
	    }
	} else {
	    $salt = substr($salt, 0, 4);
	}
	return $salt . md5($salt . $password);
    }

    /**
     * Verify given password is correct.
     * @param string $guess password guess
     * @param string $hashedPassword hashed password
     * @return boolean true if correct
     */
    function isCorrectPassword($guess, $hashedPassword) {
	return (GalleryUtilities::md5Salt($guess, $hashedPassword) === $hashedPassword);
    }

    /**
     * Verify that the API provided is compatible with the API that we require.
     *
     * We're only compatible if the major numbers are the same, and the required minor number is
     * less than or equal to the provided minor number.
     *
     * @param array $required (major, minor)
     * @param array $provided (major, minor)
     * @return boolean true if compatible
     */
    function isCompatibleWithApi($required, $provided) {
	if (!is_array($required) || !is_array($provided)) {
	    return false;
	}
	if (count($required) != count($provided) || count($required) != 2) {
	    return false;
	}
	for ($i = 0; $i < 1; $i++) {
	    if (!is_int($required[$i]) || !is_int($provided[$i])) {
		return false;
	    }
	}
	if ($required[0] != $provided[0]) {
	    return false;
	}
	if ($required[1] > $provided[1]) {
	    return false;
	}
	return true;
    }

    /**
     * Get all array keys, looking even in arrays contained within the array.
     * @param array $array
     * @return array of keys
     */
    function arrayKeysRecursive($array) {
	$keys = array();
	foreach ($array as $key => $item) {
	    $keys[] = $key;
	    if (is_array($item) && !empty($item)) {
		$keys = array_merge($keys, GalleryUtilities::arrayKeysRecursive($item));
	    }
	}
	return $keys;
    }

    /**
     * Get a php.ini value and return its boolean value.
     * @param string $ini_string name of the php.ini value to be retrieved
     * @return boolean value
     */
    function getPhpIniBool($ini_string) {
	$value = ini_get($ini_string);

	if (!strcasecmp('on', $value) || $value == 1 || $value === true) {
	    return true;
	}

	if (!strcasecmp('off', $value) || $value == 0 || $value === false) {
	    return false;
	}

	/* Catchall */
	return false;
    }

    /**
     * Return id of the search engine currently crawling the site by analyzing the current request.
     * @return string the crawler id, or null if it's a regular user
     */
    function identifySearchEngine() {
	if (!isset($_SERVER['HTTP_USER_AGENT'])) {
	    return null;
	}
	$userAgent = $_SERVER['HTTP_USER_AGENT'];
	if (strstr($userAgent, 'Google') || strstr($userAgent, 'gsa-crawler')) {
	    return 'google';
	} else if (strstr($userAgent, 'Yahoo')) {
	    return 'yahoo';
	} else if (strstr($userAgent, 'Ask Jeeves')) {
	    return 'askjeeves';
	} else if (strstr($userAgent, 'msnbot')) {
	    return 'microsoft';
	} else if (strstr($userAgent, 'Yandex')) {
	    return 'yandex';
	} else if (strstr($userAgent, 'StackRambler')) {
	    return 'stackrambler';
	} else if (strstr($userAgent, 'ConveraCrawler')) {
	    return 'convera';
	}

	return null;
    }

    /**
     * Return a sanitized version of the given variable from the _SERVER superglobal.
     * @param string $key the key in the _SERVER superglobal
     * @return string the value
     */
    function getServerVar($key) {
	if (!isset($_SERVER[$key])) {
	    return null;
	}

	$value = $_SERVER[$key];
	GalleryUtilities::sanitizeInputValues($value);
	return $value;
    }

    /**
     * Return a sanitized version of the given variable from the _COOKIE superglobal.
     * @param string $key the key in the _COOKIE superglobal
     * @return string the value
     */
    function getCookieVar($key) {
	if (!isset($_COOKIE[$key])) {
	    return null;
	}

	/* Fix PHP HTTP_COOKIE header bug http://bugs.php.net/bug.php?id=32802 */
	GalleryUtilities::fixCookieVars();

	$value = $_COOKIE[$key];
	GalleryUtilities::sanitizeInputValues($value);
	return $value;
    }

    /**
     * Deprecated.  Use Gallery::isEmbedded instead.
     * @see Gallery::isEmbedded
     * @deprecated
     */
    function isEmbedded() {
	global $gallery;
	return $gallery->isEmbedded();
    }

    /**
     * Fix the superglobal $_COOKIE to conform with RFC 2965
     *
     * We don't use $_COOKIE[$cookiename] because it doesn't conform to RFC 2965 (the cookie
     * standard), ie. in $_COOKIE, we don't get the cookie with the most specific path for a given
     * cookie name, we get the cookie with the least specific cookie path.  This function does it
     * exactly the other way around, to a) fix our cookie/login problems and to b) conform with the
     * RFC.  The PHP bug was already fixed in spring 2005, but we will have to deal with broken PHP
     * versions for a long time.
     * @see http://bugs.php.net/bug.php?id=32802
     *
     * Fixes also another PHP cookie bug.  PHP doesn't expect the cookie header to have
     * quoted-strings, but they are perfectly legal according to RFC 2965.
     *
     * The third bug fixed here is an MS Internet Explorer (IE) bug.  When using default cookie
     * domains (no leading dot, don't set the domain in set-cookie), IE is supposed to return only
     * cookies that have the exact request-host as their domain.  Example:
     * Cookies stored in the browser with cookie domains: .example.com, .www.example.com,
     *          example.com, www.example.com
     *          The request-host is www.example.com. Thus, IE should return all those cookies but
     *          the example.com cookie, because it's a default domain cookie and it doesn't match
     *          exactly the request-host. But IE returns the example.com cookie too.
     * As MS decided that it returns the cookie with the best domain-match first (unspecified in RFC
     * 2965), this wouldn't be a problem if PHP didn't select the last cookie in the HTTP_COOKIE
     * header.  But with fixCookieVars(), this case is also fixed.
     *
     * This function reevaluates the HTTP Cookie header and populates $_COOKIE with the correct
     * cookies.  We fix only non-array and non '[', ']' containing cookies for simplicity.  To fix
     * our login problem, we'd have to fix only the GALLERYSID cookie anyway.
     *
     * @param boolean $force force the reevaluation of the HTTP header string Cookie
     * @param boolean $unset unset static variable for testability
     */
    function fixCookieVars($force=false, $unset=false) {
	static $fixed;
	if (!isset($fixed) || $force) {
	    $fixed = true;
	    if (isset($_SERVER['HTTP_COOKIE']) && !empty($_SERVER['HTTP_COOKIE'])) {
		/*
		 * Array to keep track of fixed cookies to not make the same mistake as PHP, ie.
		 * don't assign values to cookies that were already fixed/set before
		 */
		$fixedCookies = array();
		/* Check if the Cookie header contains quoted-strings */
		if (strstr($_SERVER['HTTP_COOKIE'], '"') === false) {
		    /*
		     * Use fast method, no quoted-strings in the header.  Get rid of less specific
		     * cookies if multiple cookies with the same NAME are present. Do this by going
		     * from left/first cookie to right/last cookie.
		     */
		    $tok = strtok($_SERVER['HTTP_COOKIE'], ',;');
		    while ($tok) {
			GalleryUtilities::_registerCookieAttr($tok, $fixedCookies);
			$tok = strtok(',;');
		    }
		} else {
		    /*
		     * We can't just tokenize the Cookie header string because there are
		     * quoted-strings and delimiters in quoted-string should be handled as values
		     * and not as delimiters.  Thus, we have to parse it character by character.
		     */
		    $quotedStringOpen = false;
		    $string = $_SERVER['HTTP_COOKIE'];
		    $len = strlen($string);
		    $i = 0;
		    $lastPos = 0;
		    while ($i < $len) {
			switch ($string{$i}) {
			    /* Two attr-pair separators */
			case ',':
			case ';':
			    if ($quotedStringOpen) {
				/* Ignore separators within quoted-strings */
			    } else {
				/* An attr[=value] pair */
				GalleryUtilities::_registerCookieAttr(substr($string, $lastPos,
									     $i - $lastPos),
								      $fixedCookies);
				$lastPos = $i + 1; /* Next attr starts at next char */
			    }
			    break;
			case '"':
			    $quotedStringOpen = !$quotedStringOpen;
			    break;
			case '\\':
			    /* Escape the next character = jump over it */
			    $i++;
			    break;
			}
			$i++;
		    }
		    /* Register last attr in header, but only if the syntax is correct */
		    if (!$quotedStringOpen) {
			GalleryUtilities::_registerCookieAttr(substr($string, $lastPos),
							      $fixedCookies);
		    }
		}
	    }
	}

	/*
	 * To test methods that call fixCookieVars, we have to first unset the static $fixed
	 * variable to enable testability of these functions.  This way, fixCookieVars will
	 * repopulate $_COOKIE on the next call, ie. it simulates a case, where fixCookieVars has
	 * not been called before on the request.
	 */
	if ($unset) {
	    $fixed = null;
	}
    }

    /**
     * Register a cookie variable safely.
     *
     * Creates an entry in $_COOKIE for $attr, which is a name=value pair.  We try to mimic the PHP
     * source code here: make the entry binary safe, don't register non-NAME attributes (eg. cookie
     * version, ...)
     *
     * The one thing we don't do here is treat array cookies correctly because it would but too
     * involving.  But we gracefully just don't replace these array cookies in $_COOKIE, so if they
     * are used somewhere, they will be left intact by fixCookieVars().
     *
     * @param string $attr the cookie var attr, NAME [=VALUE]
     * @param array $fixedCookies (string already registered cookie name, ...)
     * @access private
     */
    function _registerCookieAttr($attr, &$fixedCookies) {
	global $gallery;
	/* Split NAME [=VALUE], value is optional for all attributes but the cookie name */
	if (($pos = strpos($attr, '=')) !== false) {
	    $val = substr($attr, $pos + 1);
	    $key = substr($attr, 0, $pos);
	} else {
	    /* No cookie name=value attr, we can ignore it */
	    return null;
	}
	/* Urldecode header data (php-style of name = attr handling) */
	$key = trim(urldecode($key));
	/* Don't accept zero length key */
	if (($len = strlen($key)) == 0) {
	    return null;
	}
	/* Don't fix cookies with '[', ']' or any array-cookies (for simplicity) */
	$pos = strchr($key, '[');
	if (strchr($key, '[') !== false || strchr($key, ']') !== false) {
	    return null;
	}
	/* Make it a binary safe variable name */
	for ($i = 0; $i < $len; $i++) {
	    if ($key{$i} == ' ' || $key{$i} == '.') {
		$key{$i} = '_';
	    }
	}
	/*
	 * Don't register non-NAME attributes like domain, path, ... which are all starting with a
	 * dollar sign according to RFC 2965
	 */
	if (strpos($key, '$') === 0) {
	    return null;
	}
	/* Urldecode value */
	$val = trim(urldecode($val));
	/* Add slashes if magic_quotes_gpc is on */
	$phpVm = $gallery->getPhpVm();
	if ($phpVm->get_magic_quotes_gpc()) {
	    $key = addslashes($key);
	    $val = addslashes($val);
	}
	if (!isset($fixedCookies[$key])) {
	    $_COOKIE[$key] = $val;
	    $fixedCookies[$key] = true;
	}
    }

    /**
     * ASCII version of PHP's strtolower().  PHP's strtolower doesn't work in all locales as
     * expected, eg. in Turkish, we get non-ASCII characters for an ASCII input string.
     * @param string $string
     * @return string lowercase version of the string
     */
    function strToLower($string) {
	return strtr($string, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz');
    }

    /**
     * ASCII version of PHP's strtoupper().
     * @param string $string
     * @return string uppercase version of the string
     */
    function strToUpper($string) {
	return strtr($string, 'abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ');
    }

    /**
     * Checks whether the given HTTP header is safe.
     *
     * PHP versions before PHP 4.4.2 / PHP 5.1.2 allowed for HTTP header injection (HTTP RS).
     * This function ensures that servers running older PHP versions are protected as well.
     *
     * @param string $header
     * @return bool true if the given header is safe
     */
    function isSafeHttpHeader($header) {
	if (!is_string($header)) {
	    return false;
	}

	/* Don't allow plain occurrences of CR or LF */
	if (strpos($header, chr(13)) !== false || strpos($header, chr(10)) !== false) {
	    return false;
	}

	/* Don't allow (x times) url encoded versions of CR or LF */
	if (preg_match('/%(25)*(0a|0d)/i', $header)) {
	    return false;
	}

	return true;
    }
}
?>

Sindbad File Manager Version 1.0, Coded By Sindbad EG ~ The Terrorists