<?PHP

function UserIsLoggedIn()
{
    global $G_User;
    return $G_User->IsLoggedIn();
}

function LocalFunctionExists($FunctionBaseName)
{
    $FunctionExists = FALSE;
    if (file_exists("include/SPT--Local.php"))
    {
        require_once("include/SPT--Local.php");
        if (function_exists("Local_".$FunctionBaseName))
        {
            $FunctionExists = TRUE;
        }
    }
    return $FunctionExists;
}

function GetCurrentThemeDir()
{
    global $SysConfig;
    global $User;

    # if accessibility wizard is enabled
    #     and user has accessibility attribute selected that requires accessible theme
    if ($SysConfig->AccessibilityWizardEnabled()
        && (($User->PrefFontSize() > 0)
            || (strlen($User->PrefFontTypeFace()))
            || (strlen($User->PrefFontColor()))
            || (strlen($User->PrefBackgroundColor()))
            || ($User->PrefColorAvoidanceFlags() > 0)))
    {
        # print accessible theme
        return "Theme--Accessible";
    }
    else
    {
        # print configured theme
        return $SysConfig->CurrentTheme();
    }
}

function CheckAuthorization($AuthFlag = NULL, $AuthFlag2 = NULL, $AuthFlag3 = NULL, $AuthFlag4 = NULL, $AuthFlag5 = NULL, $AuthFlag6 = NULL)
{
    global $G_User;
    if ($G_User->IsLoggedIn()
            && (!$AuthFlag || $G_User->HasPriv($AuthFlag, $AuthFlag2, $AuthFlag3, $AuthFlag4, $AuthFlag5, $AuthFlag6)))
    {
        return TRUE;
    }
    else
    {
        global $AF;
        $AF->HookEvent("EVENT_HTML_FILE_LOAD", "CheckAuthorization_SignalHook");
        return FALSE;
    }
}
function CheckAuthorization_SignalHook($PageName)
{
    return array("PageName" => "UnauthorizedAccess");
}

function PrintErrorMessageList()
{
    global $ErrorMessages;

    if (isset($ErrorMessages))
    {
        if ($ErrorMessages->HasErrors())
        {
            print($ErrorMessages->GetMessagesAsUList());
        }
    }
}

function PageTitle($NewTitle = NULL, $IncludePortalName = TRUE)
{
    global $SysConfig;
    static $Title;

    if ($NewTitle !== NULL)
    {
        $Title = $NewTitle;
    }
    if ($IncludePortalName && strlen($SysConfig->PortalName()))
    {
        $TitleToReturn = $SysConfig->PortalName()." - ".$Title;
    }
    else
    {
        $TitleToReturn = $Title;
    }
    return $TitleToReturn;
}

function GetFastRatingInterfaceDirectory()
{
    global $AF;

    if (preg_match('/(.*)\/images\/BigStars--0_0\.gif$/',
        $AF->GUIFile("BigStars--0_0.gif"), $Matches))
    {
        return $Matches[1];
    }

    return "";
}

function GetActiveUIOptionList($Selected)
{
    # possible UI directories
    $InterfaceDirs = array(
            "interface",
            "local/interface",
            );

    # start out with empty list
    $Text = "";

    # for each possible UI directory
    foreach ($InterfaceDirs as $InterfaceDir)
    {
        # for each file in current directory
        $Dir = dir($InterfaceDir);
        while (($DirEntry = $Dir->read()) !== FALSE)
        {
            # if file is UI directory
            if ($DirEntry{0} != "." && is_dir($InterfaceDir."/".$DirEntry)
                && preg_match("/[a-zA-Z]+/", $DirEntry))
            {
                # read UI name (if available)
                $UIName = "";
                $UINameFile = $InterfaceDir."/".$DirEntry."/NAME";
                if (is_readable($UINameFile))
                {
                    $FHandle = fopen($UINameFile, "r");
                    $UIName = fgets($FHandle, 64);
                    fclose($FHandle);
                }
                if (strlen($UIName) < 1) {  $UIName = $DirEntry;  }

                # print option tag for UI
                if ($DirEntry == $Selected) {  $Select = " selected";  } else {  $Select = "";  }
                $Text .= "<option value=\"${DirEntry}\"${Select}>${UIName}</option>\n";
            }
        }
    }

    # return list to caller
    return $Text;
}

function InMetadataTool()
{
    global $Editing;

    if ((strstr($_SERVER["SCRIPT_NAME"], "/MetadataTool/")
                    && (strstr($_SERVER["SCRIPT_NAME"], "SPT--ImportData") == FALSE)
                    && (strstr($_SERVER["SCRIPT_NAME"], "SPT--EditControlledNameTypes") == FALSE))
            || (strstr($_SERVER["SCRIPT_NAME"],
                    "SPT--BrowseResources.php") && $Editing)
            || (strstr($_SERVER["SCRIPT_NAME"],
                    "SPT--Advanced.php") && $Editing)
            || (strstr($_SERVER["SCRIPT_NAME"],
                    "SPT--AdvancedSearch.php") && $Editing))
    {
        return TRUE;
    }
    else
    {
        return FALSE;
    }
}

function CloseTags($Tag, $Text)
{
    # if there are a greater number of opening tags than closing tags
    if (preg_match_all("/<".$Tag.">/i", $Text, $Dummy) > preg_match_all("/<\\/".$Tag.">/i", $Text, $Dummy))
    {
        # add closing tag to text
        $Text .= "</".$Tag.">";
    }

    # return (possibly) revised text to caller
    return $Text;
}

function GetHtmlTranslatedString($Value)
{
    $Value = stripslashes($Value);
    $Value = str_replace(array("&", ">", "<", "\"", "'"),
                        array("&amp;", "&gt;", "&lt;", "&quot;", "&#039;"),
                        $Value);
    return $Value;
}

# debugging output utility function (should not be used in production code)
function PrintForDebug($VarName, $VarValue)
{
    print("<pre>".$VarName.": ");
    if (!is_int($VarValue) && !is_string($VarValue) && !is_bool($VarValue))
            {  print("\n");  }
    if (function_exists("xdebug_var_dump"))
    {
        xdebug_var_dump($VarValue);
    }
    else
    {
        print_r($VarValue);
    }
    print("</pre>\n");
}

# return our URL
function OurUrl()
{
    $Url = "http://".(($_SERVER["SERVER_NAME"] != "127.0.0.1")
                ? $_SERVER["SERVER_NAME"]
                : $_SERVER["HTTP_HOST"])
            .$_SERVER["SCRIPT_NAME"];
    return $Url;
}

# return our base URL with trailing slash
function OurBaseUrl()
{
    $BaseUrl = "http://".(($_SERVER["SERVER_NAME"] != "127.0.0.1")
                ? $_SERVER["SERVER_NAME"]
                : $_SERVER["HTTP_HOST"])
            .dirname($_SERVER["SCRIPT_NAME"]);
    if (substr($BaseUrl, -1) != "/")
    {
        $BaseUrl .= "/";
    }
    return $BaseUrl;
}

function utf8_strlen($string)
{
    return strlen(utf8_decode($string));
}

function utf8_substr($string, $start, $length=NULL)
{
    $Parameters = array(utf8_decode($string), $start);

    if (!is_null($length))
    {
        $Parameters[] = $length;
    }

    return utf8_encode(call_user_func_array("substr", $Parameters));
}

# attempt to truncate a string as neatly as possible with
#        respect to word breaks and punctuation
function NeatlyTruncateString($String, $MaxLength, $BreakAnywhere=FALSE)
{
    global $SysConfig;

    $strlen = (strcasecmp($SysConfig->DefaultCharacterSet(), "UTF-8") === 0)
        ? "utf8_strlen" : "strlen";
    $substr = (strcasecmp($SysConfig->DefaultCharacterSet(), "UTF-8") === 0)
        ? "utf8_substr" : "substr";

    # If the string is too long
    if ($MaxLength < $strlen($String))
    {
        # We are going to iterate through the string one char at a time
        # As we go, we will :
        #    * count printing characters (eg, the ones not inside HTML tags)
        #    * mark location of the last space or punct character where
        #        the string could be neatly broken
        #    * maintain a stack of opened tags
        #    * pop tags off of this stack when they close

        $CurLen=0;            # Number of printing characters seen so far
        $Cur=0;               # Current position in the string
        $LastBreakpoint=NULL; # Most recent seen breaking place
        $TagStack = array();  # Stack of open tags
        $InTag = FALSE;
        $BeginsWithUrl = substr(strip_tags($String), 0, 7) == "http://";

        while ($CurLen < $MaxLength && $Cur < $strlen($String))
        {
            if ($InTag)
            {
                # If we are in a tag, just look for the
                # end of the tag, eating characters as we go
                if (strstr(">", $String[$Cur] ) !== FALSE)
                {
                    $InTag = FALSE;
                }
                $Cur++;
                $LastBreakpoint=$Cur;
            }
            else
            {
                # If we were not in a tag, but one starts here:
                if ($String[$Cur]=="<")
                {
                    $InTag = TRUE; # Toggle the flag
                    $ThisTag = "";

                    # Eat the start char and leading spaces
                    while (strstr("< ", $String[$Cur]) !== FALSE)
                    {
                        $Cur++;
                    }
                    # Eat the tag up to the first space or tag-end
                    while (strstr(" >",$String[$Cur] ) === FALSE )
                    {
                        $ThisTag .= $String[$Cur];
                        $Cur++;
                    }

                    $ThisTag = strtolower($ThisTag);

                    if ( $ThisTag[0] == "/" )
                    {
                        # If it was an end-tag, find the last
                        # matching start tag on the tag stack and
                        # pop it off
                        $ThisTag = $substr($ThisTag,1,$strlen($ThisTag));
                        unset($TagStack[max(array_keys($TagStack,$ThisTag))]);
                    }
                    else
                    {
                        # Otherwise, it is a start tag for an element
                        # which has an end tag, push it to the stack
                        if($substr( $ThisTag, 0, 2 ) != "br" &&
                           $substr( $ThisTag, 0, 2 ) != "hr" )
                        {
                            array_push($TagStack, $ThisTag);
                        }
                    }
                }
                else
                {
                    # Otherwise, we are just out in printed text land
                    # Look for point where we can break the string
                    # And store the last occurrence of one
                    if (strstr(",;: ",$String[$Cur]) !== FALSE ||
                        $BreakAnywhere || $BeginsWithUrl)
                    {
                        $LastBreakpoint = $Cur;
                    }
                    $CurLen++;
                    $Cur++;
                }
            }
        }

        # We have now parsed the whole string and need to decide
        # how we are going to chop it

        if ($LastBreakpoint === NULL)
        {
            # If there were no breakpoints,
            # just chop the string at maxlength
            $String = $substr($String, 0, $MaxLength);
        }
        else
        {
            # Otherwise, chop it at the last breakpoint
            $String = $substr($String, 0, $LastBreakpoint);

            # Close all the open tags
            while (count($TagStack)>0)
            {
                $String .= "</".array_pop($TagStack).">";
            }

            # And tack on the ellipsis
            $String .= "...";
        }
    }

    return $String;
}

/**
 * Get the value from an array with a given index or a default value if it does
 * not exist.
 * @param $Array array
 * @param $Key index of the value to retrieve
 * @param $Default value to return if the value does not exist (defaults to NULL)
 * @return the value at the given index or the default value if it doesn't exist
 */
function GetArrayValue(array $Array, $Key, $Default=NULL)
{
    return (array_key_exists($Key, $Array)) ? $Array[$Key] : $Default;
}

# retrieve host name for given IP address (returns IP if no host name)
function GetHostNameForAddr($IpAddress)
{
    static $HostNames;
    if (!isset($HostNames[$IpAddress]))
    {
        $HostNames[$IpAddress] = preg_match("/\b(?:[0-9]{1,3}\.){3}[0-9]{1,3}\b/",
                $IpAddress) ? gethostbyaddr($IpAddress)
                : (($IpAddress == "::1") ? "localhost" : $IpAddress);
    }
    return $HostNames[$IpAddress];
}

# retrieve all saved searches for a given user
function GetSavedSearchesForUser($UserId)
{
    # start with empty list of searches
    $Searches = array();

    # retrieve all IDs for user
    $DB = new SPTDatabase();
    $DB->Query("SELECT SearchId FROM SavedSearches WHERE UserId = '".intval($UserId)."'");
    $SearchIds = $DB->FetchColumn("SearchId");

    # for each search ID
    foreach ($SearchIds as $SearchId)
    {
        # add search to list
        $Searches[$SearchId] = new SavedSearch($SearchId);
    }

    # return list of searches to caller
    return $Searches;
}

# retrieve all saved searches that should be run according to frequency and last run time
function GetSavedSearchesDueToRun()
{
    # start with empty list of searches
    $Searches = array();

    # retrieve searches with frequency/time values that indicate need to be run
    $DB = new SPTDatabase();
    $DB->Query("SELECT SearchId FROM SavedSearches"
            ." WHERE ((Frequency = ".SavedSearch::SEARCHFREQ_HOURLY.") AND (DateLastRun < '"
                    .date("Y-m-d H:i:s", (strtotime("1 hour ago") + 15))."'))"
            ." OR ((Frequency = ".SavedSearch::SEARCHFREQ_DAILY.") AND (DateLastRun < '"
                    .date("Y-m-d H:i:s", (strtotime("1 day ago") + 15))."'))"
            ." OR ((Frequency = ".SavedSearch::SEARCHFREQ_WEEKLY.") AND (DateLastRun < '"
                    .date("Y-m-d H:i:s", (strtotime("1 week ago") + 15))."'))"
            ." OR ((Frequency = ".SavedSearch::SEARCHFREQ_BIWEEKLY.") AND (DateLastRun < '"
                    .date("Y-m-d H:i:s", (strtotime("2 weeks ago") + 15))."'))"
            ." OR ((Frequency = ".SavedSearch::SEARCHFREQ_MONTHLY.") AND (DateLastRun < '"
                    .date("Y-m-d H:i:s", (strtotime("1 month ago") + 15))."'))"
            ." OR ((Frequency = ".SavedSearch::SEARCHFREQ_QUARTERLY.") AND (DateLastRun < '"
                    .date("Y-m-d H:i:s", (strtotime("3 months ago") + 15))."'))"
            ." OR ((Frequency = ".SavedSearch::SEARCHFREQ_YEARLY.") AND (DateLastRun < '"
                    .date("Y-m-d H:i:s", (strtotime("1 year ago") + 15))."'))");
    $SearchIds = $DB->FetchColumn("SearchId");

    # for each search ID
    foreach ($SearchIds as $SearchId)
    {
        # add search to list
        $Searches[$SearchId] = new SavedSearch($SearchId);
    }

    # return list of searches to caller
    return $Searches;
}

/**
 * Strips a string of any tags and attributes that are not provided as
 * exceptions. Stripping of tags or attributes can be disabled by options.
 * Uses the \f (form feed) character as a token, so this will fail in the
 * unlikely event that someone manages to get a \f character into the input
 * string to this function.
 *
 * Options are:
 *     "StripTags" => set to FALSE to disable tag stripping
 *     "StripAttributes" => set to FALSE to disable attribute stripping
 *     "Tags" => string of allowed tags, whitespace delimited (e.g., "a b i")
 *     "Attributes" => string of allowed attributes, whitespace delimited (e.g., "href target")
 *
 * @param string $String string to parse
 * @param object $Options (see above)
 * @return string the parsed string
 * @author Tim Baumgard
 */
function StripTagsAttributes($String, $Options=array())
{
    # make sure have values for the predicate options
    if (!is_array($Options)) {  $Options = array();  }
    if (!isset($Options["StripTags"])) {  $Options["StripTags"] = TRUE;  }
    if (!isset($Options["StripAttributes"])) {  $Options["StripAttributes"] = TRUE;  }

    # phase 1: strip invalid tags if necessary
    if ($Options["StripTags"])
    {
        # remove bad chars, trim, and replace whitespace with "|"
        if (!isset($Options["Tags"]) || !is_string($Options["Tags"]))
            {  $Options["Tags"] = "";  }
        $Tags = preg_replace('/[^a-zA-z0-9 ]/i', '', $Options["Tags"]);
        $Tags = preg_replace('/\s+/i', '|', trim($Tags));

        # escape all allowed tags if there are any to allow
        if (strlen($Tags))
        {
            $String = preg_replace('/<\s*('.$Tags.')(\s[^>]*[^>\/])?'.
                   '(\s*(\/))?\s*>|<\s*(\/)\s*('.$Tags.')[^>]*>/i',
                   sprintf('%c$1$2$4$5$6%c', 12, 12), $String);
        }

        # remove all other tags and then unescape allowed tags
        $String = preg_replace('/<[^>]*>/i', '', $String);
        $String = preg_replace(sprintf('/%c([^%c]*)%c/i', 12, 12, 12),
                '<$1>', $String);
    }

    # phase 2: strip attributes if necessary
    if ($Options["StripAttributes"])
    {
        # remove bad chars, trim, and split by whitespace
        if (!isset($Options["Attributes"]) || !is_string($Options["Attributes"]))
            {  $Options["Attributes"] = "";  }
        $Attributes = preg_replace('/[^a-zA-z0-9 ]/i', '', $Options["Attributes"]);
        $Attributes = preg_split('/\s+/', $Attributes);

        # move all of the attributes into separate contexts for validation
        $String = preg_replace('/<\s*([A-Za-z0-9]+)\s+([^>]*[^>\/])(\/)?\s*>/i',
            sprintf('<$1$3>%c$2%c', 12, 12), $String);

        # extract each allowed attribute from its context
        for ($i = 0; $i < count($Attributes) && strlen($Attributes[$i]); $i++)
        {
            $String = preg_replace(sprintf('/<([A-Za-z0-9]+)(\s[^>]*[^>\/])?(\/)?>'.
                    '%c([^%c]*\s*)'.$Attributes[$i].'=("[^"]*"|\'[^\']*\')'.
                    '([^%c]*\s*)%c/i', 12, 12, 12, 12),
                    sprintf("<$1$2 ".$Attributes[$i]."=$5$3>%c$4$6%c", 12, 12),
                    $String);
        }

        # destroy all the contexts created (thus deleting invalid) and make
        # well-formed singleton tags
        $String = preg_replace(array(sprintf('/%c[^%c]*%c/i', 12, 12, 12), '/<([^>]+)\/>/i'),
                array('', '<$1 />'), $String);
    }

    # return the string
    return $String;
}

function StripXSSThreats($String)
{
    $Options["Tags"]="a b br i s small strike sub sup u";
    $Options["Attributes"]="href";
    return StripTagsAttributes($String,$Options);
}

/**
 * Version-agnostic htmlentities() function.
 * See: http://www.php.net/manual/en/function.htmlentities.php
 * @param $string the input string
 * @param $quote_style defines what is done with quotes
 * @param $charset defines the character set used in conversion
 * @param $double_encode when off, PHP won't encode existing HTML entities (>= 5.2.3)
 * @return the encoded string
 */
function safehtmlentities($string, $quote_style=ENT_COMPAT, $charset="ISO-8859-1", $double_encode=TRUE)
{
    # ignore $double_encode param if php version < 5.2.3
    if (version_compare(phpversion(), "5.2.3", "<"))
    {
        return htmlentities($string, $quote_style, $charset);
    }

    return htmlentities($string, $quote_style, $charset, $double_encode);
}

function defaulthtmlentities($String)
{
    global $SysConfig;

    $CharacterSet = $SysConfig->DefaultCharacterSet();

    return safehtmlentities($String, ENT_QUOTES, $CharacterSet, FALSE);
}

/**
 * Version-agnostic htmlspecialchars() function.
 * See: http://www.php.net/manual/en/function.htmlspecialchars.php
 * @param $string the input string
 * @param $quote_style defines what is done with quotes
 * @param $charset defines the character set used in conversion
 * @param $double_encode when off, PHP won't encode existing HTML entities (>= 5.2.3)
 * @return the encoded string
 */
function safehtmlspecialchars($string, $quote_style=ENT_COMPAT, $charset="ISO-8859-1", $double_encode=TRUE)
{
    # ignore $double_encode param if php version < 5.2.3
    if (version_compare(phpversion(), "5.2.3", "<"))
    {
        return htmlspecialchars($string, $quote_style, $charset);
    }

    return htmlspecialchars($string, $quote_style, $charset, $double_encode);
}

function defaulthtmlspecialchars($String)
{
    global $SysConfig;

    $CharacterSet = $SysConfig->DefaultCharacterSet();

    return safehtmlspecialchars($String, ENT_QUOTES, $CharacterSet, FALSE);
}

# retrieve PHP configuration settings via call to phpinfo()
function GetPhpInfo()
{
    # grab PHP info page
    ob_start();
    phpinfo();
    $InfoPage = ob_get_contents();
    ob_end_clean();

    # start by assuming that no info is available
    $Info = array();

    # for each section on page
    $PageChunks = explode("<h2", $InfoPage);
    foreach ($PageChunks as $PageChunk)
    {
        # look for module/section name
        preg_match("/<a name=\"module_([^<>]*)\">/", $PageChunk, $Piece);

        # if we found module/section
        if (count($Piece) > 1)
        {
            # save module/section name
            $ModuleName = trim($Piece[1]);
        }
        else
        {
            # assume no module/section name
            $ModuleName = "";
        }

        # pull out info values from HTML tables
        preg_match_all("/<tr[^>]*><td[^>]*>(.*)<\/td><td[^>]*>(.*)<\/td>/Ux",
                       $PageChunk, $LocalValue);
        preg_match_all("/<tr[^>]*><td[^>]*>(.*)<\/td><td[^>]*>(.*)<\/td><td[^>]*>(.*)<\/td>/Ux",
                       $PageChunk, $MasterValue);

        # store "local" info values
        foreach ($LocalValue[0] as $MatchString => $Dummy)
        {
            $MatchString = trim($MatchString);
            $Info[$ModuleName][trim(strip_tags($LocalValue[1][$MatchString]))] =
                    array(trim(strip_tags($LocalValue[2][$MatchString])));
        }

        # store "master" info values
        foreach ($MasterValue[0] as $MatchString => $Dummy)
        {
            $MatchString = trim($MatchString);
            $Info[$ModuleName][trim(strip_tags($MasterValue[1][$MatchString]))] =
                    array(trim(strip_tags($MasterValue[2][$MatchString])), trim(strip_tags($MasterValue[3][$MatchString])));
        }
    }

    # return info to caller
    return $Info;
}

function MungeEmailAddress($String)
{
    $FuzzOne = substr(md5(mt_rand()), 0, rand(8,32));
    $FuzzTwo = substr(md5(mt_rand()), 0, rand(8,32));
    return '<span class="EMungeAddr">'.preg_replace(
        '/@/',
        '<span> '.htmlentities($FuzzOne).' </span>'
        .'&#64;'
        .'<span> '.htmlentities($FuzzTwo).' </span>',
        $String).'</span>';
}

/**
* Converts a simpleXML element into an array. Preserves attributes and everything.
* You can choose to get your elements either flattened, or stored in a custom index that
* you define.
* For example, for a given element
* <field name="someName" type="someType"/>
* if you choose to flatten attributes, you would get:
* $array['field']['name'] = 'someName';
* $array['field']['type'] = 'someType';
* If you choose not to flatten, you get:
* $array['field']['@attributes']['name'] = 'someName';
* _____________________________________
* Repeating fields are stored in indexed arrays. so for a markup such as:
* <parent>
* <child>a</child>
* <child>b</child>
* <child>c</child>
* </parent>
* you array would be:
* $array['parent']['child'][0] = 'a';
* $array['parent']['child'][1] = 'b';
* ...And so on.
* _____________________________________
* @param SimpleXMLElement $Xml XML to convert
* @param boolean $FlattenValues Whether to flatten values
*       or to set them under a particular index.  Defaults to TRUE;
* @param boolean $FlattenAttributes Whether to flatten attributes
*       or to set them under a particular index. Defaults to TRUE;
* @param boolean $FlattenChildren Whether to flatten children
*       or to set them under a particular index. Defaults to TRUE;
* @param string $ValueKey Index for values, in case $FlattenValues was
*       set to FALSE. Defaults to "@value"
* @param string $AttributesKey Index for attributes, in case
*       $FlattenAttributes was set to FALSE. Defaults to "@attributes"
* @param string $ChildrenKey Index for children, in case $FlattenChildren
*       was set to FALSE. Defaults to "@children"
* @return array The resulting array.
*/
function SimpleXMLToArray($Xml,
        $FlattenValues = TRUE,
        $FlattenAttributes = TRUE,
        $FlattenChildren = TRUE,
        $ValueKey = "@values",
        $AttributesKey = "@attributes",
        $ChildrenKey = "@children")
{
    $Array = array();
    $XMLClassName = "SimpleXMLElement";
    if (!($Xml instanceof $XMLClassName)) {  return $Array;  }

    $Name = $Xml->getName();
    $Value = trim((string)$Xml);
    if (!strlen($Value)) {  $Value = NULL;  }

    if ($Value !== NULL)
    {
        if ($FlattenValues) {  $Array = $Value;  }
        else {  $Array[$ValueKey] = $Value;  }
    }

    $Children = array();
    foreach ($Xml->children() as $ElementName => $Child)
    {
        $Value = SimpleXMLToArray($Child, $FlattenValues, $FlattenAttributes,
                $FlattenChildren, $ValueKey, $AttributesKey, $ChildrenKey);

        if (isset($Children[$ElementName]))
        {
            if (!isset($MultipleMembers[$ElementName]))
            {
                $Temp = $Children[$ElementName];
                unset($Children[$ElementName]);
                $Children[$ElementName][] = $Temp;
                $MultipleMembers[$ElementName] = TRUE;
            }
            $Children[$ElementName][] = $Value;
        }
        else
        {
            $Children[$ElementName] = $Value;
        }
    }
    if (count($Children))
    {
        if ($FlattenChildren) {  $Array = array_merge($Array, $Children);  }
        else {  $Array[$ChildrenKey] = $Children;  }
    }

    $Attribs = array();
    foreach ($Xml->attributes() as $Name => $Value)
    {
        $Attribs[$Name] = trim($Value);
    }
    if (count($Attribs))
    {
        if (!$FlattenAttributes) {  $Array[$AttributesKey] = $Attribs;  }
        else {  $Array = array_merge($Array, $Attribs);  }
    }

    return $Array;
}


/**
 * Checks an sql statement for potentially destructive keywords.  For
 * safe-looking statements, returns false.  Otherwise, returns true.
 */
function ContainsDangerousSQL($SqlStatement)
{
    $EvilKeywords = array(
        "ALTER","RENAME","TRUNCATE","CREATE","START","COMMIT",
        "ROLLBACK", "SET", "BACKUP", "OPTIMIZE", "REPAIR", "RESTORE",
        "SET", "GRANT", "USE", "UPDATE", "INSERT", "DELETE", "DROP");

    if (preg_match("/(^|\() *(".implode("|",$EvilKeywords).") /i",
                   $SqlStatement) )
    {
        return TRUE;
    }
    else
    {
        return FALSE;
    }
}
?>
