$type, 'message' => $message, 'file' => $file, 'line' => $line ); } /** * Logs a message to a user defined log file. Additional actions to take for * different types of message types can be specified (ERROR, NOTICE, etc). * * @access public * * @param string $message The text description of the message. * @param int $priority The type of message priority (in descending order): * LOG_EMERG system is unusable * LOG_ALERT action must be taken immediately * LOG_CRIT critical conditions * LOG_ERR error conditions * LOG_WARNING warning conditions * LOG_NOTICE normal, but significant, condition * LOG_INFO informational message * LOG_DEBUG debug-level message * @param string $file The file where the log event occurs. * @param string $line The line of the file where the log event occurs. */ function logMsg($message, $priority=LOG_INFO, $file=null, $line=null) { global $CFG; static $previous_events = array(); // If priority is not specified, assume the worst. if (!priorityToString($priority)) { logMsg(sprintf('Log priority %s not defined. (Message: %s)', $priority, $message), LOG_EMERG, $file, $line); $priority = LOG_EMERG; } // If log file is not specified, create one in the codebase root. if (!is_dir($CFG->log_directory) || !is_writable($CFG->log_directory)) { // We must use trigger_error rather than calling logMsg, which might lead to an infinite loop. trigger_error(sprintf('Codebase error: log directory not found or writable: %s', $CFG->log_directory), E_USER_NOTICE); $CFG->log_directory = '/tmp'; $CFG->log_filename = sprintf('%s_%s.log', getenv('USER'), getenv('HTTP_HOST')); } // In case __FILE__ and __LINE__ are not provided, note that fact. $file = '' == $file ? 'unknown-file' : $file; $line = '' == $line ? 'unknown-line' : $line; // Strip HTML tags except any with more than 7 characters because that's probably not a HTML tag, e.g. . preg_match_all('/(<[^>\s]{7,})[^>]*>/', $message, $strip_tags_allow); $message = strip_tags(preg_replace('/\s+/', ' ', $message), (!empty($strip_tags_allow[1]) ? join('> ', $strip_tags_allow[1]) . '>' : null)); // Serialize multi-line messages. $message = preg_replace('/\s+/m', ' ', trim($message)); // Store this event under a unique key, counting each time it occurs so that it only gets reported a limited number of times. $msg_id = md5($message . $priority . $file . $line); if ($CFG->log_ignore_repeated_events && isset($previous_events[$msg_id])) { $previous_events[$msg_id]++; if ($previous_events[$msg_id] == 2) { logMsg(sprintf('%s (Event repeated %s or more times)', $message, $previous_events[$msg_id]), $priority, $file, $line); } return false; } else { $previous_events[$msg_id] = 1; } // Make sure to log in the system's locale. $locale = setlocale(LC_TIME, 0); setlocale(LC_TIME, 'C'); // Data to be stored for a log event. $event = array( 'date' => date('Y-m-d H:i:s'), 'remote ip' => getRemoteAddr(), 'pid' => getmypid(), 'type' => priorityToString($priority), 'file:line' => "$file : $line", 'url' => (isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : ''), 'message' => $message ); // Here's a shortened version of event data. $event_short = $event; $event_short['url'] = truncate($event_short['url'], 120); // Restore original locale. setlocale(LC_TIME, $locale); // FILE ACTION if ($CFG->log_file_priority && $priority <= $CFG->log_file_priority) { $event_str = '[' . join('] [', $event_short) . ']'; error_log($event_str . "\n", 3, $CFG->log_directory . '/' . $CFG->log_filename); } // EMAIL ACTION if ($CFG->log_email_priority && $priority <= $CFG->log_email_priority) { if (empty($CFG->log_to_email)) { $CFG->log_to_email = 'bug@strangecode.com'; } $hostname = (isset($_SERVER['HTTP_HOST']) && '' != $_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : php_uname('n'); $subject = sprintf('[%s %s] %s', $hostname, $event['type'], mb_substr($event['message'], 0, 64)); $email_msg = sprintf("A log event of type '%s' occurred on %s\n\n", $event['type'], $hostname); $headers = "From: codebase@strangecode.com\r\n"; foreach ($event as $k=>$v) { $email_msg .= sprintf("%-11s%s\n", $k, $v); } mb_send_mail($CFG->log_to_email, $subject, $email_msg, $headers, '-f codebase@strangecode.com'); } // SMS ACTION if ($CFG->log_sms_priority && $priority <= $CFG->log_sms_priority) { if (empty($CFG->log_to_email)) { $CFG->log_to_sms = 'bug@strangecode.com'; } $hostname = (isset($_SERVER['HTTP_HOST']) && '' != $_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : php_uname('n'); $subject = sprintf('[%s %s]', $hostname, $priority); $sms_msg = sprintf('%s [%s:%s]', mb_substr($event_short['message'], 0, 64), basename($file), $line); $headers = 'From: ' . $CFG->site_email; mb_send_mail($CFG->log_to_sms, $subject, $sms_msg, $headers); } // SCREEN ACTION if ($CFG->log_screen_priority && $priority <= $CFG->log_screen_priority) { echo "[{$event['date']}] [{$event['type']}] [{$event['file:line']}] [{$event['message']}]\n"; } } /** * Returns the string representation of a LOG_* integer constant. * * @param int $priority The LOG_* integer constant. * * @return The string representation of $priority. */ function priorityToString ($priority) { $priorities = array( LOG_EMERG => 'emergency', LOG_ALERT => 'alert', LOG_CRIT => 'critical', LOG_ERR => 'error', LOG_WARNING => 'warning', LOG_NOTICE => 'notice', LOG_INFO => 'info', LOG_DEBUG => 'debug' ); if (isset($priorities[$priority])) { return $priorities[$priority]; } else { return false; } } /** * Set the URL to return to when dieBoomerangURL() is called. * * @param string $url A fully validated URL. * @param bool $id An identification tag for this url. * FIXME: url garbage collection? */ function setBoomerangURL($url=null, $id=null) { // A redirection will never happen immediately after setting the boomerangURL. // Set the time so ensure this doesn't happen. See validBoomerangURL for more. if (isset($url) && is_string($url)) { // Delete any boomerang request keys in the query string (along with any trailing delimiters after the deletion). $url = preg_replace(array('/([&?])boomerang=[^&?]+[&?]?/', '/[&?]$/'), array('$1', ''), $url); if (isset($_SESSION['_boomerang']['url']) && is_array($_SESSION['_boomerang']['url']) && !empty($_SESSION['_boomerang']['url'])) { // If the URL currently exists in the boomerang array, delete. while ($existing_key = array_search($url, $_SESSION['_boomerang']['url'])) { unset($_SESSION['_boomerang']['url'][$existing_key]); } } if (isset($id)) { $_SESSION['_boomerang']['url'][$id] = $url; } else { $_SESSION['_boomerang']['url'][] = $url; } logMsg(sprintf('setBoomerangURL added URL %s to session %s=%s', $url, session_name(), session_id()), LOG_DEBUG, __FILE__, __LINE__); return true; } else { return false; } } /** * Return the URL set for the specified $id. * * @param string $id An identification tag for this url. */ function getBoomerangURL($id=null) { if (isset($id)) { if (isset($_SESSION['_boomerang']['url'][$id])) { return $_SESSION['_boomerang']['url'][$id]; } else { return ''; } } else if (is_array($_SESSION['_boomerang']['url'])) { return end($_SESSION['_boomerang']['url']); } else { return false; } } /** * Delete the URL set for the specified $id. * * @param string $id An identification tag for this url. */ function deleteBoomerangURL($id=null) { if (isset($id) && isset($_SESSION['_boomerang']['url'][$id])) { unset($_SESSION['_boomerang']['url'][$id]); } else if (is_array($_SESSION['_boomerang']['url'])) { array_pop($_SESSION['_boomerang']['url']); } } /** * Check if a valid boomerang URL value has been set. * if it is not the current url, and has not been accessed within n seconds. * * @return bool True if it is set and not the current URL. */ function validBoomerangURL($id=null, $use_nonspecific_boomerang=false) { if (!isset($_SESSION['_boomerang']['url'])) { logMsg(sprintf('validBoomerangURL no URL set in session %s=%s', session_name(), session_id()), LOG_DEBUG, __FILE__, __LINE__); return false; } // Time is the timestamp of a boomerangURL redirection, or setting of a boomerangURL. // a boomerang redirection will always occur at least several seconds after the last boomerang redirect // or a boomerang being set. $boomerang_time = isset($_SESSION['_boomerang']['time']) ? $_SESSION['_boomerang']['time'] : 0; if (isset($id) && isset($_SESSION['_boomerang']['url'][$id])) { $url = $_SESSION['_boomerang']['url'][$id]; } else if (!isset($id) || $use_nonspecific_boomerang) { // Use non specific boomerang if available. $url = end($_SESSION['_boomerang']['url']); } else { // If URL is not specified, use the $CFG->redirect_home config value. $url = $CFG->redirect_home; } logMsg(sprintf('validBoomerangURL testing url: %s', $url), LOG_DEBUG, __FILE__, __LINE__); if (empty($url)) { return false; } if ($url == absoluteMe()) { // The URL we are directing to is not the current page. logMsg(sprintf('Boomerang URL not valid, same as absoluteMe: %s', $url), LOG_DEBUG, __FILE__, __LINE__); return false; } if ($boomerang_time >= (time() - 2)) { // Last boomerang direction was more than 2 seconds ago. logMsg(sprintf('Boomerang URL not valid, boomerang_time too short: %s', time() - $boomerang_time), LOG_DEBUG, __FILE__, __LINE__); return false; } return true; } /* * Redirects a user by calling App::dieURL(). It will use: * 1. the stored boomerang URL, it it exists * 2. a specified $default_url, it it exists * 3. the referring URL, it it exists. * 4. redirect_home_url configuration variable. * * @access public * @param string $id Identifier for this script. * @param mixed $carry_args Additional arguments to carry in the URL automatically (see App::oHREF()). * @param string $default_url A default URL if there is not a valid specified boomerang URL. * @return bool False if the session is not running. No return otherwise. * @author Quinn Comendant * @since 31 Mar 2006 19:17:00 */ function dieBoomerangURL($id=null, $carry_args=null, $default_url=null) { // Get URL from stored boomerang. Allow non specific URL if ID not valid. if (validBoomerangURL($id, true)) { if (isset($id) && isset($_SESSION['_boomerang']['url'][$id])) { $url = $_SESSION['_boomerang']['url'][$id]; } else { $url = end($_SESSION['_boomerang']['url']); } } else if (isset($default_url)) { $url = $default_url; } else if (!refererIsMe()) { // Ensure that the redirecting page is not also the referrer. $url = getenv('HTTP_REFERER'); } else { $url = ''; } logMsg(sprintf('dieBoomerangURL found URL: %s', $url), LOG_DEBUG, __FILE__, __LINE__); // Delete stored boomerang. deleteBoomerangURL($id); // A redirection will never happen immediately twice. // Set the time so ensure this doesn't happen. $_SESSION['_boomerang']['time'] = time(); dieURL($url, $carry_args); } /** * Uses an http header to redirect the client to the given $url. If sessions are not used * and the session is not already defined in the given $url, the SID is appended as a URI query. * As with all header generating functions, make sure this is called before any other output. * * @param string $url The URL the client will be redirected to. * @param mixed $carry_args Additional url arguments to carry in the query, * or FALSE to prevent carrying queries. Can be any of the following formats: * -array('key1', key2', key3') <-- to save these keys if in the form data. * -array('key1'=>'value', key2'='value') <-- to set keys to default values if not present in form data. * -false <-- To not carry any queries. If URL already has queries those will be retained. * @param bool $always_include_sid Force session id to be added to Location header. */ function dieURL($url, $carry_args=null, $always_include_sid=false) { global $CFG; if ('' == $url) { // If URL is not specified, use the redirect_home. $url = $CFG->redirect_home; } if (preg_match('!^/!', $url)) { // If relative URL is given, prepend correct local hostname. $hostname = ('on' == getenv('HTTPS')) ? 'https://' . getenv('HTTP_HOST') : 'http://' . getenv('HTTP_HOST'); $url = $hostname . $url; } $url = url($url, $carry_args, $always_include_sid); header(sprintf('Location: %s', $url)); logMsg(sprintf('dieURL dying to URL: %s', $url), LOG_DEBUG, __FILE__, __LINE__); die; } /** * Prints a hidden form element with the PHPSESSID when cookies are not used, as well * as hidden form elements for GET_VARS that might be in use. * * @global string $carry_queries An array of keys to define which values to * carry through from the POST or GET. * $carry_queries = array('qry'); for example * * @param mixed $carry_args Additional url arguments to carry in the query, * or FALSE to prevent carrying queries. Can be any of the following formats: * -array('key1', key2', key3') <-- to save these keys if in the form data. * -array('key1'=>'value', key2'='value') <-- to set keys to default values if not present in form data. * -false <-- To not carry any queries. If URL already has queries those will be retained. */ function printHiddenSession($carry_args=null) { static $_using_trans_sid; global $carry_queries; // Save the trans_sid setting. if (!isset($_using_trans_sid)) { $_using_trans_sid = ini_get('session.use_trans_sid'); } // Initialize the carried queries. if (!isset($carry_queries['_carry_queries_init'])) { if (!is_array($carry_queries)) { $carry_queries = array($carry_queries); } $tmp = $carry_queries; $carry_queries = array(); foreach ($tmp as $key) { if (!empty($key) && getFormData($key, false)) { $carry_queries[$key] = getFormData($key); } } $carry_queries['_carry_queries_init'] = true; } // Get any additional query names to add to the $carry_queries array // that are found as function arguments. // If FALSE is a function argument, DO NOT carry the queries. $do_carry_queries = true; $one_time_carry_queries = array(); if (!is_null($carry_args)) { if (is_array($carry_args) && !empty($carry_args)) { foreach ($carry_args as $key=>$arg) { // Get query from appropriate source. if (false === $arg) { $do_carry_queries = false; } else if (false !== getFormData($arg, false)) { $one_time_carry_queries[$arg] = getFormData($arg); // Set arg to form data if available. } else if (!is_numeric($key) && '' != $arg) { $one_time_carry_queries[$key] = getFormData($key, $arg); // Set to arg to default if specified (overwritten by form data). } } } else if (false !== getFormData($carry_args, false)) { $one_time_carry_queries[$carry_args] = getFormData($carry_args); } else if (false === $carry_args) { $do_carry_queries = false; } } // For each existing POST value, we create a hidden input to carry it through a form. if ($do_carry_queries) { // Join the perm and temp carry_queries and filter out the _carry_queries_init element for the final query args. $query_args = array_diff_assoc(array_merge($carry_queries, $one_time_carry_queries), array('_carry_queries_init' => true)); foreach ($query_args as $key=>$val) { echo ''; } } // Include the SID if cookies are disabled. if (!isset($_COOKIE[session_name()]) && !$_using_trans_sid) { echo ''; } } /** * Outputs a fully qualified URL with a query of all the used (ie: not empty) * keys and values, including optional queries. This allows simple printing of * links without needing to know which queries to add to it. If cookies are not * used, the session id will be propogated in the URL. * * @global string $carry_queries An array of keys to define which values to * carry through from the POST or GET. * $carry_queries = array('qry'); for example. * * @param string $url The initial url * @param mixed $carry_args Additional url arguments to carry in the query, * or FALSE to prevent carrying queries. Can be any of the following formats: * -array('key1', key2', key3') <-- to save these keys if in the form data. * -array('key1'=>'value', key2'='value') <-- to set keys to default values if not present in form data. * -false <-- To not carry any queries. If URL already has queries those will be retained. * * @param mixed $always_include_sid Always add the session id, even if using_trans_sid = true. This is required when * URL starts with http, since PHP using_trans_sid doesn't do those and also for * header('Location...') redirections. * * @return string url with attached queries and, if not using cookies, the session id */ function url($url='', $carry_args=null, $always_include_sid=false) { static $_using_trans_sid; global $carry_queries; global $CFG; // Save the trans_sid setting. if (!isset($_using_trans_sid)) { $_using_trans_sid = ini_get('session.use_trans_sid'); } // Initialize the carried queries. if (!isset($carry_queries['_carry_queries_init'])) { if (!is_array($carry_queries)) { $carry_queries = array($carry_queries); } $tmp = $carry_queries; $carry_queries = array(); foreach ($tmp as $key) { if (!empty($key) && getFormData($key, false)) { $carry_queries[$key] = getFormData($key); } } $carry_queries['_carry_queries_init'] = true; } // Get any additional query arguments to add to the $carry_queries array. // If FALSE is a function argument, DO NOT carry the queries. $do_carry_queries = true; $one_time_carry_queries = array(); if (!is_null($carry_args)) { if (is_array($carry_args) && !empty($carry_args)) { foreach ($carry_args as $key=>$arg) { // Get query from appropriate source. if (false === $arg) { $do_carry_queries = false; } else if (false !== getFormData($arg, false)) { $one_time_carry_queries[$arg] = getFormData($arg); // Set arg to form data if available. } else if (!is_numeric($key) && '' != $arg) { $one_time_carry_queries[$key] = getFormData($key, $arg); // Set to arg to default if specified (overwritten by form data). } } } else if (false !== getFormData($carry_args, false)) { $one_time_carry_queries[$carry_args] = getFormData($carry_args); } else if (false === $carry_args) { $do_carry_queries = false; } } // Get the first delimiter that is needed in the url. $delim = preg_match('/\?/', $url) ? ini_get('arg_separator.output') : '?'; $q = ''; if ($do_carry_queries) { // Join the perm and temp carry_queries and filter out the _carry_queries_init element for the final query args. $query_args = array_diff_assoc(urlEncodeArray(array_merge($carry_queries, $one_time_carry_queries)), array('_carry_queries_init' => true)); foreach ($query_args as $key=>$val) { // Check value is set and value does not already exist in the url. if (!preg_match('/[?&]' . preg_quote($key) . '=/', $url)) { $q .= $delim . $key . '=' . $val; $delim = ini_get('arg_separator.output'); } } } // Include the necessary SID if the following is true: // - no cookie in http request OR cookies disabled in config // - sessions are enabled // - the link stays on our site // - transparent SID propagation with session.use_trans_sid is not being used OR url begins with protocol (using_trans_sid has no effect here) // OR // - we must include the SID because we say so (it's used in a context where cookies will not be effective, ie. moving from http to https) // AND // - the SID is not already in the query. if ( ( ( ( !isset($_COOKIE[session_name()]) || !$CFG->session_use_cookies ) && $CFG->session_use_trans_sid && $CFG->enable_session && isMyDomain($url) && ( !$_using_trans_sid || preg_match('!^(http|https)://!i', $url) ) ) || $always_include_sid ) && !preg_match('/[?&]' . preg_quote(session_name()) . '=/', $url) ) { $url .= $q . $delim . session_name() . '=' . session_id(); // logMsg(sprintf('oHREF appending session id to URL: %s', $url), LOG_DEBUG, __FILE__, __LINE__); } else { $url .= $q; } return $url; } /** * Returns a URL processed with App::url and htmlentities for printing in html. * * @access public * @param string $url Input URL to parse. * @return string URL with App::url() and htmlentities() applied. * @author Quinn Comendant * @since 09 Dec 2005 17:58:45 */ function oHREF($url, $carry_args=null, $always_include_sid=false) { $url = url($url, $carry_args, $always_include_sid); // Replace any & not followed by an html or unicode entity with it's & equivalent. $url = preg_replace('/&(?![\w\d#]{1,10};)/', '&', $url); return $url; } /** * Force the user to connect via https (port 443) by redirecting them to * the same page but with https. */ function sslOn() { global $CFG; if (function_exists('apache_get_modules')) { $modules = apache_get_modules(); } else { // It's safe to assume we have mod_ssl if we can't determine otherwise. $modules = array('mod_ssl'); } if ('on' != getenv('HTTPS') && $CFG->ssl_enabled && in_array('mod_ssl', $modules)) { raiseMsg(sprintf(_("Secure SSL connection made to %s"), $CFG->ssl_domain), MSG_NOTICE, __FILE__, __LINE__); // Always append session because some browsers do not send cookie when crossing to SSL URL. dieURL('https://' . $CFG->ssl_domain . getenv('REQUEST_URI'), null, true); } } /** * to enforce the user to connect via http (port 80) by redirecting them to * a http version of the current url. */ function sslOff() { if ('on' == getenv('HTTPS')) { dieURL('http://' . getenv('HTTP_HOST') . getenv('REQUEST_URI'), null, true); } } /** * If the given $url is on the same web site, return true. This can be used to * prevent from sending sensitive info in a get query (like the SID) to another * domain. $method can be "ip" or "domain". The domain method might be preferred * if your domain spans mutiple IP's (load sharing servers) * * @param string $url the URI to test. * @param mixed $method the method to use. Either 'domain' or 'ip'. * * @return bool true if given $url is this domain or has no domain (is a * relative url), false if it's another */ function isMyDomain($url) { if (!preg_match('|\w{1,}\.\w{2,5}/|', $url)) { // If we can't find a domain we assume the URL is relative. return true; } else { return preg_match('/' . preg_quote(getenv('HTTP_HOST')) . '/', $url); } } /** * Loads a list of tables in the current database into an array, and returns * true if the requested table is found. Use this function to enable/disable * funtionality based upon the current available db tables. * * @param string $table The name of the table to search. * * @return bool true if given $table exists. */ function dbTableExists($table) { static $existing_tables; // Save the trans_sid setting. if (!isset($existing_tables)) { // Get DB tables. $existing_tables = array(); $qid = dbQuery("SHOW TABLES"); while (list($row) = mysql_fetch_row($qid)) { $existing_tables[] = $row; } } // Test if requested table is in database. return in_array($table, $existing_tables); } /** * Takes a URL and returns it without the query or anchor portion * * @param string $url any kind of URI * * @return string the URI with ? or # and everything after removed */ function stripQuery($url) { return preg_replace('![?#].*!', '', $url); } /** * Returns the remote IP address, taking into consideration proxy servers. * * @param bool $dolookup If true we resolve to IP to a host name, * if false we don't. * * @return string IP address if $dolookup is false or no arg * Hostname if $dolookup is true */ function getRemoteAddr($dolookup=false) { $ip = getenv('HTTP_CLIENT_IP'); if (empty($ip) || $ip == 'unknown' || $ip == 'localhost' || $ip == '127.0.0.1') { $ip = getenv('HTTP_X_FORWARDED_FOR'); if (empty($ip) || $ip == 'unknown' || $ip == 'localhost' || $ip == '127.0.0.1') { $ip = getenv('REMOTE_ADDR'); } } return $dolookup && '' != $ip ? gethostbyaddr($ip) : $ip; } /** * Tests whether a given iP address can be found in an array of IP address networks. * Elements of networks array can be single IP addresses or an IP address range in CIDR notation * See: http://en.wikipedia.org/wiki/Classless_inter-domain_routing * * @access public * * @param string IP address to search for. * @param array Array of networks to search within. * * @return mixed Returns the network that matched on success, false on failure. */ function ipInRange($my_ip, $ip_pool) { if (!is_array($ip_pool)) { $ip_pool = array($ip_pool); } $my_ip_binary = sprintf('%032b', ip2long($my_ip)); foreach ($ip_pool as $ip) { if (preg_match('![\d\.]{7,15}/\d{1,2}!', $ip)) { // IP is in CIDR notation. list($cidr_ip, $cidr_bitmask) = split('/', $ip); $cidr_ip_binary = sprintf('%032b', ip2long($cidr_ip)); if (substr($my_ip_binary, 0, $cidr_bitmask) === substr($cidr_ip_binary, 0, $cidr_bitmask)) { // IP address is within the specified IP range. return $ip; } } else { if ($my_ip === $ip) { // IP address exactly matches. return $ip; } } } return false; } /** * Returns a fully qualified URL to the current script, including the query. * * @return string a full url to the current script */ function absoluteMe() { $protocol = ('on' == getenv('HTTPS')) ? 'https://' : 'http://'; return $protocol . getenv('HTTP_HOST') . getenv('REQUEST_URI'); } /** * Compares the current url with the referring url. * * @param string $compary_query Include the query string in the comparison. * * @return bool true if the current script (or specified valid_referer) * is the referrer. false otherwise. */ function refererIsMe($exclude_query=false) { if ($exclude_query) { return (stripQuery(absoluteMe()) == stripQuery(getenv('HTTP_REFERER'))); } else { return (absoluteMe() == getenv('HTTP_REFERER')); } } ?>