Advertisement
Guest User

php

a guest
Dec 29th, 2015
411
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
PHP 46.49 KB | None | 0 0
  1. <?php
  2.    
  3. /*
  4.     This program is free software; you can redistribute it and/or
  5.     modify it under the terms of the GNU General Public License
  6.     as published by the Free Software Foundation; either version 2
  7.     of the License, or (at your option) any later version.
  8.  
  9.     This program is distributed in the hope that it will be useful,
  10.     but WITHOUT ANY WARRANTY; without even the implied warranty of
  11.     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12.     GNU General Public License for more details.
  13.  
  14.     More about this license: LICENCE.html
  15. */ 
  16.    
  17.     define('QA_VERSION', '1.9.9.2'); // also used as suffix for .js and .css requests
  18.     define('QA_BUILD_DATE', '2015-11-19');
  19.  
  20. //  Execution section of this file - remainder contains function definitions
  21.  
  22.     qa_initialize_php();
  23.     qa_initialize_constants_1();
  24.  
  25.     if (defined('QA_WORDPRESS_LOAD_FILE')) // if relevant, load WordPress integration in global scope
  26.         require_once QA_WORDPRESS_LOAD_FILE;
  27.  
  28.     qa_initialize_constants_2();
  29.     qa_initialize_modularity();
  30.     qa_register_core_modules();
  31.     qa_load_plugin_files();
  32.     qa_load_override_files();
  33.  
  34.     require_once QA_INCLUDE_DIR.'king-db.php';
  35.  
  36.     qa_db_allow_connect();
  37.    
  38.  
  39. //  Version comparison functions
  40.  
  41.     function qa_version_to_float($version)
  42. /*
  43.     Converts the $version string (e.g. 1.6.2.2) to a floating point that can be used for greater/lesser comparisons
  44.     (PHP's version_compare() function is not quite suitable for our needs)
  45. */
  46.     {
  47.         $value=0.0;
  48.  
  49.         if (preg_match('/[0-9\.]+/', $version, $matches)) {
  50.             $parts=explode('.', $matches[0]);
  51.             $units=1.0;
  52.            
  53.             foreach ($parts as $part) {
  54.                 $value+=min($part, 999)*$units;
  55.                 $units/=1000;
  56.             }
  57.         }
  58.  
  59.         return $value;
  60.     }
  61.    
  62.    
  63.     function qa_qa_version_below($version)
  64. /*
  65.     Returns true if the current Q2A version is lower than $version, if both are valid version strings for qa_version_to_float()
  66. */
  67.     {
  68.         $minqa=qa_version_to_float($version);
  69.         $thisqa=qa_version_to_float(QA_VERSION);
  70.        
  71.         return $minqa && $thisqa && ($thisqa<$minqa);
  72.     }
  73.    
  74.    
  75.     function qa_php_version_below($version)
  76. /*
  77.     Returns true if the current PHP version is lower than $version, if both are valid version strings for qa_version_to_float()
  78. */
  79.     {
  80.         $minphp=qa_version_to_float($version);
  81.         $thisphp=qa_version_to_float(phpversion());
  82.        
  83.         return $minphp && $thisphp && ($thisphp<$minphp);
  84.     }
  85.    
  86.  
  87. //  Initialization functions called above
  88.  
  89.     function qa_initialize_php()
  90. /*
  91.     Set up and verify the PHP environment for Q2A, including unregistering globals if necessary
  92. */
  93.     {
  94.         if (qa_php_version_below('4.3'))
  95.             qa_fatal_error('This requires PHP 4.3 or later');
  96.    
  97.         error_reporting(E_ALL); // be ultra-strict about error checking
  98.        
  99.         @ini_set('magic_quotes_runtime', 0);
  100.        
  101.         @setlocale(LC_CTYPE, 'C'); // prevent strtolower() et al affecting non-ASCII characters (appears important for IIS)
  102.        
  103.         if (function_exists('date_default_timezone_set') && function_exists('date_default_timezone_get'))
  104.             @date_default_timezone_set(@date_default_timezone_get()); // prevent PHP notices where default timezone not set
  105.            
  106.         if (ini_get('register_globals')) {
  107.             $checkarrays=array('_ENV', '_GET', '_POST', '_COOKIE', '_SERVER', '_FILES', '_REQUEST', '_SESSION'); // unregister globals if they're registered
  108.             $keyprotect=array_flip(array_merge($checkarrays, array('GLOBALS')));
  109.            
  110.             foreach ($checkarrays as $checkarray)
  111.                 if ( isset(${$checkarray}) && is_array(${$checkarray}) )
  112.                     foreach (${$checkarray} as $checkkey => $checkvalue)
  113.                         if (isset($keyprotect[$checkkey]))
  114.                             qa_fatal_error('My superglobals are not for overriding');
  115.                         else
  116.                             unset($GLOBALS[$checkkey]);
  117.         }
  118.     }
  119.    
  120.    
  121.     function qa_initialize_constants_1()
  122. /*
  123.     First stage of setting up Q2A constants, before (if necessary) loading WordPress integration
  124. */
  125.     {
  126.         global $qa_request_map;
  127.        
  128.         define('QA_CATEGORY_DEPTH', 4); // you can't change this number!
  129.  
  130.         if (!defined('QA_BASE_DIR'))
  131.             define('QA_BASE_DIR', dirname(dirname(__FILE__)).'/'); // try out best if not set in index.php or king-index.php - won't work with symbolic links
  132.            
  133.         define('QA_EXTERNAL_DIR', QA_BASE_DIR.'king-external/');
  134.         define('QA_INCLUDE_DIR', QA_BASE_DIR.'king-include/');
  135.         define('QA_LANG_DIR', QA_BASE_DIR.'king-lang/');
  136.         define('QA_THEME_DIR', QA_BASE_DIR.'king-theme/');
  137.         define('QA_PLUGIN_DIR', QA_BASE_DIR.'king-plugin/');
  138.  
  139.         if (!file_exists(QA_BASE_DIR.'king-config.php'))
  140.             qa_fatal_error('The config file could not be found. Please read the instructions in king-config-example.php.');
  141.        
  142.         require_once QA_BASE_DIR.'king-config.php';
  143.        
  144.         $qa_request_map=is_array(@$QA_CONST_PATH_MAP) ? $QA_CONST_PATH_MAP : array();
  145.  
  146.         if (defined('QA_WORDPRESS_INTEGRATE_PATH') && strlen(QA_WORDPRESS_INTEGRATE_PATH)) {
  147.             define('QA_FINAL_WORDPRESS_INTEGRATE_PATH', QA_WORDPRESS_INTEGRATE_PATH.((substr(QA_WORDPRESS_INTEGRATE_PATH, -1)=='/') ? '' : '/'));
  148.             define('QA_WORDPRESS_LOAD_FILE', QA_FINAL_WORDPRESS_INTEGRATE_PATH.'wp-load.php');
  149.    
  150.             if (!is_readable(QA_WORDPRESS_LOAD_FILE))
  151.                 qa_fatal_error('Could not find wp-load.php file for WordPress integration - please check QA_WORDPRESS_INTEGRATE_PATH in king-config.php');
  152.         }
  153.     }
  154.    
  155.    
  156.     function qa_initialize_constants_2()
  157. /*
  158.     Second stage of setting up Q2A constants, after (if necessary) loading WordPress integration
  159. */
  160.     {
  161.    
  162.     //  Default values if not set in king-config.php
  163.    
  164.         @define('QA_COOKIE_DOMAIN', '');
  165.         @define('QA_HTML_COMPRESSION', true);
  166.         @define('QA_MAX_LIMIT_START', 19999);
  167.         @define('QA_IGNORED_WORDS_FREQ', 10000);
  168.         @define('QA_ALLOW_UNINDEXED_QUERIES', false);
  169.         @define('QA_OPTIMIZE_LOCAL_DB', false);
  170.         @define('QA_OPTIMIZE_DISTANT_DB', false);
  171.         @define('QA_PERSISTENT_CONN_DB', false);
  172.         @define('QA_DEBUG_PERFORMANCE', false);
  173.        
  174.     //  Start performance monitoring
  175.    
  176.         if (QA_DEBUG_PERFORMANCE) {
  177.             require_once 'king-util-debug.php';
  178.             qa_usage_init();
  179.         }
  180.        
  181.     //  More for WordPress integration
  182.        
  183.         if (defined('QA_FINAL_WORDPRESS_INTEGRATE_PATH')) {
  184.             define('QA_FINAL_MYSQL_HOSTNAME', DB_HOST);
  185.             define('QA_FINAL_MYSQL_USERNAME', DB_USER);
  186.             define('QA_FINAL_MYSQL_PASSWORD', DB_PASSWORD);
  187.             define('QA_FINAL_MYSQL_DATABASE', DB_NAME);
  188.             define('QA_FINAL_EXTERNAL_USERS', true);
  189.            
  190.             // Undo WordPress's addition of magic quotes to various things (leave $_COOKIE as is since WP code might need that)
  191.  
  192.             function qa_undo_wordpress_quoting($param, $isget)
  193.             {
  194.                 if (is_array($param)) { //
  195.                     foreach ($param as $key => $value)
  196.                         $param[$key]=qa_undo_wordpress_quoting($value, $isget);
  197.                    
  198.                 } else {
  199.                     $param=stripslashes($param);
  200.                     if ($isget)
  201.                         $param=strtr($param, array('\\\'' => '\'', '\"' => '"')); // also compensate for WordPress's .htaccess file
  202.                 }
  203.                
  204.                 return $param;
  205.             }
  206.            
  207.             $_GET=qa_undo_wordpress_quoting($_GET, true);
  208.             $_POST=qa_undo_wordpress_quoting($_POST, false);
  209.             $_SERVER['PHP_SELF']=stripslashes($_SERVER['PHP_SELF']);
  210.        
  211.         } else {
  212.             define('QA_FINAL_MYSQL_HOSTNAME', QA_MYSQL_HOSTNAME);
  213.             define('QA_FINAL_MYSQL_USERNAME', QA_MYSQL_USERNAME);
  214.             define('QA_FINAL_MYSQL_PASSWORD', QA_MYSQL_PASSWORD);
  215.             define('QA_FINAL_MYSQL_DATABASE', QA_MYSQL_DATABASE);
  216.             define('QA_FINAL_EXTERNAL_USERS', QA_EXTERNAL_USERS);
  217.         }
  218.        
  219.     //  Possible URL schemes for Q2A and the string used for url scheme testing
  220.  
  221.         define('QA_URL_FORMAT_INDEX', 0);  // http://...../index.php/123/why-is-the-sky-blue
  222.         define('QA_URL_FORMAT_NEAT', 1);   // http://...../123/why-is-the-sky-blue [requires .htaccess]
  223.         define('QA_URL_FORMAT_PARAM', 3);  // http://...../?qa=123/why-is-the-sky-blue
  224.         define('QA_URL_FORMAT_PARAMS', 4); // http://...../?qa=123&qa_1=why-is-the-sky-blue
  225.         define('QA_URL_FORMAT_SAFEST', 5); // http://...../index.php?qa=123&qa_1=why-is-the-sky-blue
  226.  
  227.         define('QA_URL_TEST_STRING', '$&-_~#%\\@^*()=!()][`\';:|".{},<>?# π§½Жש'); // tests escaping, spaces, quote slashing and unicode - but not + and /
  228.     }
  229.  
  230.  
  231.     function qa_initialize_modularity()
  232. /*
  233.     Gets everything ready to start using modules, layers and overrides
  234. */
  235.     {
  236.         global $qa_modules, $qa_layers, $qa_override_files, $qa_overrides, $qa_direct;
  237.  
  238.         $qa_modules=array();
  239.         $qa_layers=array();
  240.         $qa_override_files=array();
  241.         $qa_overrides=array();
  242.         $qa_direct=array();
  243.     }
  244.    
  245.    
  246.     function qa_register_core_modules()
  247. /*
  248.     Register all modules that come as part of the Q2A core (as opposed to plugins)
  249. */
  250.     {
  251.         qa_register_module('filter', 'king-filter-basic.php', 'qa_filter_basic', '');
  252.         qa_register_module('editor', 'king-editor-basic.php', 'qa_editor_basic', '');
  253.         qa_register_module('viewer', 'king-viewer-basic.php', 'qa_viewer_basic', '');
  254.         qa_register_module('event', 'king-event-limits.php', 'qa_event_limits', 'Q2A Event Limits');
  255.         qa_register_module('event', 'king-event-notify.php', 'qa_event_notify', 'Q2A Event Notify');
  256.         qa_register_module('event', 'king-event-updates.php', 'qa_event_updates', 'Q2A Event Updates');
  257.         qa_register_module('search', 'king-search-basic.php', 'qa_search_basic', '');
  258.         qa_register_module('widget', 'king-widget-related-qs.php', 'qa_related_qs', 'Related Media');
  259.     }
  260.    
  261.    
  262.     function qa_load_plugin_files()
  263. /*
  264.     Load all the king-plugin.php files from plugins that are compatible with this version of Q2A
  265. */
  266.     {
  267.         global $qa_plugin_directory, $qa_plugin_urltoroot;
  268.        
  269.         $pluginfiles=glob(QA_PLUGIN_DIR.'*/king-plugin.php');
  270.  
  271.         foreach ($pluginfiles as $pluginfile)
  272.             if (file_exists($pluginfile)) {
  273.                 $contents=file_get_contents($pluginfile);
  274.                
  275.                 if (preg_match('/Plugin[ \t]*Minimum[ \t]*KingMedia[ \t]*Version\:[ \t]*([0-9\.]+)\s/i', $contents, $matches))
  276.                     if (qa_qa_version_below($matches[1]))
  277.                         continue; // skip plugin which requires a later version of Q2A
  278.                
  279.                 if (preg_match('/Plugin[ \t]*Minimum[ \t]*PHP[ \t]*Version\:[ \t]*([0-9\.]+)\s/i', $contents, $matches))
  280.                     if (qa_php_version_below($matches[1]))
  281.                         continue; // skip plugin which requires a later version of PHP
  282.                
  283.                 $qa_plugin_directory=dirname($pluginfile).'/';
  284.                 $qa_plugin_urltoroot=substr($qa_plugin_directory, strlen(QA_BASE_DIR));
  285.                
  286.                 require_once $pluginfile;
  287.                
  288.                 $qa_plugin_directory=null;
  289.                 $qa_plugin_urltoroot=null;
  290.             }
  291.     }
  292.  
  293.  
  294.     function qa_load_override_files()
  295. /*
  296.     Apply all the function overrides in override files that have been registered by plugins
  297. */
  298.     {
  299.         global $qa_override_files, $qa_overrides;
  300.        
  301.         $functionindex=array();
  302.  
  303.         foreach ($qa_override_files as $index => $override) {
  304.             $filename=$override['directory'].$override['include'];
  305.             $functionsphp=file_get_contents($filename);
  306.            
  307.             preg_match_all('/\Wfunction\s+(qa_[a-z_]+)\s*\(/im', $functionsphp, $rawmatches, PREG_PATTERN_ORDER|PREG_OFFSET_CAPTURE);
  308.            
  309.             $reversematches=array_reverse($rawmatches[1], true); // reverse so offsets remain correct as we step through
  310.             $postreplace=array();
  311.             $suffix='_in_'.preg_replace('/[^A-Za-z0-9_]+/', '_', basename($override['include']));
  312.                 // include file name in defined function names to make debugging easier if there is an error
  313.            
  314.             foreach ($reversematches as $rawmatch) {
  315.                 $function=strtolower($rawmatch[0]);
  316.                 $position=$rawmatch[1];
  317.  
  318.                 if (isset($qa_overrides[$function]))
  319.                     $postreplace[$function.'_base']=$qa_overrides[$function];
  320.                    
  321.                 $newname=$function.'_override_'.(@++$functionindex[$function]).$suffix;
  322.                 $functionsphp=substr_replace($functionsphp, $newname, $position, strlen($function));
  323.                 $qa_overrides[$function]=$newname;
  324.             }
  325.            
  326.             foreach ($postreplace as $oldname => $newname)
  327.                 if (preg_match_all('/\W('.preg_quote($oldname).')\s*\(/im', $functionsphp, $matches, PREG_PATTERN_ORDER|PREG_OFFSET_CAPTURE)) {
  328.                     $searchmatches=array_reverse($matches[1]);
  329.                     foreach ($searchmatches as $searchmatch)
  330.                         $functionsphp=substr_replace($functionsphp, $newname, $searchmatch[1], strlen($searchmatch[0]));
  331.                 }
  332.            
  333.         //  echo '<pre style="text-align:left;">'.htmlspecialchars($functionsphp).'</pre>'; // to debug munged code
  334.            
  335.             qa_eval_from_file($functionsphp, $filename);
  336.         }
  337.     }
  338.    
  339.  
  340. //  Functions for registering different varieties of Q2A modularity
  341.    
  342.     function qa_register_module($type, $include, $class, $name, $directory=QA_INCLUDE_DIR, $urltoroot=null)
  343. /*
  344.     Register a module of $type named $name, whose class named $class is defined in file $include (or null if no include necessary)
  345.     If this module comes from a plugin, pass in the local plugin $directory and the $urltoroot relative url for that directory
  346. */
  347.     {
  348.         global $qa_modules;
  349.        
  350.         $previous=@$qa_modules[$type][$name];
  351.        
  352.         if (isset($previous))
  353.             qa_fatal_error('A '.$type.' module named '.$name.' already exists. Please check there are no duplicate plugins. '.
  354.                 "\n\nModule 1: ".$previous['directory'].$previous['include']."\nModule 2: ".$directory.$include);
  355.        
  356.         $qa_modules[$type][$name]=array(
  357.             'directory' => $directory,
  358.             'urltoroot' => $urltoroot,
  359.             'include' => $include,
  360.             'class' => $class,
  361.         );
  362.     }
  363.    
  364.    
  365.     function qa_register_layer($include, $name, $directory=QA_INCLUDE_DIR, $urltoroot=null)
  366. /*
  367.     Register a layer named $name, defined in file $include. If this layer comes from a plugin (as all currently do),
  368.     pass in the local plugin $directory and the $urltoroot relative url for that directory
  369. */
  370.     {
  371.         global $qa_layers;
  372.        
  373.         $previous=@$qa_layers[$name];
  374.        
  375.         if (isset($previous))
  376.             qa_fatal_error('A layer named '.$name.' already exists. Please check there are no duplicate plugins. '.
  377.                 "\n\nLayer 1: ".$previous['directory'].$previous['include']."\nLayer 2: ".$directory.$include);
  378.            
  379.         $qa_layers[$name]=array(
  380.             'directory' => $directory,
  381.             'urltoroot' => $urltoroot,
  382.             'include' => $include,
  383.         );
  384.     }
  385.    
  386.    
  387.     function qa_register_overrides($include, $directory=QA_INCLUDE_DIR, $urltoroot=null)
  388. /*
  389.     Register a file $include containing override functions. If this file comes from a plugin (as all currently do),
  390.     pass in the local plugin $directory and the $urltoroot relative url for that directory
  391. */
  392.     {
  393.         global $qa_override_files;
  394.        
  395.         $qa_override_files[]=array(
  396.             'directory' => $directory,
  397.             'urltoroot' => $urltoroot,
  398.             'include' => $include
  399.         );
  400.     }
  401.    
  402.    
  403.     function qa_register_phrases($pattern, $name)
  404. /*
  405.     Register a set of language phrases, which should be accessed by the prefix $name/ in the qa_lang_*() functions.
  406.     Pass in the $pattern representing the PHP files that define these phrases, where * in the pattern is replaced with
  407.     the language code (e.g. 'fr') and/or 'default'. These files should be formatted like Q2A's king-lang-*.php files.
  408. */
  409.     {
  410.         global $qa_lang_file_pattern;
  411.        
  412.         if (file_exists(QA_INCLUDE_DIR.'king-lang-'.$name.'.php'))
  413.             qa_fatal_error('The name "'.$name.'" for phrases is reserved and cannot be used by plugins.'."\n\nPhrases: ".$pattern);
  414.  
  415.         if (isset($qa_lang_file_pattern[$name]))
  416.             qa_fatal_error('A set of phrases named '.$name.' already exists. Please check there are no duplicate plugins. '.
  417.                 "\n\nPhrases 1: ".$qa_lang_file_pattern[$name]."\nPhrases 2: ".$pattern);
  418.            
  419.         $qa_lang_file_pattern[$name]=$pattern;
  420.     }
  421.  
  422.  
  423. //  Function for registering varieties of Q2A modularity, which are (only) called from king-plugin.php files   
  424.  
  425.     function qa_register_plugin_module($type, $include, $class, $name)
  426. /*
  427.     Register a plugin module of $type named $name, whose class named $class is defined in file $include (or null if no include necessary)
  428.     This function relies on some global variable values and can only be called from a plugin's king-plugin.php file
  429. */
  430.     {
  431.         global $qa_plugin_directory, $qa_plugin_urltoroot;
  432.        
  433.         if (empty($qa_plugin_directory) || empty($qa_plugin_urltoroot))
  434.             qa_fatal_error('qa_register_plugin_module() can only be called from a plugin king-plugin.php file');
  435.  
  436.         qa_register_module($type, $include, $class, $name, $qa_plugin_directory, $qa_plugin_urltoroot);
  437.     }
  438.  
  439.    
  440.     function qa_register_plugin_layer($include, $name)
  441. /*
  442.     Register a plugin layer named $name, defined in file $include. Can only be called from a plugin's king-plugin.php file
  443. */
  444.     {
  445.         global $qa_plugin_directory, $qa_plugin_urltoroot;
  446.        
  447.         if (empty($qa_plugin_directory) || empty($qa_plugin_urltoroot))
  448.             qa_fatal_error('qa_register_plugin_layer() can only be called from a plugin king-plugin.php file');
  449.  
  450.         qa_register_layer($include, $name, $qa_plugin_directory, $qa_plugin_urltoroot);
  451.     }
  452.    
  453.    
  454.     function qa_register_plugin_overrides($include)
  455. /*
  456.     Register a plugin file $include containing override functions. Can only be called from a plugin's king-plugin.php file
  457. */
  458.     {
  459.         global $qa_plugin_directory, $qa_plugin_urltoroot;
  460.  
  461.         if (empty($qa_plugin_directory) || empty($qa_plugin_urltoroot))
  462.             qa_fatal_error('qa_register_plugin_overrides() can only be called from a plugin king-plugin.php file');
  463.            
  464.         qa_register_overrides($include, $qa_plugin_directory, $qa_plugin_urltoroot);
  465.     }
  466.    
  467.    
  468.     function qa_register_plugin_phrases($pattern, $name)
  469. /*
  470.     Register a file name $pattern within a plugin directory containing language phrases accessed by the prefix $name
  471. */
  472.     {
  473.         global $qa_plugin_directory, $qa_plugin_urltoroot;
  474.        
  475.         if (empty($qa_plugin_directory) || empty($qa_plugin_urltoroot))
  476.             qa_fatal_error('qa_register_plugin_phrases() can only be called from a plugin king-plugin.php file');
  477.  
  478.         qa_register_phrases($qa_plugin_directory.$pattern, $name);
  479.     }
  480.    
  481.    
  482. //  Low-level functions used throughout Q2A
  483.  
  484.     function qa_eval_from_file($eval, $filename)
  485. /*
  486.     Calls eval() on the PHP code in $eval which came from the file $filename. It supplements PHP's regular error reporting by
  487.     displaying/logging (as appropriate) the original source filename, if an error occurred when evaluating the code.
  488. */
  489.     {
  490.         // could also use ini_set('error_append_string') but apparently it doesn't work for errors logged on disk
  491.        
  492.         global $php_errormsg;
  493.        
  494.         $oldtrackerrors=@ini_set('track_errors', 1);
  495.         $php_errormsg=null;
  496.        
  497.         eval('?'.'>'.$eval);
  498.        
  499.         if (strlen($php_errormsg)) {
  500.             switch (strtolower(@ini_get('display_errors'))) {
  501.                 case 'on': case '1': case 'yes': case 'true': case 'stdout': case 'stderr':
  502.                     echo ' of '.qa_html($filename)."\n";
  503.                     break;
  504.             }
  505.  
  506.             @error_log('PHP KingMedia more info: '.$php_errormsg." in eval()'d code from ".qa_html($filename));
  507.         }
  508.        
  509.         @ini_set('track_errors', $oldtrackerrors);
  510.     }
  511.    
  512.    
  513.     function qa_call($function, $args)
  514. /*
  515.     Call $function with the arguments in the $args array (doesn't work with call-by-reference functions)
  516. */
  517.     {
  518.         switch (count($args)) { // call_user_func_array(...) is very slow, so we break out most cases
  519.             case 0: return $function();
  520.             case 1: return $function($args[0]);
  521.             case 2: return $function($args[0], $args[1]);
  522.             case 3: return $function($args[0], $args[1], $args[2]);
  523.             case 4: return $function($args[0], $args[1], $args[2], $args[3]);
  524.             case 5: return $function($args[0], $args[1], $args[2], $args[3], $args[4]);
  525.         }
  526.        
  527.         return call_user_func_array($function, $args);
  528.     }
  529.  
  530.    
  531.     function qa_to_override($function)
  532. /*
  533.     If $function has been overridden by a plugin override, return the name of the overriding function, otherwise return
  534.     null. But if the function is being called with the _base suffix, any override will be bypassed due to $qa_direct
  535. */
  536.     {
  537.         global $qa_overrides, $qa_direct;
  538.        
  539.         if (strpos($function, '_override_')!==false)
  540.             qa_fatal_error('Override functions should not be calling qa_to_override()!');
  541.        
  542.         if (isset($qa_overrides[$function])) {
  543.             if (@$qa_direct[$function])
  544.                 unset($qa_direct[$function]); // bypass the override just this once
  545.             else
  546.                 return $qa_overrides[$function];
  547.         }
  548.        
  549.         return null;
  550.     }
  551.    
  552.    
  553.     function qa_call_override($function, $args)
  554. /*
  555.     Call the function which immediately overrides $function with the arguments in the $args array
  556. */
  557.     {
  558.         global $qa_overrides;
  559.        
  560.         if (strpos($function, '_override_')!==false)
  561.             qa_fatal_error('Override functions should not be calling qa_call_override()!');
  562.        
  563.         if (!function_exists($function.'_base')) // define the base function the first time that it's needed
  564.             eval('function '.$function.'_base() { global $qa_direct; $qa_direct[\''.$function.'\']=true; $args=func_get_args(); return qa_call(\''.$function.'\', $args); }');
  565.        
  566.         return qa_call($qa_overrides[$function], $args);
  567.     }
  568.    
  569.    
  570.     function qa_exit($reason=null)
  571. /*
  572.     Exit PHP immediately after reporting a shutdown with $reason to any installed process modules
  573. */
  574.     {
  575.         qa_report_process_stage('shutdown', $reason);
  576.         exit;
  577.     }
  578.  
  579.  
  580.     function qa_fatal_error($message)
  581. /*
  582.     Display $message in the browser, write it to server error log, and then stop abruptly
  583. */
  584.     {
  585.         if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
  586.        
  587.         echo 'KingMedia fatal error:<p><font color="red">'.qa_html($message, true).'</font></p>';
  588.         @error_log('PHP KingMedia fatal error: '.$message);
  589.         echo '<p>Stack trace:<p>';
  590.  
  591.         $backtrace=array_reverse(array_slice(debug_backtrace(), 1));
  592.         foreach ($backtrace as $trace)
  593.             echo '<font color="#'.((strpos(@$trace['file'], '/king-plugin/')!==false) ? 'f00' : '999').'">'.
  594.                 qa_html(@$trace['function'].'() in '.basename(@$trace['file']).':'.@$trace['line']).'</font><br>'; 
  595.        
  596.         qa_exit('error');
  597.     }
  598.    
  599.  
  600. //  Functions for listing, loading and getting info on modules
  601.  
  602.     function qa_list_module_types()
  603. /*
  604.     Return an array of all the module types for which at least one module has been registered
  605. */
  606.     {
  607.         global $qa_modules;
  608.        
  609.         return array_keys($qa_modules);
  610.     }
  611.  
  612.    
  613.     function qa_list_modules($type)
  614. /*
  615.     Return a list of names of registered modules of $type
  616. */
  617.     {
  618.         global $qa_modules;
  619.        
  620.         return is_array(@$qa_modules[$type]) ? array_keys($qa_modules[$type]) : array();
  621.     }
  622.    
  623.    
  624.     function qa_get_module_info($type, $name)
  625. /*
  626.     Return an array containing information about the module of $type named $name
  627. */
  628.     {
  629.         global $qa_modules;
  630.         return @$qa_modules[$type][$name];
  631.     }
  632.    
  633.    
  634.     function qa_load_module($type, $name)
  635. /*
  636.     Return an instantiated class for module of $type named $name, whose functions can be called, or null if it doesn't exist
  637. */
  638.     {
  639.         global $qa_modules;
  640.        
  641.         $module=@$qa_modules[$type][$name];
  642.        
  643.         if (is_array($module)) {
  644.             if (isset($module['object']))
  645.                 return $module['object'];
  646.            
  647.             if (strlen(@$module['include']))
  648.                 require_once $module['directory'].$module['include'];
  649.            
  650.             if (strlen(@$module['class'])) {
  651.                 $object=new $module['class'];
  652.                
  653.                 if (method_exists($object, 'load_module'))
  654.                     $object->load_module($module['directory'], qa_path_to_root().$module['urltoroot'], $type, $name);
  655.                
  656.                 $qa_modules[$type][$name]['object']=$object;
  657.                 return $object;
  658.             }
  659.         }
  660.        
  661.         return null;
  662.     }
  663.    
  664.    
  665.     function qa_load_modules_with($type, $method)
  666. /*
  667.     Return an array of instantiated clases for modules of $type which have defined $method
  668.     (other modules of that type are also loaded but not included in the returned array)
  669. */
  670.     {
  671.         $modules=array();
  672.        
  673.         $trynames=qa_list_modules($type);
  674.        
  675.         foreach ($trynames as $tryname) {
  676.             $module=qa_load_module($type, $tryname);
  677.            
  678.             if (method_exists($module, $method))
  679.                 $modules[$tryname]=$module;
  680.         }
  681.        
  682.         return $modules;
  683.     }
  684.    
  685.    
  686. //  HTML and Javascript escaping and sanitization
  687.  
  688.     function qa_html($string, $multiline=false)
  689. /*
  690.     Return HTML representation of $string, work well with blocks of text if $multiline is true
  691. */
  692.     {
  693.         $html=htmlspecialchars((string)$string);
  694.        
  695.         if ($multiline) {
  696.             $html=preg_replace('/\r\n?/', "\n", $html);
  697.             $html=preg_replace('/(?<=\s) /', '&nbsp;', $html);
  698.             $html=str_replace("\t", '&nbsp; &nbsp; ', $html);
  699.             $html=nl2br($html);
  700.         }
  701.        
  702.         return $html;
  703.     }
  704.  
  705.    
  706.     function qa_sanitize_html($html, $linksnewwindow=false, $storage=false)
  707. /*
  708.     Return $html after ensuring it is safe, i.e. removing Javascripts and the like - uses htmLawed library
  709.     Links open in a new window if $linksnewwindow is true. Set $storage to true if sanitization is for
  710.     storing in the database, rather than immediate display to user - some think this should be less strict.
  711. */
  712.     {
  713.         if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
  714.        
  715.         require_once 'king-htmLawed.php';
  716.        
  717.         global $qa_sanitize_html_newwindow;
  718.        
  719.         $qa_sanitize_html_newwindow=$linksnewwindow;
  720.        
  721.         $safe=htmLawed($html, array(
  722.             'safe' => 1,
  723.             'elements' => '*+embed+object-form',
  724.             'schemes' => 'href: aim, feed, file, ftp, gopher, http, https, irc, mailto, news, nntp, sftp, ssh, telnet; *:file, http, https; style: !; classid:clsid',
  725.             'keep_bad' => 0,
  726.             'anti_link_spam' => array('/.*/', ''),
  727.             'hook_tag' => 'qa_sanitize_html_hook_tag',
  728.         ));
  729.        
  730.         return $safe;
  731.     }
  732.    
  733.    
  734.     function qa_sanitize_html_hook_tag($element, $attributes=null)
  735. /*
  736.     htmLawed hook function used to process tags in qa_sanitize_html(...)
  737. */
  738.     {
  739.         global $qa_sanitize_html_newwindow;
  740.  
  741.         if (!isset($attributes)) // it's a closing tag
  742.             return '</'.$element.'>';
  743.        
  744.         if ( ($element=='param') && (trim(strtolower(@$attributes['name']))=='allowscriptaccess') )
  745.             $attributes['name']='allowscriptaccess_denied';
  746.            
  747.         if ($element=='embed')
  748.             unset($attributes['allowscriptaccess']);
  749.            
  750.         if (($element=='a') && isset($attributes['href']) && $qa_sanitize_html_newwindow)
  751.             $attributes['target']='_blank';
  752.        
  753.         $html='<'.$element;
  754.         foreach ($attributes as $key => $value)
  755.             $html.=' '.$key.'="'.$value.'"';
  756.            
  757.         return $html.'>';
  758.     }
  759.    
  760.    
  761.     function qa_xml($string)
  762. /*
  763.     Return XML representation of $string, which is similar to HTML but ASCII control characters are also disallowed
  764. */
  765.     {
  766.         return htmlspecialchars(preg_replace('/[\x00-\x08\x0B\x0C\x0E-\x1F]/', '', (string)$string));
  767.     }
  768.    
  769.    
  770.     function qa_js($value, $forcequotes=false)
  771. /*
  772.     Return JavaScript representation of $value, putting in quotes if non-numeric or if $forcequotes is true
  773. */
  774.     {
  775.         if (is_numeric($value) && !$forcequotes)
  776.             return $value;
  777.         else
  778.             return "'".strtr($value, array(
  779.                 "'" => "\\'",
  780.                 '/' => '\\/',
  781.                 '\\' => '\\\\',
  782.                 "\n" => "\\n",
  783.                 "\r" => "\\n",
  784.             ))."'";
  785.     }
  786.  
  787.  
  788. //  Finding out more about the current request
  789.    
  790.     function qa_set_request($request, $relativeroot, $usedformat=null)
  791. /*
  792.     Inform Q2A that the current request is $request (slash-separated, independent of the url scheme chosen),
  793.     that the relative path to the Q2A root apperas to be $relativeroot, and the url scheme appears to be $usedformat
  794. */
  795.     {
  796.         if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
  797.        
  798.         global $qa_request, $qa_root_url_relative, $qa_used_url_format;
  799.        
  800.         $qa_request=$request;
  801.         $qa_root_url_relative=$relativeroot;
  802.         $qa_used_url_format=$usedformat;
  803.     }
  804.    
  805.    
  806.     function qa_request()
  807. /*
  808.     Returns the current Q2A request (slash-separated, independent of the url scheme chosen)
  809. */
  810.     {
  811.         if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
  812.        
  813.         global $qa_request;
  814.         return $qa_request;
  815.     }
  816.    
  817.    
  818.     function qa_request_part($part)
  819. /*
  820.     Returns the indexed $part (as separated by slashes) of the current Q2A request, or null if it doesn't exist
  821. */
  822.     {
  823.         $parts=explode('/', qa_request());
  824.         return @$parts[$part];
  825.     }
  826.    
  827.    
  828.     function qa_request_parts($start=0)
  829. /*
  830.     Returns an array of parts (as separated by slashes) of the current Q2A request, starting at part $start
  831. */
  832.     {
  833.         return array_slice(explode('/', qa_request()), $start);
  834.     }
  835.    
  836.    
  837.     function qa_gpc_to_string($string)
  838. /*
  839.     Return string for incoming GET/POST/COOKIE value, stripping slashes if appropriate
  840. */
  841.     {
  842.         if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
  843.        
  844.         return get_magic_quotes_gpc() ? stripslashes($string) : $string;
  845.     }
  846.    
  847.  
  848.     function qa_string_to_gpc($string)
  849. /*
  850.     Return string with slashes added, if appropriate for later removal by qa_gpc_to_string()
  851. */
  852.     {
  853.         if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
  854.        
  855.         return get_magic_quotes_gpc() ? addslashes($string) : $string;
  856.     }
  857.  
  858.  
  859.     function qa_get($field)
  860. /*
  861.     Return string for incoming GET field, or null if it's not defined
  862. */
  863.     {
  864.         if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
  865.        
  866.         return isset($_GET[$field]) ? qa_gpc_to_string($_GET[$field]) : null;
  867.     }
  868.  
  869.  
  870.     function qa_post_text($field)
  871. /*
  872.     Return string for incoming POST field, or null if it's not defined.
  873.     While we're at it, trim() surrounding white space and converted to Unix line endings.
  874. */
  875.     {
  876.         if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
  877.        
  878.         return isset($_POST[$field]) ? preg_replace('/\r\n?/', "\n", trim(qa_gpc_to_string($_POST[$field]))) : null;
  879.     }
  880.  
  881.    
  882.     function qa_clicked($name)
  883. /*
  884.     Return true if form button $name was clicked (as type=submit/image) to create this page request, or if a
  885.     simulated click was sent for the button (via 'qa_click' POST field)
  886. */
  887.     {
  888.         if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
  889.        
  890.         return isset($_POST[$name]) || isset($_POST[$name.'_x']) || (qa_post_text('qa_click')==$name);
  891.     }
  892.  
  893.    
  894.     function qa_remote_ip_address()
  895. /*
  896.     Return the remote IP address of the user accessing the site, if it's available, or null otherwise
  897. */
  898.     {
  899.         if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
  900.        
  901.         return @$_SERVER['REMOTE_ADDR'];
  902.     }
  903.    
  904.    
  905.     function qa_is_http_post()
  906. /*
  907.     Return true if we are responding to an HTTP POST request
  908. */
  909.     {
  910.         if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
  911.        
  912.         return ($_SERVER['REQUEST_METHOD']=='POST') || !empty($_POST);
  913.     }
  914.  
  915.    
  916.     function qa_is_https_probably()
  917. /*
  918.     Return true if we appear to be responding to a secure HTTP request (but hard to be sure)
  919. */
  920.     {
  921.         if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
  922.        
  923.         return (@$_SERVER['HTTPS'] && ($_SERVER['HTTPS']!='off')) || (@$_SERVER['SERVER_PORT']==443);
  924.     }
  925.    
  926.    
  927.     function qa_is_human_probably()
  928. /*
  929.     Return true if it appears the page request is coming from a human using a web browser, rather than a search engine
  930.     or other bot. Based on a whitelist of terms in user agents, this can easily be tricked by a scraper or bad bot.
  931. */
  932.     {
  933.         if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
  934.        
  935.         require_once QA_INCLUDE_DIR.'king-util-string.php';
  936.        
  937.         $useragent=@$_SERVER['HTTP_USER_AGENT'];
  938.        
  939.         return (strlen($useragent)==0) || qa_string_matches_one($useragent, array(
  940.             'MSIE', 'Firefox', 'Chrome', 'Safari', 'Opera', 'Gecko', 'MIDP', 'PLAYSTATION', 'Teleca',
  941.             'BlackBerry', 'UP.Browser', 'Polaris', 'MAUI_WAP_Browser', 'iPad', 'iPhone', 'iPod'
  942.         ));
  943.     }
  944.    
  945.    
  946.     function qa_is_mobile_probably()
  947. /*
  948.     Return true if it appears that the page request is coming from a mobile client rather than a desktop/laptop web browser
  949. */
  950.     {
  951.         if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
  952.        
  953.         require_once QA_INCLUDE_DIR.'king-util-string.php';
  954.        
  955.         // inspired by: http://dangerousprototypes.com/docs/PhpBB3_MOD:_Replacement_mobile_browser_detection_for_mobile_themes
  956.        
  957.         $loweragent=strtolower(@$_SERVER['HTTP_USER_AGENT']);
  958.        
  959.         if (strpos($loweragent, 'ipad')!==false) // consider iPad as desktop
  960.             return false;
  961.        
  962.         $mobileheaders=array('HTTP_X_OPERAMINI_PHONE', 'HTTP_X_WAP_PROFILE', 'HTTP_PROFILE');
  963.        
  964.         foreach ($mobileheaders as $header)
  965.             if (isset($_SERVER[$header]))
  966.                 return true;
  967.                
  968.         if (qa_string_matches_one($loweragent, array(
  969.             'android', 'phone', 'mobile', 'windows ce', 'palm', ' mobi', 'wireless', 'blackberry', 'opera mini', 'symbian',
  970.             'nokia', 'samsung', 'ericsson,', 'vodafone/', 'kindle', 'ipod', 'wap1.', 'wap2.', 'sony', 'sanyo', 'sharp',
  971.             'panasonic', 'philips', 'pocketpc', 'avantgo', 'blazer', 'ipaq', 'up.browser', 'up.link', 'mmp', 'smartphone', 'midp'
  972.         )))
  973.             return true;
  974.        
  975.         return qa_string_matches_one(strtolower(@$_SERVER['HTTP_ACCEPT']), array(
  976.             'application/vnd.wap.xhtml+xml', 'text/vnd.wap.wml'
  977.         ));
  978.     }
  979.    
  980.    
  981. //  Language phrase support
  982.  
  983.     function qa_lang($identifier)
  984. /*
  985.     Return the translated string for $identifier, unless we're using external translation logic.
  986.     This will retrieve the 'site_language' option so make sure you've already loaded/set that if
  987.     loading an option now will cause a problem (see issue in qa_default_option()). The part of
  988.     $identifier before the slash (/) replaces the * in the king-lang-*.php file references, and the
  989.     part after the / is the key of the array element to be taken from that file's returned result.
  990. */
  991.     {
  992.         if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
  993.        
  994.         global $qa_lang_file_pattern, $qa_phrases_custom, $qa_phrases_lang, $qa_phrases_default;
  995.        
  996.         list($group, $label)=explode('/', $identifier, 2);
  997.        
  998.     //  First look for a custom phrase
  999.        
  1000.         if (!isset($qa_phrases_custom[$group])) { // only load each language file once
  1001.             $phrases=@include QA_LANG_DIR.'custom/king-lang-'.$group.'.php'; // can tolerate missing file or directory
  1002.             $qa_phrases_custom[$group]=is_array($phrases) ? $phrases : array();
  1003.         }
  1004.        
  1005.         if (isset($qa_phrases_custom[$group][$label]))
  1006.             return $qa_phrases_custom[$group][$label];
  1007.            
  1008.     //  Second look for a localized file
  1009.    
  1010.         $languagecode=qa_opt('site_language');
  1011.        
  1012.         if (strlen($languagecode)) {
  1013.             if (!isset($qa_phrases_lang[$group])) {
  1014.                 if (isset($qa_lang_file_pattern[$group]))
  1015.                     $include=str_replace('*', $languagecode, $qa_lang_file_pattern[$group]);
  1016.                 else
  1017.                     $include=QA_LANG_DIR.$languagecode.'/king-lang-'.$group.'.php';
  1018.                
  1019.                 $phrases=@include $include;
  1020.                 $qa_phrases_lang[$group]=is_array($phrases) ? $phrases : array();
  1021.             }
  1022.            
  1023.             if (isset($qa_phrases_lang[$group][$label]))
  1024.                 return $qa_phrases_lang[$group][$label];
  1025.         }
  1026.        
  1027.     //  Finally load the default
  1028.    
  1029.         if (!isset($qa_phrases_default[$group])) { // only load each default language file once
  1030.             if (isset($qa_lang_file_pattern[$group]))
  1031.                 $include=str_replace('*', 'default', $qa_lang_file_pattern[$group]);
  1032.             else
  1033.                 $include=QA_INCLUDE_DIR.'king-lang-'.$group.'.php';
  1034.                
  1035.             $qa_phrases_default[$group]=@include_once $include;
  1036.         }
  1037.        
  1038.         if (isset($qa_phrases_default[$group][$label]))
  1039.             return $qa_phrases_default[$group][$label];
  1040.            
  1041.         return '['.$identifier.']'; // as a last resort, return the identifier to help in development
  1042.     }
  1043.  
  1044.  
  1045.     function qa_lang_sub($identifier, $textparam, $symbol='^')
  1046. /*
  1047.     Return the translated string for $identifier, with $symbol substituted for $textparam
  1048. */
  1049.     {
  1050.         return str_replace($symbol, $textparam, qa_lang($identifier));
  1051.     }
  1052.    
  1053.  
  1054.     function qa_lang_html($identifier)
  1055. /*
  1056.     Return the translated string for $identifier, converted to HTML
  1057. */
  1058.     {
  1059.         return qa_html(qa_lang($identifier));
  1060.     }
  1061.  
  1062.    
  1063.     function qa_lang_html_sub($identifier, $htmlparam, $symbol='^')
  1064. /*
  1065.     Return the translated string for $identifier converted to HTML, with $symbol *then* substituted for $htmlparam
  1066. */
  1067.     {
  1068.         return str_replace($symbol, $htmlparam, qa_lang_html($identifier));
  1069.     }
  1070.    
  1071.  
  1072.     function qa_lang_html_sub_split($identifier, $htmlparam, $symbol='^')
  1073. /*
  1074.     Return an array containing the translated string for $identifier converted to HTML, then split into three,
  1075.     with $symbol substituted for $htmlparam in the 'data' element, and obvious 'prefix' and 'suffix' elements
  1076. */
  1077.     {
  1078.         $html=qa_lang_html($identifier);
  1079.  
  1080.         $symbolpos=strpos($html, $symbol);
  1081.         if (!is_numeric($symbolpos))
  1082.             qa_fatal_error('Missing '.$symbol.' in language string '.$identifier);
  1083.            
  1084.         return array(
  1085.             'prefix' => substr($html, 0, $symbolpos),
  1086.             'data' => $htmlparam,
  1087.             'suffix' => substr($html, $symbolpos+1),
  1088.         );
  1089.     }
  1090.  
  1091.    
  1092. //  Request and path generation
  1093.  
  1094.     function qa_path_to_root()
  1095. /*
  1096.     Return the relative path to the Q2A root (if it's was previously set by qa_set_request())
  1097. */
  1098.     {
  1099.         if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
  1100.        
  1101.         global $qa_root_url_relative;
  1102.         return $qa_root_url_relative;
  1103.     }
  1104.    
  1105.    
  1106.     function qa_get_request_map()
  1107. /*
  1108.     Return an array of mappings of Q2A requests, as defined in the king-config.php file
  1109. */
  1110.     {
  1111.         if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
  1112.        
  1113.         global $qa_request_map;
  1114.         return $qa_request_map;
  1115.     }
  1116.    
  1117.  
  1118.     function qa_path($request, $params=null, $rooturl=null, $neaturls=null, $anchor=null)
  1119. /*
  1120.     Return the relative URI path for $request, with optional parameters $params and $anchor.
  1121.     Slashes in $request will not be urlencoded, but any other characters will.
  1122.     If $neaturls is set, use that, otherwise retrieve the option. If $rooturl is set, take
  1123.     that as the root of the Q2A site, otherwise use path to root which was set elsewhere.
  1124. */
  1125.     {
  1126.         if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
  1127.        
  1128.         if (!isset($neaturls)) {
  1129.             require_once QA_INCLUDE_DIR.'king-app-options.php';
  1130.             $neaturls=qa_opt('neat_urls');
  1131.         }
  1132.        
  1133.         if (!isset($rooturl))
  1134.             $rooturl=qa_path_to_root();
  1135.        
  1136.         $url=$rooturl.( (empty($rooturl) || (substr($rooturl, -1)=='/') ) ? '' : '/');
  1137.         $paramsextra='';
  1138.        
  1139.         $requestparts=explode('/', $request);
  1140.         $pathmap=qa_get_request_map();
  1141.        
  1142.         if (isset($pathmap[$requestparts[0]])) {
  1143.             $newpart=$pathmap[$requestparts[0]];
  1144.            
  1145.             if (strlen($newpart))
  1146.                 $requestparts[0]=$newpart;
  1147.             elseif (count($requestparts)==1)
  1148.                 array_shift($requestparts);
  1149.         }
  1150.        
  1151.         foreach ($requestparts as $index => $requestpart)
  1152.             $requestparts[$index]=urlencode($requestpart);
  1153.         $requestpath=implode('/', $requestparts);
  1154.        
  1155.         switch ($neaturls) {
  1156.             case QA_URL_FORMAT_INDEX:
  1157.                 if (!empty($request))
  1158.                     $url.='index.php/'.$requestpath;
  1159.                 break;
  1160.                
  1161.             case QA_URL_FORMAT_NEAT:
  1162.                 $url.=$requestpath;
  1163.                 break;
  1164.                
  1165.             case QA_URL_FORMAT_PARAM:
  1166.                 if (!empty($request))
  1167.                     $paramsextra='?qa='.$requestpath;
  1168.                 break;
  1169.                
  1170.             default:
  1171.                 $url.='index.php';
  1172.            
  1173.             case QA_URL_FORMAT_PARAMS:
  1174.                 if (!empty($request))
  1175.                     foreach ($requestparts as $partindex => $requestpart)
  1176.                         $paramsextra.=(strlen($paramsextra) ? '&' : '?').'qa'.($partindex ? ('_'.$partindex) : '').'='.$requestpart;
  1177.                 break;
  1178.         }
  1179.        
  1180.         if (isset($params))
  1181.             foreach ($params as $key => $value)
  1182.                 $paramsextra.=(strlen($paramsextra) ? '&' : '?').urlencode($key).'='.urlencode((string)$value);
  1183.        
  1184.         return $url.$paramsextra.( empty($anchor) ? '' : '#'.urlencode($anchor) );
  1185.     }
  1186.  
  1187.  
  1188.     function qa_path_html($request, $params=null, $rooturl=null, $neaturls=null, $anchor=null)
  1189. /*
  1190.     Return HTML representation of relative URI path for $request - see qa_path() for other parameters
  1191. */
  1192.     {
  1193.         return qa_html(qa_path($request, $params, $rooturl, $neaturls, $anchor));
  1194.     }
  1195.    
  1196.    
  1197.     function qa_path_absolute($request, $params=null, $anchor=null)
  1198. /*
  1199.     Return the absolute URI for $request - see qa_path() for other parameters
  1200. */
  1201.     {
  1202.         return qa_path($request, $params, qa_opt('site_url'), null, $anchor);
  1203.     }
  1204.  
  1205.    
  1206.     function qa_q_request($questionid, $title)
  1207. /*
  1208.     Return the Q2A request for question $questionid, and make it search-engine friendly based on $title, which is
  1209.     shortened if necessary by removing shorter words which are generally less meaningful.
  1210. */
  1211.     {
  1212.         if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
  1213.        
  1214.         require_once QA_INCLUDE_DIR.'king-app-options.php';
  1215.         require_once QA_INCLUDE_DIR.'king-util-string.php';
  1216.    
  1217.         $title=qa_block_words_replace($title, qa_get_block_words_preg());
  1218.        
  1219.         $words=qa_string_to_words($title, true, false, false);
  1220.  
  1221.         $wordlength=array();
  1222.         foreach ($words as $index => $word)
  1223.             $wordlength[$index]=qa_strlen($word);
  1224.  
  1225.         $remaining=qa_opt('q_urls_title_length');
  1226.        
  1227.         if (array_sum($wordlength)>$remaining) {
  1228.             arsort($wordlength, SORT_NUMERIC); // sort with longest words first
  1229.            
  1230.             foreach ($wordlength as $index => $length) {
  1231.                 if ($remaining>0)
  1232.                     $remaining-=$length;
  1233.                 else
  1234.                     unset($words[$index]);
  1235.             }
  1236.         }
  1237.        
  1238.         $title=implode('-', $words);
  1239.         if (qa_opt('q_urls_remove_accents'))
  1240.             $title=qa_string_remove_accents($title);
  1241.        
  1242.         return (int)$questionid.'/'.$title;
  1243.     }
  1244.    
  1245.    
  1246.     function qa_anchor($basetype, $postid)
  1247. /*
  1248.     Return the HTML anchor that should be used for post $postid with $basetype (Q/A/C)
  1249. */
  1250.     {
  1251.         if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
  1252.        
  1253.         return strtolower($basetype).$postid; // used to be $postid only but this violated HTML spec
  1254.     }
  1255.    
  1256.    
  1257.     function qa_q_path($questionid, $title, $absolute=false, $showtype=null, $showid=null)
  1258. /*
  1259.     Return the URL for question $questionid with $title, possibly using $absolute URLs.
  1260.     To link to a specific answer or comment in a question, set $showtype and $showid accordingly.
  1261. */
  1262.     {
  1263.         if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
  1264.        
  1265.         if ( (($showtype=='Q') || ($showtype=='A') || ($showtype=='C')) && isset($showid))  {
  1266.             $params=array('show' => $showid); // due to pagination
  1267.             $anchor=qa_anchor($showtype, $showid);
  1268.        
  1269.         } else {
  1270.             $params=null;
  1271.             $anchor=null;
  1272.         }
  1273.        
  1274.         return qa_path(qa_q_request($questionid, $title), $params, $absolute ? qa_opt('site_url') : null, null, $anchor);
  1275.     }
  1276.    
  1277.    
  1278.     function qa_q_path_html($questionid, $title, $absolute=false, $showtype=null, $showid=null)
  1279. /*
  1280.     Return the HTML representation of the URL for $questionid - other parameters as for qa_q_path()
  1281. */
  1282.     {
  1283.         return qa_html(qa_q_path($questionid, $title, $absolute, $showtype, $showid));
  1284.     }
  1285.  
  1286.    
  1287.     function qa_feed_request($feed)
  1288. /*
  1289.     Return the request for the specified $feed
  1290. */
  1291.     {
  1292.         if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
  1293.        
  1294.         return 'feed/'.$feed.'.rss';
  1295.     }
  1296.    
  1297.    
  1298.     function qa_self_html()
  1299. /*
  1300.     Return an HTML-ready relative URL for the current page, preserving GET parameters - this is useful for action="..." in HTML forms
  1301. */
  1302.     {
  1303.         if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
  1304.        
  1305.         global $qa_used_url_format;
  1306.        
  1307.         return qa_path_html(qa_request(), $_GET, null, $qa_used_url_format);
  1308.     }
  1309.    
  1310.  
  1311.     function qa_path_form_html($request, $params=null, $rooturl=null, $neaturls=null, $anchor=null)
  1312. /*
  1313.     Return HTML for hidden fields to insert into a <form method="get"...> on the page.
  1314.     This is needed because any parameters on the URL will be lost when the form is submitted.
  1315. */
  1316.     {
  1317.         if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
  1318.        
  1319.         $path=qa_path($request, $params, $rooturl, $neaturls, $anchor);
  1320.         $formhtml='';
  1321.        
  1322.         $questionpos=strpos($path, '?');
  1323.         if (is_numeric($questionpos)) {
  1324.             $params=explode('&', substr($path, $questionpos+1));
  1325.            
  1326.             foreach ($params as $param)
  1327.                 if (preg_match('/^([^\=]*)(\=(.*))?$/', $param, $matches))
  1328.                     $formhtml.='<input type="hidden" name="'.qa_html(urldecode($matches[1])).'" value="'.qa_html(urldecode(@$matches[3])).'"/>';
  1329.         }
  1330.        
  1331.         return $formhtml;
  1332.     }
  1333.    
  1334.    
  1335.     function qa_redirect($request, $params=null, $rooturl=null, $neaturls=null, $anchor=null)
  1336. /*
  1337.     Redirect the user's web browser to $request and then we're done - see qa_path() for other parameters
  1338. */
  1339.     {
  1340.         if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
  1341.        
  1342.         qa_redirect_raw(qa_path($request, $params, $rooturl, $neaturls, $anchor));
  1343.     }
  1344.    
  1345.    
  1346.     function qa_redirect_raw($url)
  1347. /*
  1348.     Redirect the user's web browser to page $path which is already a URL
  1349. */
  1350.     {
  1351.         if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
  1352.        
  1353.         header('Location: '.$url);
  1354.         qa_exit('redirect');
  1355.     }
  1356.    
  1357.  
  1358. //  General utilities
  1359.  
  1360.     function qa_retrieve_url($url)
  1361. /*
  1362.     Return the contents of remote $url, using file_get_contents() if possible, otherwise curl functions
  1363. */
  1364.     {
  1365.         if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
  1366.        
  1367.         $contents=@file_get_contents($url);
  1368.        
  1369.         if ((!strlen($contents)) && function_exists('curl_exec')) { // try curl as a backup (if allow_url_fopen not set)
  1370.             $curl=curl_init($url);
  1371.             curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
  1372.             curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
  1373.             $contents=@curl_exec($curl);
  1374.             curl_close($curl);
  1375.         }
  1376.        
  1377.         return $contents;
  1378.     }
  1379.  
  1380.  
  1381.     function qa_opt($name, $value=null)
  1382. /*
  1383.     Shortcut to get or set an option value without specifying database
  1384. */
  1385.     {
  1386.         global $qa_options_cache;
  1387.        
  1388.         if ((!isset($value)) && isset($qa_options_cache[$name]))
  1389.             return $qa_options_cache[$name]; // quick shortcut to reduce calls to qa_get_options()
  1390.        
  1391.         require_once QA_INCLUDE_DIR.'king-app-options.php';
  1392.        
  1393.         if (isset($value))
  1394.             qa_set_option($name, $value);  
  1395.        
  1396.         $options=qa_get_options(array($name));
  1397.  
  1398.         return $options[$name];
  1399.     }
  1400.    
  1401.    
  1402. //  Event and process stage reporting
  1403.  
  1404.     function qa_suspend_event_reports($suspend=true)
  1405. /*
  1406.     Suspend the reporting of events to event modules via qa_report_event(...) if $suspend is
  1407.     true, otherwise reinstate it. A counter is kept to allow multiple calls.
  1408. */
  1409.     {
  1410.         global $qa_event_reports_suspended;
  1411.        
  1412.         $qa_event_reports_suspended+=($suspend ? 1 : -1);
  1413.     }
  1414.    
  1415.    
  1416.     function qa_report_event($event, $userid, $handle, $cookieid, $params=array())
  1417. /*
  1418.     Send a notification of event $event by $userid, $handle and $cookieid to all event modules, with extra $params
  1419. */
  1420.     {
  1421.         if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
  1422.        
  1423.         global $qa_event_reports_suspended;
  1424.        
  1425.         if ($qa_event_reports_suspended>0)
  1426.             return;
  1427.        
  1428.         $eventmodules=qa_load_modules_with('event', 'process_event');
  1429.         foreach ($eventmodules as $eventmodule)
  1430.             $eventmodule->process_event($event, $userid, $handle, $cookieid, $params);
  1431.     }
  1432.    
  1433.    
  1434.     function qa_report_process_stage($method) // can have extra params
  1435.     {
  1436.         global $qa_process_reports_suspended;
  1437.        
  1438.         if (@$qa_process_reports_suspended)
  1439.             return;
  1440.            
  1441.         $qa_process_reports_suspended=true; // prevent loop, e.g. because of an error
  1442.        
  1443.         $args=func_get_args();
  1444.         $args=array_slice($args, 1);
  1445.        
  1446.         $processmodules=qa_load_modules_with('process', $method);
  1447.         foreach ($processmodules as $processmodule)
  1448.             call_user_func_array(array($processmodule, $method), $args);
  1449.  
  1450.         $qa_process_reports_suspended=null;
  1451.     }
  1452.    
  1453.  
  1454. /*
  1455.     Omit PHP closing tag to help avoid accidental output
  1456. */
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement