// Copyright: Copyright (C) 2002-2011 Pristine Communications // Set the include path if (!defined("INCPATH_SET")) { require_once dirname(__FILE__) . "/incpath.inc.php"; } // Referenced subroutines require_once "monica/altlang.inc.php"; require_once "monica/cgiemu.inc.php"; require_once "monica/chkpriv.inc.php"; require_once "monica/curtime.inc.php"; require_once "monica/decform.inc.php"; require_once "monica/errhndl.inc.php"; require_once "monica/formfunc.inc.php"; require_once "monica/getlang.inc.php"; require_once "monica/gettext.inc.php"; require_once "monica/hires.inc.php"; require_once "monica/http.inc.php"; require_once "monica/https.inc.php"; require_once "monica/lastmodf.inc.php"; require_once "monica/listpref.inc.php"; require_once "monica/lninfo.inc.php"; require_once "monica/login.inc.php"; require_once "monica/logout.inc.php"; require_once "monica/page2rel.inc.php"; require_once "monica/pagefunc.inc.php"; require_once "monica/rel2abs.inc.php"; require_once "monica/requri.inc.php"; require_once "monica/scptpriv.inc.php"; require_once "monica/sql.inc.php"; require_once "monica/sqlconst.inc.php"; require_once "monica/unauth.inc.php"; require_once "monica/unicode.inc.php"; require_once "monica/upload.inc.php"; require_once "monica/xhtml.inc.php"; require_once "monica/xfileio.inc.php"; // Settings if (!defined("FIELD_HARD_LIMIT")) { define("FIELD_HARD_LIMIT", 307200); } if (!defined("FORM_HARD_LIMIT")) { define("FORM_HARD_LIMIT", 512000); } // initenv: Initialize the script environment // Input: // $args: An associative array of arguments, where // $args["allowed"]: The allowed request methods // $args["restricted"]: Whether restricted to those permitted here // $args["session"]: Whether to start a session (boolean) // $args["sql"]: Which SQL (SQL_NONE, SQL_POSTGRESQL, SQL_MYSQL) // $args["sql_lock"]: The SQL tables to lock in advance function initenv($args) { // The submitted data $FORM =& get_or_post(); // Parse the arguments $session = true; if (array_key_exists("session", $args)) { $session = $args["session"]; } elseif (defined("START_SESSION")) { $session = START_SESSION; } settype($session, "boolean"); $sql = SQL_POSTGRESQL; if ( array_key_exists("sql", $args) && in_array($args["sql"], $GLOBALS["VALID_SQLS"])) { $sql = $args["sql"]; } elseif (defined("START_SQL") && in_array(START_SQL, $GLOBALS["VALID_SQLS"])) { $sql = START_SQL; } $GLOBALS["SQL_DBTYPE"] = $sql; $restricted = false; if (array_key_exists("restricted", $args)) { $restricted = $args["restricted"]; } $lastmod = false; if (array_key_exists("lastmod", $args)) { $lastmod = $args["lastmod"]; } settype($lastmod, "boolean"); $logtime = false; if (array_key_exists("logtime", $args)) { $logtime = $args["logtime"]; } settype($logtime, "boolean"); define("LOGTIME", $logtime); // Set the custom error handler: http_500(): Standard error handler for Monica set_error_handler("http500_error_handler"); // Secure the script environment secure_env(); // Set the multi-byte environment mb_internal_encoding("UTF-8"); // Initial the gettext locale _init_initgettext(); // Check the last output buffering level define("LAST_OB_LEVEL", (ini_get("output_buffering") == "" || !IS_CGI)? 0: 1); // Start output buffering (till one level more than LAST_OB_LEVEL) ob_start(); // Block FunWebProduct // See http://www.networkworld.com/newsletters/web/2003/1208web2.html if ( array_key_exists("HTTP_USER_AGENT", $_SERVER) && strpos($_SERVER["HTTP_USER_AGENT"], "FunWebProduct") !== false) { http_403(N_("Sorry, browsers with FunWebProduct plugin (Smiley, PopSwatter, Spin4Dough, My Mail Signature, My Mail Stationery, My Mail Stamp, Cursor Mania, etc.) are are not welcome. It duplicates your request and produces high load and even crashes to our server. Please remove it first before you visit us.")); } // Block bad-behaved e-mail crawlers // Some bad-behaved e-mail crawlers cannot deal with the parent // directory ".." and ampersands, and attach them to the URI infinitely if (strstr(REQUEST_PATH, "/..") !== false || strstr(REQUEST_URI, "&") !== false) { http_400(false); } // Limit the request methods, default to GET, HEAD and POST // Set to null for no check. Useful for testing and debugging $allowed = array_key_exists("allowed", $args)? $args["allowed"]: array("GET", "HEAD", "POST"); if (!is_null($allowed) && !in_array($_SERVER["REQUEST_METHOD"], $allowed)) { http_405($allowed); } // Block bad robots _init_block_bad_robots(); // Initialize the session if ($session) { // Form processed with HTTPs $https = false; if (array_key_exists("https", $FORM)) { $https = $FORM["https"]? true: false; } // We are HTTPS, processing a form that came from // another non-HTTPS virtual host. // Overriding the session ID. to keep the original session. if ($https && is_https()) { // Use the provided session ID if (array_key_exists(session_name(), $FORM)) { session_id($FORM[session_name()]); } } // Avoid bad robots producing bad session ID. $GLOBALS["php_errormsg"] = null; set_error_handler("null_error_handler"); session_start(); if (!is_null($GLOBALS["php_errormsg"])) { http_400($GLOBALS["php_errormsg"]); } restore_error_handler(); } // Prevent huge sized request check_request_size(); // If client has not logged in on restricted area, we can // bypass SQL connection to save our work if (IS_CGI && is_null(get_login_sn()) && $restricted !== false) { unauth(); } // Initialize the SQL connection if ($sql) { if (defined("NODATABASE") && NODATABASE) { http_503("Database shut down for maintainance."); } sql_connect(); } // Only available on systems with membership turned on if ($sql && use_users() && $session) { // Update the log-in infomation upd_login_info(); // Check the client permission if ($restricted !== false && !is_script_permitted($restricted)) { unauth(); } // Check if this site is closed for policy _init_check_nologin(); } // Prepare the SQL tables to lock if ($sql && array_key_exists("sql_lock", $args)) { // Read-only on non-POSTed forms if ($_SERVER["REQUEST_METHOD"] != "POST") { foreach (array_keys($args["sql_lock"]) as $table) { $args["sql_lock"][$table] = LOCK_SH; } // Add mtime to POSTed forms } else { if (in_array("mtime", sql_tables())) { $args["sql_lock"]["mtime"] = LOCK_EX; } } // Supply the default locks if (use_users()) { $default_locks = array("users", "groups", "scptpriv", "userpref", "users AS createdby", "users AS updatedby"); foreach ($default_locks as $table) { if (!array_key_exists($table, $args["sql_lock"])) { $args["sql_lock"][$table] = LOCK_SH; } } } } // Check the last modified if ($lastmod) { // Set the database tables to check $tables = array(); if (array_key_exists("lmtables", $args)) { $tables = array_merge($tables, $args["lmtables"]); } // Add the locked tables automatically if (array_key_exists("sql_lock", $args)) { $tables = array_merge($tables, array_keys($args["sql_lock"])); } // Set the files to check $files = array(); if (array_key_exists("lmfiles", $args)) { $files = array_merge($files, $args["lmfiles"]); } $callers = debug_backtrace(); if (not_modified($tables, $files, basename($callers[count($callers)-1]["file"]))) { http_304(); } } // Lock the SQL tables if ($sql && array_key_exists("sql_lock", $args)) { sql_lock($args["sql_lock"]); } // Fetch the current data if (function_exists("fetch_current")) { fetch_current(); } // Decode user input FORMs to UTF-8 decode_forms(); // Process the list preference form if (form_type() == "listpref") { if ( array_key_exists("domain", $_POST) && class_exists($_POST["domain"])) { $LIST = new $_POST["domain"]; $LIST->set_listpref(); } else { $handler = new ListPreference($_POST); $handler->main(); } } return; } // restenv: Restore the script environment function restenv() { // Disconnect SQL if ($GLOBALS["SQL_DBTYPE"] != SQL_NONE) { sql_close(); } // Print the page and end the output buffering, if it exists if (ob_get_level() > LAST_OB_LEVEL) { // Return from the innermost level to level 1 while (ob_get_level() > LAST_OB_LEVEL + 1) { ob_end_flush(); } // Obtain the final content $html = ob_get_contents(); ob_end_clean(); // No content -- HTTP 204 if ($html == "") { http_204(); } // The content type $type = array_key_exists("CONTENT_TYPE", $GLOBALS)? $GLOBALS["CONTENT_TYPE"]: xhtml_content_type(); $is_html = preg_match("/^(?:text\/html|application\/xhtml\+xml)\b/", $type)? true: false; $is_rss = $type == "application/rss+xml"; // application/rss+xml is not supported yet if ($is_rss) { $type = "application/xml"; } $is_text = preg_match("/^(?:text\/plain)\b/", $type)? true: false; $is_csv = preg_match("/^(?:text\/csv)\b/", $type)? true: false; $is_js = preg_match("/^(?:text\/javascript)\b/", $type)? true: false; $is_attach = array_key_exists("CONTENT_DISPOSITION", $GLOBALS) && preg_match("/^attachment\b/", $GLOBALS["CONTENT_DISPOSITION"]); // Do the run-time replacements if (($is_html || $is_rss || $is_text || $is_js) && function_exists("page_replacements")) { $rep = page_replacements(); foreach (array_keys($rep) as $key) { $html = str_replace("", $rep[$key]["content"], $html); } } // Fix the HTML output if ($is_html) { if (preg_match("/; charset=([^ ;]+)/", $type, $m)) { $charset = $m[1]; } else { $charset = getlang(LN_CHARSET); $type .= "; charset=$charset"; } // Convert the URLs to relative if ($is_attach) { $html = page2abs($html, REQUEST_PATH); } else { $html = page2rel($html, REQUEST_PATH); } // Mung the e-mail at-signs (@) $html = str_replace("@", "@", $html); // Decode the e-mail at-signs (@) of spamtrap $html = str_replace("spamtrap@", "spamtrap@", $html); // Convert to the desired character set $html = page_encode($html, $charset); // Fix the RSS output } elseif ($is_rss) { if (preg_match("/; charset=([^ ;]+)/", $type, $m)) { $charset = $m[1]; } else { $charset = getlang(LN_CHARSET); $type .= "; charset=$charset"; } // Mung the e-mail at-signs (@) $html = str_replace("@", "@", $html); // Decode the e-mail at-signs (@) of spamtrap $html = str_replace("spamtrap@", "spamtrap@", $html); // Convert to the desired character set $html = page_encode($html, $charset); // Fix the plain text output } elseif ($is_text) { if (preg_match("/; charset=([^ ;]+)/", $type, $m)) { $charset = $m[1]; } else { $charset = "UTF-8"; $type .= "; charset=$charset"; } // Mung the e-mail at-signs (@) $html = str_replace("@", "-at-", $html); // Decode the e-mail at-signs (@) of spamtrap $html = str_replace("spamtrap-at-", "spamtrap@", $html); // Convert to the desired character set $html = h_encode($html, $charset); // Fix the comma-seperated values output } elseif ($is_csv) { if (preg_match("/; charset=([^ ;]+)/", $type, $m)) { $charset = $m[1]; } else { $charset = "UTF-8"; $type .= "; charset=$charset"; } // Convert to the desired character set $html = h_encode($html, $charset); // Fix the javascript output } elseif ($is_js) { if (preg_match("/; charset=([^ ;]+)/", $type, $m)) { $charset = $m[1]; } else { $charset = getlang(LN_CHARSET); $type .= "; charset=$charset"; } // Convert to the desired character set $html = h_encode($html, $charset); } header("Content-Type: $type"); header("Content-Length: " . strlen($html)); if (array_key_exists("CONTENT_DISPOSITION", $GLOBALS)) { header("Content-Disposition: " . $GLOBALS["CONTENT_DISPOSITION"]); } header("Accept-Ranges: none"); if ( substr($type, 0, 5) == "text/" || $is_html || substr($type, 0, 15) == "application/pdf") { header("Content-Language: " . getlang(LN_NAME)); } // Client cache if (array_key_exists("LAST_MODIFIED", $GLOBALS)) { if (!is_null(get_login_sn())) { header("Cache-Control: private"); } else { header("Cache-Control: public"); } header("Last-Modified: " . date("r", $GLOBALS["LAST_MODIFIED"])); } else { header("Cache-Control: no-cache"); } // Content negotiation, see HTTP/1.1 section 13.6 if ( count($GLOBALS["ALL_LINGUAS"]) > 1 && $_SERVER["REQUEST_METHOD"] != "POST" && $_SERVER["REQUEST_METHOD"] != "PUT" && !array_key_exists("lang", $_GET)) { header("Content-Location: " . rel2abs(altlang(getlang(), page_param()))); header("Vary: negotiate,accept,accept-language,cookie"); } // Print the page body if ($_SERVER["REQUEST_METHOD"] != "HEAD") { echo $html; } } // Purge the file cache if (isset($_SESSION) && array_key_exists("savefile", $_SESSION)) { $FILES =& file_deposit(); $total = 0; foreach (array_keys($FILES) as $sn) { // Not a normal record if ( !is_array($FILES[$sn]) || !array_key_exists("size", $FILES[$sn])) { continue; } $total += $FILES[$sn]["size"]; // Too larged if ($total > UPLOAD_DEPOSIT_MAX) { clear_file_deposit(); break; } } } // Print the processing time for debugging purpose if (defined("LOGTIME") && LOGTIME) { error_log("Process time: " . (time_hires() - T_START) . " sec."); } return; } // secure_env: Secure the script environment function secure_env() { $ENVLIST = array("PATH", "USER", "MAIL", "HOME", "USERNAME", "LOGNAME", "PWD", "ENV", "KDEDIR"); foreach ($ENVLIST as $env) { if (getenv($env) !== false) { putenv("$env="); } } return; } // check_request_size: Prevent huge sized request function check_request_size() { $len = _init_check_array_size($_GET); if ($len === false) http_413(); if ($len > FORM_HARD_LIMIT) http_413(); $len = _init_check_array_size($_POST); if ($len === false) http_413(); if ($len > FORM_HARD_LIMIT) http_413(); $len = _init_check_array_size($_COOKIE); if ($len === false) http_413(); if ($len > FORM_HARD_LIMIT) http_413(); $len = _init_check_array_size($_SERVER); if ($len === false) http_413(); if ($len > FORM_HARD_LIMIT) http_413(); return; } // _init_check_array_size: Recursively check the size of an array function _init_check_array_size(&$a) { // Check the array size if (gettype($a) != "array") { $len = strlen($a); if ($len > FIELD_HARD_LIMIT) { return false; } return $len; } // Check the array size $len = 0; foreach (array_keys($a) as $k) { $len += strlen($k); if (gettype($a[$k]) == "array") { $len0 = _init_check_array_size($a[$k]); if ($len0 === false) { return false; } $len += $len0; } else { $len0 = strlen($a[$k]); if ($len0 > FIELD_HARD_LIMIT) { return false; } $len += $len0; } } return $len; } // _init_check_nologin: If this site is closed for policy function _init_check_nologin() { // Not logged in - no checks needed if (is_null(get_login_sn())) { return; } // NOLOGIN: Only super-users can log in now if (defined("NOLOGIN") && NOLOGIN) { // Only super-users can log into no-log-in websites if (is_su()) { return; } logout(); // Deny access to the development site ob_end_clean(); ob_start(); html_header(_("Log-In Closed")); html_title(_("Log-In Closed")); html_message(_("Log-in is temporarily closed for maintainance now. Please come again later. Sorry for the inconvienence.")); html_footer(); $html = ob_get_contents(); ob_end_clean(); if ($_SERVER["REQUEST_METHOD"] != "HEAD") { echo $html; } // No need to return exit; // CLOSEDEV: Only developers can log in now } elseif (defined("CLOSEDEV") && CLOSEDEV) { if (is_group("dev")) { return; } logout(); // Deny access to the development site ob_end_clean(); ob_start(); html_header(_("Development Site Closed")); html_title(_("Development Site Closed")); html_message(_("Development site is closed. Please work on the live site.")); html_footer(); $html = ob_get_contents(); ob_end_clean(); if ($_SERVER["REQUEST_METHOD"] != "HEAD") { echo $html; } // No need to return exit; } } // _init_block_bad_robots: Block bad robots function _init_block_bad_robots() { // User-Agent: was sent if (array_key_exists("HTTP_USER_AGENT", $_SERVER)) { // Check MSIE 6 and unbalanced brackets if ($_SERVER["HTTP_USER_AGENT"] == "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1") { http_403(); } // Check User-Agent in user-agent identification if (substr($_SERVER["HTTP_USER_AGENT"], 0, 11) == "User-Agent:") { http_403(); } // Check User-Agent in user-agent identification if (strpos($_SERVER["HTTP_USER_AGENT"], "DTS Agent") !== false) { http_403(false); } // MSIE 6 must be HTTP/1.1 // -- disabled 2006-07-07. Lots of innocent set this way (why?) // Opera pretends to be MSIE 6, and uses HTTP/1.0 (why?) //if ( preg_match("/\bMSIE [456]/", $_SERVER["HTTP_USER_AGENT"]) // && strstr($_SERVER["HTTP_USER_AGENT"], "Opera") === false // && $_SERVER["SERVER_PROTOCOL"] != "HTTP/1.1") { // http_403(); //} } // Check huge amount of requests from a same host in a short time if (IS_CGI) { $file = ini_get("session.save_path") . "/ipacct.txt"; $count = 0; $newrecs = array(); $lastseen = null; // Check the current records if (file_exists($file)) { $fp = fopen($file, "r+"); flock($fp, LOCK_EX); $size = filesize($file); // fread() does not allow reading of 0 bytes now if ($size == 0) { $content = ""; } else { $content = fread($fp, $size); } $currecs = explode("\n", $content); foreach ($currecs as $record) { // Drop invalid if (!preg_match("/^(\d+) (\d+\.\d+\.\d+\.\d+) /", $record, $m)) { continue; } // We only keep 30 minutes if (NOW - $m[1] >= 1800) { continue; } $newrecs[] = "$record\n"; if ($m[2] == $_SERVER["REMOTE_ADDR"]) { if (is_null($lastseen) || $lastseen > $m[1]) { $lastseen = $m[1]; } $count++; } } fseek($fp, 0, SEEK_SET); ftruncate($fp, 0); // No current records yet } else { $fp = fopen($file, "w"); flock($fp, LOCK_EX); } // Add this record if (is_null($lastseen)) { $lastseen = NOW; } $newrecs[] = sprintf("%d %s %s\n", NOW, $_SERVER["REMOTE_ADDR"], date("Y-m-d H:i:s", NOW)); $count++; // Update the accounting fwrite($fp, implode("", $newrecs)); flock($fp, LOCK_UN); fclose($fp); /* Debug if (array_key_exists("debug_reqid", $_GET)) { error_log(sprintf("req=%s NOW=%d lastseen=%d count=%d avg=%.2f block=%s", $_GET["debug_reqid"], NOW, $lastseen, $count, $count / ((NOW == $lastseen)? 1: NOW - $lastseen), ($count / ((NOW == $lastseen)? 1: NOW - $lastseen) > 2? "yes": "no"))); } */ // Averagely 2 script requests in 1 second from a same host - that is too much. if ($count / ((NOW == $lastseen)? 1: NOW - $lastseen) > 2) { http_503("Request too fast."); } } } // _init_initgettext: Initialize the gettext locale function _init_initgettext() { putenv("LANG=" . getlang(LN_LOCALE)); putenv("LANGUAGE=" . getlang(LN_LOCALE)); putenv("LC_ALL=" . getlang(LN_LOCALE)); setlocale(LC_ALL, ""); bindtextdomain(COMMONDOMAIN, COMMONLOCALEDIR); if (defined("PACKAGE")) { bindtextdomain(PACKAGE, LOCALEDIR); textdomain(PACKAGE); } } ?>