/**
 * Strict Sanitization of GET/POST/COOKIE Globals
 *
 * @param array data
 * @return array
 * @author Joseph Todd Parsons <*****@*****.**>
 */
function fim_sanitizeGPC($type, $data)
{
    global $config;
    /* Define Defaults */
    $metaDataDefaults = array('cast' => 'string', 'require' => false, 'trim' => false, 'filter' => '', 'evaltrue' => false);
    /* Get The Request Body */
    if (in_array($type, array('p', 'post', 'u', 'put', 'd', 'delete'))) {
        $requestBody = file_get_contents('php://input');
    } else {
        $requestBody = '';
    }
    /* Store Request Body */
    /* Process Request Body */
    if (strlen($requestBody) > 0) {
        // If a request body exists, we will use it instead of PHP's generated superglobals. This allows for further REST compatibility. We will, however, only use it for GET and POST requests, at the present time.
        if (($type === 'p' || $type === 'post') && $_SERVER['REQUEST_METHOD'] === 'POST') {
            $activeGlobal = fim_requestBodyToGPC($requestBody);
        } elseif (($type === 'u' || $type === 'put') && $_SERVER['REQUEST_METHOD'] === 'PUT') {
            $activeGlobal = $requestBody;
        } else {
            throw new Exception('Request body present but unsupported in this instance. Type:' . $type);
        }
    } else {
        // Request information is stored in superglobals; get that information.
        switch ($type) {
            // Set the GLOBAL to a local var for processing.
            case 'g':
            case 'get':
                $activeGlobal = $_GET;
                break;
            case 'p':
            case 'post':
                $activeGlobal = $_POST;
                break;
            case 'c':
            case 'cookie':
                $activeGlobal = $_COOKIE;
                break;
            case 'r':
            case 'request':
                $activeGlobal = $_REQUEST;
                break;
            default:
                throw new Exception('Invalid type in fim_sanitizeGPC');
                return false;
                break;
        }
    }
    if (!is_array($activeGlobal)) {
        $activeGlobal = array();
    }
    // Make sure the active global is populated with data.
    foreach ($data as $indexName => $indexData) {
        /* Validate Metadata */
        foreach ($indexData as $metaName => $metaData) {
            if (!in_array($metaName, array('default', 'require', 'trim', 'evaltrue', 'valid', 'min', 'max', 'filter', 'cast'))) {
                throw new Exception('Unrecognised metadata: ' . $metaName);
            } elseif (($metaName === 'require' || $metaName === 'trim' || $metaName === 'evaltrue') && !is_bool($metaData)) {
                throw new Exception('Invalid "' . $metaName . '" in data in fim_sanitizeGPC');
            } elseif ($metaName === 'valid' && !is_array($metaData)) {
                throw new Exception('Defined valid values do not correspond to recognized data type (array).');
            } elseif (($metaName === 'min' || $metaName === 'max') && !is_numeric($metaData)) {
                throw new Exception('Invalid "' . $metaName . '" in data in fim_sanitizeGPC');
            } elseif ($metaName === 'filter' && !in_array($metaData, array('', 'int', 'bool', 'string'))) {
                throw new Exception('Invalid "filter" in data in fim_sanitizeGPC');
            } elseif ($metaName === 'cast' && !in_array($metaData, array('int', 'bool', 'string', 'csv', 'json', 'jsonList', 'ascii128', 'alphanum'))) {
                throw new Exception('Invalid "cast" in data in fim_sanitizeGPC');
            }
        }
        $indexMetaData = array_merge($metaDataDefaults, $indexData);
        // Store indexMetaData with the defaults.
        /* Process Global */
        if (isset($activeGlobal[$indexName], $indexMetaData['valid']) && !in_array($activeGlobal[$indexName], $indexMetaData['valid'])) {
            unset($activeGlobal[$indexName]);
        }
        // If the global is provided, check to see if it's valid. If not, unprovide it (used in the next statements). TODO: Throw warning?
        if (!isset($activeGlobal[$indexName]) && $indexMetaData['default']) {
            $activeGlobal[$indexName] = $indexMetaData['default'];
        }
        // If the global is _not_ provided (either because of the above statement or because it was never provided, but has a default, then provide it as the default.
        if (!isset($activeGlobal[$indexName])) {
            // Finally, if the global is thus-far unprovided...
            if ($indexMetaData['require']) {
                throw new Exception('Required data not present (index ' . $indexName . ').');
            } else {
                continue;
            }
            // And not required, just ignore this global and move on to the next one.
        }
        if ($indexMetaData['trim'] === true) {
            $activeGlobal[$indexName] = trim($activeGlobal[$indexName]);
        }
        // Trim white space.
        switch ($indexMetaData['cast']) {
            case 'csv':
                // Deprecated; replace with JSON type.
                // If a cast is set for a CSV list, explode with a comma seperator, make sure all values corrosponding to the filter (int, bool, or string - the latter pretty much changes nothing), and if evaltrue is true, then the preserveAll flag would be false, and vice-versa.
                $newData[$indexName] = fim_arrayValidate(explode(',', $activeGlobal[$indexName]), $indexMetaData['filter'], $indexMetaData['evaltrue'] ? false : true, isset($indexMetaData['valid']) ? $indexMetaData['valid'] : false);
                break;
            case 'json':
                $newData[$indexName] = json_decode($activeGlobal[$indexName], true, $config['jsonDecodeRecursionLimit'], JSON_BIGINT_AS_STRING);
                /* Newer Code -- Breaks Conventions Because I'm Not Sure Which Conventions I Want Yet */
                $holder = array();
                foreach ($newData[$indexName] as $key => $value) {
                    $holder[fim_cast($indexMetaData['filterKey'] ? $indexMetaData['filterKey'] : 'string', $key)] = fim_cast($indexMetaData['filter'] ? $indexMetaData['filter'] : 'string', $key);
                }
                break;
            case 'jsonList':
                $newData[$indexName] = fim_arrayValidate(array_values(json_decode($activeGlobal[$indexName], true, $config['jsonDecodeRecursionLimit'], JSON_BIGINT_AS_STRING)), $indexMetaData['filter'] ? $indexMetaData['filter'] : 'string', $indexMetaData['evaltrue'] ? false : true, count($indexMetaData['valid']) ? $indexMetaData['valid'] : false);
                break;
            case 'int':
                if ($indexMetaData['evaltrue'] && (int) $activeGlobal[$indexName]) {
                    $newData[$indexName] = (int) $activeGlobal[$indexName];
                } else {
                    $newData[$indexName] = (int) $activeGlobal[$indexName];
                }
                // Include the value whether true or false.
                if (isset($indexMetaData['min']) && $newData[$indexName] < $indexMetaData['min']) {
                    $newData[$indexName] = $indexMetaData['min'];
                } elseif (isset($indexMetaData['max']) && $newData[$indexName] > $indexMetaData['max']) {
                    $newData[$indexName] = $indexMetaData['max'];
                }
                // Maximum Value
                break;
            case 'bool':
                $newData[$indexName] = fim_cast('bool', $activeGlobal[$indexName], isset($indexMetaData['default']) ? $indexMetaData['default'] : null);
                break;
            case 'ascii128':
                $newData[$indexName] = preg_replace('/[^(\\x20-\\x7F)]*/', '', $output);
                break;
                // Remove characters outside of ASCII128 range.
                break;
            case 'alphanum':
                $newData[$indexName] = preg_replace('/[^a-zA-Z0-9]*/', '', str_replace(array_keys($config['romanisation']), array_values($config['romanisation']), $output));
                break;
                // Remove characters that are non-alphanumeric. Note that we will try to romanise what we can.
                break;
            default:
                // String or otherwise.
                $newData[$indexName] = (string) $activeGlobal[$indexName];
                // Append value as string-cast.
                break;
        }
    }
    return $newData;
}
    $database->editUserLists('userIgnoreList', $user, $request['ignoreList'], $_SERVER['REQUEST_METHOD']);
}
/* Friends List */
if (count($request['friendsList'])) {
    $database->editUserLists('userFriendsList', $user, $request['friendsList'], $_SERVER['REQUEST_METHOD']);
}
/* Default Formatting */
if (isset($request['defaultFormatting'])) {
    $updateArray['defaultFormatting'] = (int) $request['defaultFormatting'];
    $xmlData['editUserOptions']['response']['defaultFormatting']['status'] = true;
    $xmlData['editUserOptions']['response']['defaultFormatting']['newValue'] = (string) implode(',', $defaultFormatting);
}
/* Default Highlight & Default Colour */
foreach (array('defaultHighlight', 'defaultColor') as $value) {
    if (isset($request[$value])) {
        $rgb = fim_arrayValidate(explode(',', $request[$value]), 'int', true);
        if (count($rgb) === 3) {
            // Too many entries.
            if ($rgb[0] < 0 || $rgb[0] > 255) {
                // First val out of range.
                $xmlData['editUserOptions']['response'][$value]['status'] = false;
                $xmlData['editUserOptions']['response'][$value]['errStr'] = 'outOfRange1';
                $xmlData['editUserOptions']['response'][$value]['errDesc'] = 'The first value ("red") was out of range.';
            } elseif ($rgb[1] < 0 || $rgb[1] > 255) {
                // Second val out of range.
                $xmlData['editUserOptions']['response'][$value]['status'] = false;
                $xmlData['editUserOptions']['response'][$value]['errStr'] = 'outOfRange2';
                $xmlData['editUserOptions']['response'][$value]['errDesc'] = 'The first value ("green") was out of range.';
            } elseif ($rgb[2] < 0 || $rgb[2] > 255) {
                // Third val out of range.
                $xmlData['editUserOptions']['response'][$value]['status'] = false;