3 * General functions library
5 * @author Roland Haeder <webmaster@shipsimu.org>
7 * @copyright Copyright (c) 2009 - 2017 Cracker Tracker Team
8 * @license GNU GPL 3.0 or any newer version
9 * @link http://www.shipsimu.org
11 * This program is free software: you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation, either version 3 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program. If not, see <http://www.gnu.org/licenses/>.
25 // Implode recursive a multi-dimension array, taken from www.php.net
26 function implode_r (string $glue, array $array, string $array_name = NULL) {
28 while (list($key,$value) = @each($array)) {
29 if (is_array($value)) {
30 // Is an array again, so call recursive
31 $return[] = implode_r($glue, $value, (string) $key);
33 if ($array_name != NULL) {
34 $return[] = $array_name . '[' . (string) $key . ']=' . $value . "\n";
36 $return[] = $key . '=' . $value."\n";
41 // Return resulting array
42 return implode($glue, $return);
45 // Implode a simple array with a 'call-back' to our escaper function
46 function implode_secure (array $array) {
51 foreach ($array as $entry) {
53 if (in_array($entry, array('NOW()'))) {
54 // Add it with non-string glue
55 $return .= $entry . ',';
56 } elseif (empty($entry)) {
57 // Empty strings need no escaping
60 // Secure this string and add it
61 $return .= '"' . crackerTrackerEscapeString($entry) . '",';
66 $return = substr($return, 0, -1);
72 // Load configuration, if found
73 function crackerTrackerLoadConfiguration () {
75 $fqfn = sprintf('%s/config/db_config.php', $GLOBALS['ctracker_base_path']);
77 // Is the file readable?
78 if (!isCrackerTrackerFileFound($fqfn)) {
79 // No config file found
80 die(__FUNCTION__.': No configuration file found.');
87 $GLOBALS['ctracker_header'] = crackerTrackerLoadEmailTemplate('header');
90 // Getter for ctracker_debug_enabled
91 function isCrackerTrackerDebug () {
93 $result = ((isset($GLOBALS['ctracker_debug_enabled'])) && ($GLOBALS['ctracker_debug_enabled'] === true));
96 //* DEBUG: */ error_log('result=' . intval($result));
102 // Determines the real remote address
103 function determineCrackerTrackerRealRemoteAddress () {
105 $address = '0.0.0.0';
107 // Is a proxy in use?
108 if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
110 $address = trim($_SERVER['HTTP_X_FORWARDED_FOR']);
111 } elseif (isset($_SERVER['HTTP_CLIENT_IP'])) {
112 // Yet, another proxy
113 $address = trim($_SERVER['HTTP_CLIENT_IP']);
114 } elseif (isset($_SERVER['REMOTE_ADDR'])) {
115 // The regular address when no proxy was used
116 $address = trim(getenv('REMOTE_ADDR'));
119 if ($address == 'unknown') {
120 // Invalid IP somehow given
121 $address = '0.0.0.0';
122 } elseif (strstr($address, ',')) {
123 // This strips out the real address from proxy output
124 $addressArray = explode(',', $address);
125 $address = $addressArray[0];
132 // Determine if a proxy was used
133 function isCrackerTrackerProxyUsed () {
134 // Check if specific entries are set
135 $proxyUsed = ((isset($_SERVER['HTTP_X_FORWARDED_FOR'])) || (isset($_SERVER['HTTP_CLIENT_IP'])));
141 // Detects the user-agent string
142 function crackerTrackerUserAgent (bool $sanitize = false) {
143 // Default is 'unknown'
146 // Is the entry there?
147 if (isset($_SERVER['HTTP_USER_AGENT'])) {
148 // Then use it securely
149 $ua = crackerTrackerSecureString(urldecode($_SERVER['HTTP_USER_AGENT']));
153 if ($sanitize === true) {
155 $ua = crackerTrackerSanitize($ua);
162 // Detects the script name
163 function crackerTrackerScriptName (bool $sanitize = false) {
168 if (!empty($_SERVER['SCRIPT_NAME'])) {
170 $scriptName = crackerTrackerSecureString($_SERVER['SCRIPT_NAME']);
174 if ($sanitize === true) {
176 $scriptName = crackerTrackerSanitize($scriptName);
183 // Detects the query string
184 function crackerTrackerQueryString (bool $sanitize = false) {
189 if (!empty($_SERVER['QUERY_STRING'])) {
190 // Get string escaped
191 $query = crackerTrackerEscapeString(urldecode($_SERVER['QUERY_STRING']));
192 } elseif (!empty($_SERVER['REQUEST_URI'])) {
193 // Get string escaped
194 $query = crackerTrackerEscapeString(urldecode($_SERVER['REQUEST_URI']));
198 if ((!empty($query)) && ($sanitize === true)) {
200 $query = crackerTrackerSanitize($query);
207 // Detects the server's name
208 function crackerTrackerServerName (bool $sanitize = false) {
213 if (!empty($_SERVER['SERVER_NAME'])) {
215 $serverName = crackerTrackerSecureString($_SERVER['SERVER_NAME']);
219 if ($sanitize === true) {
221 $serverName = crackerTrackerSanitize($serverName);
228 // Detects the referer
229 function crackerTrackerReferer (bool $sanitize = false) {
234 if (!empty($_SERVER['HTTP_REFERER'])) {
235 // Then use it securely
236 $referer = crackerTrackerSecureString(urldecode($_SERVER['HTTP_REFERER']));
240 if ($sanitize === true) {
242 $referer = crackerTrackerSanitize($referer);
249 // Detects request method
250 function crackerTrackerRequestMethod () {
255 if (!empty($_SERVER['REQUEST_METHOD'])) {
257 $method = $_SERVER['REQUEST_METHOD'];
264 // Detects the scripts path
265 function crackerTrackerScriptPath () {
266 // Should always be there!
267 $path = dirname(crackerTrackerScriptName()) . '/';
269 // Return detected path
273 // Detects wether we have SSL
274 function crackerTrackerSecured () {
276 $ssl = ((isset($_SERVER['HTTPS'])) && ($_SERVER['HTTPS'] != 'off'));
282 // Secures a string by escaping it and passing it through strip_tags,htmlentities-chain
283 function crackerTrackerSecureString ($str) {
285 $str = crackerTrackerEscapeString($str);
287 // Then pass it through the functions
288 $str = htmlentities(strip_tags($str), ENT_QUOTES);
294 // Is the file there and readable?
295 function isCrackerTrackerFileFound ($FQFN) {
297 return ((file_exists($FQFN)) && (is_readable($FQFN)));
300 // Loads a given "template" (this is more an include file)
301 function crackerTrackerLoadTemplate ($template) {
302 // Create the full-qualified filename (FQFN)
303 $FQFN = sprintf('%s/libs/templates/%s.tpl.php',
304 $GLOBALS['ctracker_base_path'],
308 // Is this template found?
309 if (isCrackerTrackerFileFound($FQFN)) {
311 crackerTrackerLanguage();
316 // Not found, so die here
321 // Loads a given "template" (this is more an include file)
322 function crackerTrackerLoadLocalizedTemplate ($template) {
323 // Create the full-qualified filename (FQFN)
324 $FQFN = sprintf('%s/libs/templates/%s/%s.tpl.php',
325 $GLOBALS['ctracker_base_path'],
326 getCrackerTrackerLanguage(),
330 // Is this template found?
331 if (isCrackerTrackerFileFound($FQFN)) {
335 // Not found, so die here
340 // Detects the browser's language file and tries to load it, fall-back on english!
341 function crackerTrackerLanguage () {
342 // Default is English
343 $GLOBALS['ctracker_language'] = 'en';
346 // Is the language string there?
347 if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
349 foreach (explode(',', $_SERVER['HTTP_ACCEPT_LANGUAGE']) as $lang) {
350 // Split it again for q=x.x value
351 $langArray = explode(';', $lang);
353 // If there is an entry, we have a weight
354 if ((isset($langArray[1])) && ($langArray[1] > $weight)) {
355 // Use this language/weight instead
356 $GLOBALS['ctracker_language'] = $langArray[0];
357 $weight = $langArray[1];
363 $FQFN = sprintf('%s/libs/language/%s.php',
364 $GLOBALS['ctracker_base_path'],
365 getCrackerTrackerLanguage()
368 // Is it not there, switch to "en"
369 if ((getCrackerTrackerLanguage() != 'en') && (!isCrackerTrackerFileFound($FQFN))) {
370 // English is default!
371 $GLOBALS['ctracker_language'] = 'en';
373 // Construct FQFN again
374 $FQFN = sprintf('%s/libs/language/en.php', $GLOBALS['ctracker_base_path']);
377 // Load the language file
381 // Loads a given email template and passes through $content
382 function crackerTrackerLoadEmailTemplate ($template, array $content = [], $language = NULL) {
384 crackerTrackerLanguage();
387 $FQFN = sprintf('%s/libs/mails/%s/%s.tpl',
388 $GLOBALS['ctracker_base_path'],
389 getCrackerTrackerLanguage($language),
393 // So is the file there?
394 if (isCrackerTrackerFileFound($FQFN)) {
396 $result = 'No result from template ' . $template . '. Please report this at http://forum.shipsimu.org Thank you.';
399 //* DEBUG-DIE: */ die('<pre>$result = "' . crackerTrackerCompileCode(trim(file_get_contents($FQFN))) . '";</pre>');
400 eval('$result = "' . crackerTrackerCompileCode(trim(file_get_contents($FQFN))) . '";');
410 // Getter for message
411 function getCrackerTrackerLocalized ($message) {
413 $output = '!' . $message . '!';
415 // Is the language string there?
416 if (isset($GLOBALS['ctracker_localized'][$message])) {
418 $output = $GLOBALS['ctracker_localized'][$message];
425 // Tries to find a message and outputs it
426 function crackerTrackerOutputLocalized ($message) {
428 print getCrackerTrackerLocalized($message);
431 // Compiles the given code
432 function crackerTrackerCompileCode ($code) {
433 // Find all $content[foo]
434 preg_match_all('/\$(content|GLOBALS)((\[([a-zA-Z0-9-_]+)\])*)/', $code, $matches);
436 // Replace " with {QUOTE}
437 $code = str_replace('"', '{QUOTE}', $code);
440 foreach ($matches[0] as $key=>$match) {
442 if (substr($match, 0, 8) == '$GLOBALS') {
444 $code = str_replace($match, "\" . \$GLOBALS['" . $matches[4][$key] . "'] . \"", $code);
445 } elseif (substr($match, 0, 8) == '$content') {
447 $code = str_replace($match, "\" . \$content['" . $matches[4][$key] . "'] . \"", $code);
455 // "Getter" for language
456 function getCrackerTrackerLanguage ($lang = NULL) {
457 // Default is from browser
458 $language = $GLOBALS['ctracker_language'];
461 if (!is_null($lang)) {
462 // Then use this instead
470 // "Getter" for ticket id
471 function getCrackerTrackerTicketId () {
476 if (isset($GLOBALS['ctracker_last_ticket']['ctracker_ticket'])) {
478 $id = $GLOBALS['ctracker_last_ticket']['ctracker_ticket'];
485 // Sends a cookie to the user that he would not see this security warning again
486 function sendCrackerTrackerCookie () {
488 // @TODO Why can't domain be set to value from crackerTrackerServerName() ?
489 setcookie('ctracker_ticket', getCrackerTrackerTicketId(), (time() + 60*60*24), '/', '', crackerTrackerSecured(), true);
490 $_COOKIE['ctracker_ticket'] = getCrackerTrackerTicketId();
493 // Is the cookie set?
494 function ifCrackerTrackerCookieIsSet () {
495 // Is it set and valid?
496 return ((isset($_COOKIE['ctracker_ticket'])) && ($_COOKIE['ctracker_ticket'] > 0));
499 // Redirects to the same URL
500 function crackerTrackerRedirectSameUrl () {
501 // Construct and redirect to same URL
502 crackerTrackerSendRawRedirect(sprintf('%s://%s%s?%s',
503 (crackerTrackerSecured() ? 'https' : 'http'),
504 crackerTrackerServerName(),
505 crackerTrackerScriptName(),
506 crackerTrackerQueryString()
511 * Send a HTTP redirect to the browser. This function was taken from DokuWiki
512 * (GNU GPL 2; http://www.dokuwiki.org) and modified to fit into this script.
514 * Works arround Microsoft IIS cookie sending bug. Does exit the script.
516 * @link http://support.microsoft.com/kb/q176113/
517 * @author Andreas Gohr <andi@splitbrain.org>
519 function crackerTrackerSendRawRedirect ($url) {
520 // Better remove any data by ctracker
523 // always close the session
524 session_write_close();
526 // Revert entity &
527 $url = str_replace('&', '&', $url);
529 // check if running on IIS < 6 with CGI-PHP
530 if ((isset($_SERVER['SERVER_SOFTWARE'])) && (isset($_SERVER['GATEWAY_INTERFACE'])) &&
531 (strpos($_SERVER['GATEWAY_INTERFACE'],'CGI') !== false) &&
532 (preg_match('|^Microsoft-IIS/(\d)\.\d$|', trim($_SERVER['SERVER_SOFTWARE']), $matches)) &&
534 // Send the IIS header
535 header('Refresh: 0;url=' . $url);
537 // Send generic header
538 header('Location: ' . $url);
543 // Removes all ctracker-related data from global space
544 function unsetCtrackerData () {
546 //* DEBUG: */ error_log(__FUNCTION__ . ': CALLED!');
548 // Unset all ctracker data
550 'ctracker_base_path',
555 'ctracker_debug_enabled',
557 'ctracker_whitelist',
558 'ctracker_get_blacklist',
559 'ctracker_post_blacklist',
561 'ctracker_post_track',
562 'ctracker_checked_get',
563 'ctracker_checked_post',
564 'ctracker_checked_ua',
566 'ctracker_last_result',
570 'ctracker_localized',
572 'ctracker_blocked_methods',
575 unset($GLOBALS[$key]);
580 function crackerTrackerSanitize ($str) {
581 return str_replace(array('//', '/./'), array('/', '/'), $str);
584 function crackerTrackerIsConsole () {
585 return (php_sapi_name() == 'cli');
588 function ctrackerAntiSpamField () {
590 $fields = ctrackerGetAntiSpamFields();
593 $fieldName = $fields[mt_rand(0, count($fields) - 1)];
599 function ctrackerGetAntiSpamFields () {
609 function ifCtrackerTrackerAntiSpamFieldGiven () {
610 // Is request method POST?
611 if (crackerTrackerRequestMethod() != 'POST') {
616 // Walk through all fields
617 foreach (ctrackerGetAntiSpamFields() as $fieldName) {
619 if (in_array($fieldName, $_POST) && !empty($_POST[$fieldName])) {