includes/functions.php
changeset 1227 bdac73ed481e
parent 1226 de56132c008d
child 1249 81b03b3e88d0
equal deleted inserted replaced
1226:de56132c008d 1227:bdac73ed481e
    18  * @return string Configuration value, or bool(false) if the value is not set
    18  * @return string Configuration value, or bool(false) if the value is not set
    19  */
    19  */
    20 
    20 
    21 function getConfig($n, $default = false)
    21 function getConfig($n, $default = false)
    22 {
    22 {
    23   global $enano_config;
    23 	global $enano_config;
    24   if ( isset( $enano_config[ $n ] ) )
    24 	if ( isset( $enano_config[ $n ] ) )
    25   {
    25 	{
    26     return $enano_config[$n];
    26 		return $enano_config[$n];
    27   }
    27 	}
    28   else
    28 	else
    29   {
    29 	{
    30     return $default;
    30 		return $default;
    31   }
    31 	}
    32 }
    32 }
    33 
    33 
    34 /**
    34 /**
    35  * Update or change a configuration value.
    35  * Update or change a configuration value.
    36  * @param string The identifier of the value ("site_name" etc.)
    36  * @param string The identifier of the value ("site_name" etc.)
    38  * @return null
    38  * @return null
    39  */
    39  */
    40 
    40 
    41 function setConfig($n, $v)
    41 function setConfig($n, $v)
    42 {
    42 {
    43   global $enano_config, $db;
    43 	global $enano_config, $db;
    44   
    44 	
    45   if ( isset($enano_config[$n]) )
    45 	if ( isset($enano_config[$n]) )
    46   {
    46 	{
    47     if ( $enano_config[$n] === $v )
    47 		if ( $enano_config[$n] === $v )
    48     {
    48 		{
    49       // configuration already matches this value
    49 			// configuration already matches this value
    50       return true;
    50 			return true;
    51     }
    51 		}
    52   }
    52 	}
    53   
    53 	
    54   $enano_config[$n] = $v;
    54 	$enano_config[$n] = $v;
    55   if ( $v === false )
    55 	if ( $v === false )
    56     unset($enano_config[$n]);
    56 		unset($enano_config[$n]);
    57   
    57 	
    58   $v = $db->escape($v);
    58 	$v = $db->escape($v);
    59 
    59 
    60   $e = $db->sql_query('DELETE FROM '.table_prefix.'config WHERE config_name=\''.$n.'\';');
    60 	$e = $db->sql_query('DELETE FROM '.table_prefix.'config WHERE config_name=\''.$n.'\';');
    61   if ( !$e )
    61 	if ( !$e )
    62   {
    62 	{
    63     $db->_die('Error during generic setConfig() call row deletion.');
    63 		$db->_die('Error during generic setConfig() call row deletion.');
    64   }
    64 	}
    65 
    65 
    66   if ( $v !== false )
    66 	if ( $v !== false )
    67   {
    67 	{
    68     $e = $db->sql_query('INSERT INTO '.table_prefix.'config(config_name, config_value) VALUES(\''.$n.'\', \''.$v.'\')');
    68 		$e = $db->sql_query('INSERT INTO '.table_prefix.'config(config_name, config_value) VALUES(\''.$n.'\', \''.$v.'\')');
    69     if ( !$e )
    69 		if ( !$e )
    70     {
    70 		{
    71       $db->_die('Error during generic setConfig() call row insertion.');
    71 			$db->_die('Error during generic setConfig() call row insertion.');
    72     }
    72 		}
    73   }
    73 	}
    74 }
    74 }
    75 
    75 
    76 /**
    76 /**
    77  * Create a URI for an internal link.
    77  * Create a URI for an internal link.
    78  * @param string The full identifier of the page to link to (Special:Administration)
    78  * @param string The full identifier of the page to link to (Special:Administration)
    81  * @return string
    81  * @return string
    82  */
    82  */
    83 
    83 
    84 if ( !function_exists('makeUrl') )
    84 if ( !function_exists('makeUrl') )
    85 {
    85 {
    86   function makeUrl($t, $query = false, $escape = false)
    86 	function makeUrl($t, $query = false, $escape = false)
    87   {
    87 	{
    88     global $db, $session, $paths, $template, $plugins; // Common objects
    88 		global $db, $session, $paths, $template, $plugins; // Common objects
    89     $flags = '';
    89 		$flags = '';
    90     $sep = urlSeparator;
    90 		$sep = urlSeparator;
    91     $t = sanitize_page_id($t);
    91 		$t = sanitize_page_id($t);
    92     if ( isset($_GET['printable'] ) )
    92 		if ( isset($_GET['printable'] ) )
    93     {
    93 		{
    94       $flags .= $sep . 'printable=yes';
    94 			$flags .= $sep . 'printable=yes';
    95       $sep = '&';
    95 			$sep = '&';
    96     }
    96 		}
    97     if ( isset($_GET['theme'] ) )
    97 		if ( isset($_GET['theme'] ) )
    98     {
    98 		{
    99       $flags .= $sep . 'theme='.$session->theme;
    99 			$flags .= $sep . 'theme='.$session->theme;
   100       $sep = '&';
   100 			$sep = '&';
   101     }
   101 		}
   102     if ( isset($_GET['style'] ) )
   102 		if ( isset($_GET['style'] ) )
   103     {
   103 		{
   104       $flags .= $sep . 'style='.$session->style;
   104 			$flags .= $sep . 'style='.$session->style;
   105       $sep = '&';
   105 			$sep = '&';
   106     }
   106 		}
   107     if ( isset($_GET['lang']) && preg_match('/^[a-z0-9_]+$/', @$_GET['lang']) )
   107 		if ( isset($_GET['lang']) && preg_match('/^[a-z0-9_]+$/', @$_GET['lang']) )
   108     {
   108 		{
   109       $flags .= $sep . 'lang=' . urlencode($_GET['lang']);
   109 			$flags .= $sep . 'lang=' . urlencode($_GET['lang']);
   110       $sep = '&';
   110 			$sep = '&';
   111     }
   111 		}
   112   
   112 	
   113     $url = is_object($session) ? $session->append_sid(contentPath.$t.$flags) : contentPath . $t . $flags;
   113 		$url = is_object($session) ? $session->append_sid(contentPath.$t.$flags) : contentPath . $t . $flags;
   114     if($query)
   114 		if($query)
   115     {
   115 		{
   116       $sep = strstr($url, '?') ? '&' : '?';
   116 			$sep = strstr($url, '?') ? '&' : '?';
   117       $url = $url . $sep . $query;
   117 			$url = $url . $sep . $query;
   118     }
   118 		}
   119   
   119 	
   120     return ($escape) ? htmlspecialchars($url) : $url;
   120 		return ($escape) ? htmlspecialchars($url) : $url;
   121   }
   121 	}
   122 }
   122 }
   123 
   123 
   124 /**
   124 /**
   125  * Create a URI for an internal link, and be namespace-friendly. Watch out for this one because it's different from most other Enano functions, in that the namespace is the first parameter.
   125  * Create a URI for an internal link, and be namespace-friendly. Watch out for this one because it's different from most other Enano functions, in that the namespace is the first parameter.
   126  * @param string The namespace ID
   126  * @param string The namespace ID
   130  * @return string
   130  * @return string
   131  */
   131  */
   132 
   132 
   133 if ( !function_exists('makeUrlNS') )
   133 if ( !function_exists('makeUrlNS') )
   134 {
   134 {
   135   function makeUrlNS($n, $t, $query = false, $escape = false)
   135 	function makeUrlNS($n, $t, $query = false, $escape = false)
   136   {
   136 	{
   137     global $db, $session, $paths, $template, $plugins; // Common objects
   137 		global $db, $session, $paths, $template, $plugins; // Common objects
   138     $flags = '';
   138 		$flags = '';
   139   
   139 	
   140     if(defined('ENANO_BASE_CLASSES_INITIALIZED'))
   140 		if(defined('ENANO_BASE_CLASSES_INITIALIZED'))
   141     {
   141 		{
   142       $sep = urlSeparator;
   142 			$sep = urlSeparator;
   143     }
   143 		}
   144     else
   144 		else
   145     {
   145 		{
   146       $sep = (strstr($_SERVER['REQUEST_URI'], '?')) ? '&' : '?';
   146 			$sep = (strstr($_SERVER['REQUEST_URI'], '?')) ? '&' : '?';
   147     }
   147 		}
   148     if ( isset( $_GET['printable'] ) ) {
   148 		if ( isset( $_GET['printable'] ) ) {
   149       $flags .= $sep . 'printable';
   149 			$flags .= $sep . 'printable';
   150       $sep = '&';
   150 			$sep = '&';
   151     }
   151 		}
   152     if ( isset( $_GET['theme'] ) )
   152 		if ( isset( $_GET['theme'] ) )
   153     {
   153 		{
   154       $flags .= $sep . 'theme='.$session->theme;
   154 			$flags .= $sep . 'theme='.$session->theme;
   155       $sep = '&';
   155 			$sep = '&';
   156     }
   156 		}
   157     if ( isset( $_GET['style'] ) )
   157 		if ( isset( $_GET['style'] ) )
   158     {
   158 		{
   159       $flags .= $sep . 'style='.$session->style;
   159 			$flags .= $sep . 'style='.$session->style;
   160       $sep = '&';
   160 			$sep = '&';
   161     }
   161 		}
   162     if ( isset($_GET['lang']) && preg_match('/^[a-z0-9_]+$/', @$_GET['lang']) )
   162 		if ( isset($_GET['lang']) && preg_match('/^[a-z0-9_]+$/', @$_GET['lang']) )
   163     {
   163 		{
   164       $flags .= $sep . 'lang=' . urlencode($_GET['lang']);
   164 			$flags .= $sep . 'lang=' . urlencode($_GET['lang']);
   165       $sep = '&';
   165 			$sep = '&';
   166     }
   166 		}
   167     
   167 		
   168     $ns_prefix = "$n:";
   168 		$ns_prefix = "$n:";
   169   
   169 	
   170     if(defined('ENANO_BASE_CLASSES_INITIALIZED'))
   170 		if(defined('ENANO_BASE_CLASSES_INITIALIZED'))
   171     {
   171 		{
   172       $ns_prefix = ( isset($paths->nslist[$n]) ) ? $paths->nslist[$n] : $n . substr($paths->nslist['Special'], -1);
   172 			$ns_prefix = ( isset($paths->nslist[$n]) ) ? $paths->nslist[$n] : $n . substr($paths->nslist['Special'], -1);
   173       $url = contentPath . $ns_prefix . $t . $flags;
   173 			$url = contentPath . $ns_prefix . $t . $flags;
   174     }
   174 		}
   175     else
   175 		else
   176     {
   176 		{
   177       // If the path manager hasn't been initted yet, take an educated guess at what the URI should be
   177 			// If the path manager hasn't been initted yet, take an educated guess at what the URI should be
   178       $url = contentPath . $n . ':' . $t . $flags;
   178 			$url = contentPath . $n . ':' . $t . $flags;
   179     }
   179 		}
   180   
   180 	
   181     if($query)
   181 		if($query)
   182     {
   182 		{
   183       if(strstr($url, '?'))
   183 			if(strstr($url, '?'))
   184       {
   184 			{
   185         $sep =  '&';
   185 				$sep =  '&';
   186       }
   186 			}
   187       else
   187 			else
   188       {
   188 			{
   189         $sep = '?';
   189 				$sep = '?';
   190       }
   190 			}
   191       $url = $url . $sep . $query . $flags;
   191 			$url = $url . $sep . $query . $flags;
   192     }
   192 		}
   193   
   193 	
   194     if(defined('ENANO_BASE_CLASSES_INITIALIZED'))
   194 		if(defined('ENANO_BASE_CLASSES_INITIALIZED'))
   195     {
   195 		{
   196       $url = $session->append_sid($url);
   196 			$url = $session->append_sid($url);
   197     }
   197 		}
   198   
   198 	
   199     return ($escape) ? htmlspecialchars($url) : $url;
   199 		return ($escape) ? htmlspecialchars($url) : $url;
   200   }
   200 	}
   201 }
   201 }
   202 
   202 
   203 /**
   203 /**
   204  * Create a URI for an internal link, be namespace-friendly, and add http://hostname/scriptpath to the beginning if possible. Watch out for this one because it's different from most other Enano functions, in that the namespace is the first parameter.
   204  * Create a URI for an internal link, be namespace-friendly, and add http://hostname/scriptpath to the beginning if possible. Watch out for this one because it's different from most other Enano functions, in that the namespace is the first parameter.
   205  * @param string The namespace ID
   205  * @param string The namespace ID
   209  * @return string
   209  * @return string
   210  */
   210  */
   211 
   211 
   212 function makeUrlComplete($n, $t, $query = false, $escape = false)
   212 function makeUrlComplete($n, $t, $query = false, $escape = false)
   213 {
   213 {
   214   return get_server_url() . makeUrlNS($n, $t, $query, $escape);
   214 	return get_server_url() . makeUrlNS($n, $t, $query, $escape);
   215 }
   215 }
   216 
   216 
   217 /**
   217 /**
   218  * Returns an http:// URL for this server.
   218  * Returns an http:// URL for this server.
   219  * @return string
   219  * @return string
   220  */
   220  */
   221 
   221 
   222 function get_server_url()
   222 function get_server_url()
   223 {
   223 {
   224   $server_name = false;
   224 	$server_name = false;
   225   if ( isset($_SERVER['HTTP_HOST']) )
   225 	if ( isset($_SERVER['HTTP_HOST']) )
   226   	$server_name = $_SERVER['HTTP_HOST'];
   226 		$server_name = $_SERVER['HTTP_HOST'];
   227   else if ( isset($_SERVER['SERVER_NAME']) )
   227 	else if ( isset($_SERVER['SERVER_NAME']) )
   228   	$server_name = $_SERVER['SERVER_NAME'];
   228 		$server_name = $_SERVER['SERVER_NAME'];
   229   else
   229 	else
   230     $server_name = 'localhost';
   230 		$server_name = 'localhost';
   231   
   231 	
   232   return 'http' . ( $GLOBALS['is_https'] ) . '://' . $server_name;
   232 	return 'http' . ( $GLOBALS['is_https'] ) . '://' . $server_name;
   233 }
   233 }
   234 
   234 
   235 /**
   235 /**
   236  * Returns the full page ID string of the main page.
   236  * Returns the full page ID string of the main page.
   237  * @return string
   237  * @return string
   238  */
   238  */
   239 
   239 
   240 function get_main_page($force_logged_in = false)
   240 function get_main_page($force_logged_in = false)
   241 {
   241 {
   242   global $db, $session, $paths, $template, $plugins; // Common objects
   242 	global $db, $session, $paths, $template, $plugins; // Common objects
   243   
   243 	
   244   $logged_in = false;
   244 	$logged_in = false;
   245   if ( is_object($session) && !$force_logged_in )
   245 	if ( is_object($session) && !$force_logged_in )
   246   {
   246 	{
   247     $logged_in = $session->user_logged_in;
   247 		$logged_in = $session->user_logged_in;
   248   }
   248 	}
   249   else if ( $force_logged_in )
   249 	else if ( $force_logged_in )
   250   {
   250 	{
   251     $logged_in = true;
   251 		$logged_in = true;
   252   }
   252 	}
   253   return $logged_in && getConfig('main_page_alt_enable', '0') == '1' ? getConfig('main_page_alt', getConfig('main_page', 'Main_Page')) : getConfig('main_page', 'Main_Page');
   253 	return $logged_in && getConfig('main_page_alt_enable', '0') == '1' ? getConfig('main_page_alt', getConfig('main_page', 'Main_Page')) : getConfig('main_page', 'Main_Page');
   254 }
   254 }
   255 
   255 
   256 /**
   256 /**
   257  * Get the requested page title, taking into account all the different possible URL parsing schemes.
   257  * Get the requested page title, taking into account all the different possible URL parsing schemes.
   258  * @param bool If true (default), runs the result through sanitize_page_id().
   258  * @param bool If true (default), runs the result through sanitize_page_id().
   260  * @return string
   260  * @return string
   261  */
   261  */
   262 
   262 
   263 function get_title($sanitize = true, $chop_special = false)
   263 function get_title($sanitize = true, $chop_special = false)
   264 {
   264 {
   265   $title = '';
   265 	$title = '';
   266   if ( isset($_GET['title']) )
   266 	if ( isset($_GET['title']) )
   267   {
   267 	{
   268     $title = $_GET['title'];
   268 		$title = $_GET['title'];
   269   }
   269 	}
   270   else if ( isset($_SERVER['PATH_INFO']) )
   270 	else if ( isset($_SERVER['PATH_INFO']) )
   271   {
   271 	{
   272     // fix for apache + CGI (occurred on a GoDaddy server, thanks mm3)
   272 		// fix for apache + CGI (occurred on a GoDaddy server, thanks mm3)
   273     if ( @substr(@$_SERVER['GATEWAY_INTERFACE'], 0, 3) === 'CGI' && $_SERVER['PATH_INFO'] == scriptPath . '/index.php' )
   273 		if ( @substr(@$_SERVER['GATEWAY_INTERFACE'], 0, 3) === 'CGI' && $_SERVER['PATH_INFO'] == scriptPath . '/index.php' )
   274     {
   274 		{
   275       // do nothing; ignore PATH_INFO
   275 			// do nothing; ignore PATH_INFO
   276     }
   276 		}
   277     else
   277 		else
   278     {
   278 		{
   279       $title = substr($_SERVER['PATH_INFO'], ( strpos($_SERVER['PATH_INFO'], '/') ) + 1 );
   279 			$title = substr($_SERVER['PATH_INFO'], ( strpos($_SERVER['PATH_INFO'], '/') ) + 1 );
   280     }
   280 		}
   281   }
   281 	}
   282   else
   282 	else
   283   {
   283 	{
   284     // This method really isn't supported because apache has a habit of passing dots as underscores, thus corrupting the request
   284 		// This method really isn't supported because apache has a habit of passing dots as underscores, thus corrupting the request
   285     // If you really want to try it, the URI format is yoursite.com/?/Page_title
   285 		// If you really want to try it, the URI format is yoursite.com/?/Page_title
   286     if ( !empty($_SERVER['QUERY_STRING']) && substr($_SERVER['QUERY_STRING'], 0, 1) == '/' )
   286 		if ( !empty($_SERVER['QUERY_STRING']) && substr($_SERVER['QUERY_STRING'], 0, 1) == '/' )
   287     {
   287 		{
   288       $pos = ( ($_ = strpos($_SERVER['QUERY_STRING'], '&')) !== false ) ? $_ - 1: 0x7FFFFFFF;
   288 			$pos = ( ($_ = strpos($_SERVER['QUERY_STRING'], '&')) !== false ) ? $_ - 1: 0x7FFFFFFF;
   289       $title = substr($_SERVER['QUERY_STRING'], 1, $pos);
   289 			$title = substr($_SERVER['QUERY_STRING'], 1, $pos);
   290     }
   290 		}
   291   }
   291 	}
   292   
   292 	
   293   if ( $chop_special )
   293 	if ( $chop_special )
   294   {
   294 	{
   295     list(, $ns) = RenderMan::strToPageID($title);
   295 		list(, $ns) = RenderMan::strToPageID($title);
   296     if ( $ns == 'Special' || $ns == 'Admin' )
   296 		if ( $ns == 'Special' || $ns == 'Admin' )
   297     {
   297 		{
   298       list($title) = explode('/', $title);
   298 			list($title) = explode('/', $title);
   299     }
   299 		}
   300   }
   300 	}
   301   
   301 	
   302   return ( $sanitize ) ? sanitize_page_id($title) : $title;
   302 	return ( $sanitize ) ? sanitize_page_id($title) : $title;
   303 }
   303 }
   304 
   304 
   305 /**
   305 /**
   306  * Returns true if we are allowed to have a page with a fully blank URL string. This page magically exists when you set the main page to blank.
   306  * Returns true if we are allowed to have a page with a fully blank URL string. This page magically exists when you set the main page to blank.
   307  * @return bool
   307  * @return bool
   308  */
   308  */
   309 
   309 
   310 function have_blank_urlname_page()
   310 function have_blank_urlname_page()
   311 {
   311 {
   312   return getConfig('main_page', 'Main_Page') == '' || getConfig('main_page', getConfig('main_page', 'Main_Page')) == '';
   312 	return getConfig('main_page', 'Main_Page') == '' || getConfig('main_page', getConfig('main_page', 'Main_Page')) == '';
   313 }
   313 }
   314 
   314 
   315 /**
   315 /**
   316  * Enano replacement for date(). Accounts for individual users' timezone preferences.
   316  * Enano replacement for date(). Accounts for individual users' timezone preferences.
   317  * @param string Date-formatted string
   317  * @param string Date-formatted string
   319  * @return string Formatted string
   319  * @return string Formatted string
   320  */
   320  */
   321 
   321 
   322 function enano_date($string, $timestamp = false)
   322 function enano_date($string, $timestamp = false)
   323 {
   323 {
   324   if ( !is_int($timestamp) && !is_double($timestamp) && strval(intval($timestamp)) !== $timestamp )
   324 	if ( !is_int($timestamp) && !is_double($timestamp) && strval(intval($timestamp)) !== $timestamp )
   325     $timestamp = time();
   325 		$timestamp = time();
   326   
   326 	
   327   if ( is_int($string) )
   327 	if ( is_int($string) )
   328   {
   328 	{
   329     global $session, $lang;
   329 		global $session, $lang;
   330     $date_fmt = is_object($session) ? $session->date_format : DATE_4;
   330 		$date_fmt = is_object($session) ? $session->date_format : DATE_4;
   331     $time_fmt = is_object($session) ? $session->time_format : TIME_24_NS;
   331 		$time_fmt = is_object($session) ? $session->time_format : TIME_24_NS;
   332     
   332 		
   333     // within a week? use a relative date
   333 		// within a week? use a relative date
   334     if ( $timestamp + ( 86400 * 7 ) >= time() && $string & ED_DATE && is_object($lang) && is_object($session) && !($string & ED_DATE_FULL) )
   334 		if ( $timestamp + ( 86400 * 7 ) >= time() && $string & ED_DATE && is_object($lang) && is_object($session) && !($string & ED_DATE_FULL) )
   335     {
   335 		{
   336       $relative_date = get_relative_date($timestamp);
   336 			$relative_date = get_relative_date($timestamp);
   337       if ( $string === ED_DATE )
   337 			if ( $string === ED_DATE )
   338         // why do more work if we're done?
   338 				// why do more work if we're done?
   339         return $relative_date;
   339 				return $relative_date;
   340     }
   340 		}
   341     
   341 		
   342     $flags = $string;
   342 		$flags = $string;
   343     $string = array();
   343 		$string = array();
   344     if ( $flags & ED_DATE && !isset($relative_date) )
   344 		if ( $flags & ED_DATE && !isset($relative_date) )
   345       $string[] = $date_fmt;
   345 			$string[] = $date_fmt;
   346     if ( $flags & ED_TIME )
   346 		if ( $flags & ED_TIME )
   347       $string[] = $time_fmt;
   347 			$string[] = $time_fmt;
   348     
   348 		
   349     $string = implode(' ', $string);
   349 		$string = implode(' ', $string);
   350   }
   350 	}
   351   
   351 	
   352   // perform timestamp offset
   352 	// perform timestamp offset
   353   global $timezone;
   353 	global $timezone;
   354   // it's gonna be in minutes, so multiply by 60 to offset the unix timestamp
   354 	// it's gonna be in minutes, so multiply by 60 to offset the unix timestamp
   355   $timestamp = $timestamp + ( $timezone * 60 );
   355 	$timestamp = $timestamp + ( $timezone * 60 );
   356   
   356 	
   357   // are we in DST?
   357 	// are we in DST?
   358   global $dst_params;
   358 	global $dst_params;
   359   $dst_offset = 0;
   359 	$dst_offset = 0;
   360   if ( check_timestamp_dst($timestamp, $dst_params[0], $dst_params[1], $dst_params[2], $dst_params[3]) )
   360 	if ( check_timestamp_dst($timestamp, $dst_params[0], $dst_params[1], $dst_params[2], $dst_params[3]) )
   361   {
   361 	{
   362     // offset for DST
   362 		// offset for DST
   363     $timestamp += ( $dst_params[4] * 60 );
   363 		$timestamp += ( $dst_params[4] * 60 );
   364     $dst_offset = $dst_params[4];
   364 		$dst_offset = $dst_params[4];
   365   }
   365 	}
   366   
   366 	
   367   // Does this date string include a timezone? If so, gmdate() will report UTC, which is wrong
   367 	// Does this date string include a timezone? If so, gmdate() will report UTC, which is wrong
   368   // FIXME This is kind of a halfass replacement...
   368 	// FIXME This is kind of a halfass replacement...
   369   foreach ( array('e', 'T', 'O', 'P') as $char )
   369 	foreach ( array('e', 'T', 'O', 'P') as $char )
   370   {
   370 	{
   371     if ( ($pos = strpos($string, $char)) !== false )
   371 		if ( ($pos = strpos($string, $char)) !== false )
   372     {
   372 		{
   373       if ( $string{ $pos - 1 } != '\\' )
   373 			if ( $string{ $pos - 1 } != '\\' )
   374       {
   374 			{
   375         // add in our own timezone string
   375 				// add in our own timezone string
   376         // FIXME: l10n? (do we need to? does anyone really not know what "GMT" means? even uglier escaping?)
   376 				// FIXME: l10n? (do we need to? does anyone really not know what "GMT" means? even uglier escaping?)
   377         $tzi = '\\G\\M\\T';
   377 				$tzi = '\\G\\M\\T';
   378         $tzo = $timezone + $dst_offset;
   378 				$tzo = $timezone + $dst_offset;
   379         $sign = $tzo > 0 ? '+' : '-';
   379 				$sign = $tzo > 0 ? '+' : '-';
   380         $tzi .= $sign . (intval(abs($tzo / 60)));
   380 				$tzi .= $sign . (intval(abs($tzo / 60)));
   381         if ( $tzo % 60 )
   381 				if ( $tzo % 60 )
   382           $tzi .= sprintf(":%02d", abs($tzo) % 60);
   382 					$tzi .= sprintf(":%02d", abs($tzo) % 60);
   383         
   383 				
   384         $string = substr($string, 0, $pos) . $tzi . substr($string, $pos + 1);
   384 				$string = substr($string, 0, $pos) . $tzi . substr($string, $pos + 1);
   385       }
   385 			}
   386     }
   386 		}
   387   }
   387 	}
   388   
   388 	
   389   // Let PHP do the work for us =)
   389 	// Let PHP do the work for us =)
   390   $result = gmdate($string, $timestamp);
   390 	$result = gmdate($string, $timestamp);
   391   if ( isset($relative_date) )
   391 	if ( isset($relative_date) )
   392   {
   392 	{
   393     $result = "$relative_date, $result";
   393 		$result = "$relative_date, $result";
   394   }
   394 	}
   395   return $result;
   395 	return $result;
   396 }
   396 }
   397 
   397 
   398 /**
   398 /**
   399  * Get a relative date ("Today"/"Yesterday"/"N days ago")
   399  * Get a relative date ("Today"/"Yesterday"/"N days ago")
   400  * @param int Timestamp
   400  * @param int Timestamp
   401  * @return string
   401  * @return string
   402  */
   402  */
   403 
   403 
   404 function get_relative_date($time)
   404 function get_relative_date($time)
   405 {
   405 {
   406   global $lang, $session;
   406 	global $lang, $session;
   407   // Our formatting string to pass to enano_date()
   407 	// Our formatting string to pass to enano_date()
   408   // This should not include minute/second info, only today's date in whatever format suits your fancy
   408 	// This should not include minute/second info, only today's date in whatever format suits your fancy
   409   $formatstring = $session->date_format;
   409 	$formatstring = $session->date_format;
   410   // Today's date
   410 	// Today's date
   411   $today = enano_date($formatstring);
   411 	$today = enano_date($formatstring);
   412   // Yesterday's date
   412 	// Yesterday's date
   413   $yesterday = enano_date($formatstring, (time() - (24*60*60)));
   413 	$yesterday = enano_date($formatstring, (time() - (24*60*60)));
   414   // Date on the input
   414 	// Date on the input
   415   $then = enano_date($formatstring, $time);
   415 	$then = enano_date($formatstring, $time);
   416   // "X days ago" logic
   416 	// "X days ago" logic
   417   for ( $i = 2; $i <= 6; $i++ )
   417 	for ( $i = 2; $i <= 6; $i++ )
   418   {
   418 	{
   419     // hours_in_day * minutes_in_hour * seconds_in_minute * num_days
   419 		// hours_in_day * minutes_in_hour * seconds_in_minute * num_days
   420     $offset = 24 * 60 * 60 * $i;
   420 		$offset = 24 * 60 * 60 * $i;
   421     $days_ago = enano_date($formatstring, (time() - $offset));
   421 		$days_ago = enano_date($formatstring, (time() - $offset));
   422     // so does the input timestamp match the date from $i days ago?
   422 		// so does the input timestamp match the date from $i days ago?
   423     if ( $then == $days_ago )
   423 		if ( $then == $days_ago )
   424     {
   424 		{
   425       // yes, return $i
   425 			// yes, return $i
   426       return $lang->get('userfuncs_ml_date_daysago', array('days_ago' => $i));
   426 			return $lang->get('userfuncs_ml_date_daysago', array('days_ago' => $i));
   427     }
   427 		}
   428   }
   428 	}
   429   // either yesterday, today, or before 6 days ago
   429 	// either yesterday, today, or before 6 days ago
   430   switch($then)
   430 	switch($then)
   431   {
   431 	{
   432     case $today:
   432 		case $today:
   433       return $lang->get('userfuncs_ml_date_today');
   433 			return $lang->get('userfuncs_ml_date_today');
   434     case $yesterday:
   434 		case $yesterday:
   435       return $lang->get('userfuncs_ml_date_yesterday');
   435 			return $lang->get('userfuncs_ml_date_yesterday');
   436     default:
   436 		default:
   437       return $then;
   437 			return $then;
   438   }
   438 	}
   439   //     .--.
   439 	//     .--.
   440   //    |o_o |
   440 	//    |o_o |
   441   //    |!_/ |
   441 	//    |!_/ |
   442   //   //   \ \
   442 	//   //   \ \
   443   //  (|     | )
   443 	//  (|     | )
   444   // /'\_   _/`\
   444 	// /'\_   _/`\
   445   // \___)=(___/
   445 	// \___)=(___/
   446   return 'Linux rocks!';
   446 	return 'Linux rocks!';
   447 }
   447 }
   448 
   448 
   449 /**
   449 /**
   450  * Determine if a timestamp is within DST.
   450  * Determine if a timestamp is within DST.
   451  * @param int Timestamp
   451  * @param int Timestamp
   456  * @return bool
   456  * @return bool
   457  */
   457  */
   458 
   458 
   459 function check_timestamp_dst($time, $start_month, $start_sunday, $end_month, $end_sunday)
   459 function check_timestamp_dst($time, $start_month, $start_sunday, $end_month, $end_sunday)
   460 {
   460 {
   461   static $sundays = array(FIRST_SUNDAY, SECOND_SUNDAY, THIRD_SUNDAY, LAST_SUNDAY);
   461 	static $sundays = array(FIRST_SUNDAY, SECOND_SUNDAY, THIRD_SUNDAY, LAST_SUNDAY);
   462   
   462 	
   463   // perform timestamp offset
   463 	// perform timestamp offset
   464   global $timezone;
   464 	global $timezone;
   465   // it's gonna be in minutes, so multiply by 60 to offset the unix timestamp
   465 	// it's gonna be in minutes, so multiply by 60 to offset the unix timestamp
   466   $time = $time + ( $timezone * 60 );
   466 	$time = $time + ( $timezone * 60 );
   467   $year = intval(gmdate('Y', $time));
   467 	$year = intval(gmdate('Y', $time));
   468   
   468 	
   469   // one-pass validation
   469 	// one-pass validation
   470   if ( !in_array($start_sunday, $sundays) || !in_array($end_sunday, $sundays) ||
   470 	if ( !in_array($start_sunday, $sundays) || !in_array($end_sunday, $sundays) ||
   471        $start_month < 1 || $start_month > 12 || $end_month < 1 || $end_month > 12 )
   471  			$start_month < 1 || $start_month > 12 || $end_month < 1 || $end_month > 12 )
   472     return false;
   472 		return false;
   473     
   473 		
   474   // get timestamp of the selected sunday (start)
   474 	// get timestamp of the selected sunday (start)
   475   $dst_start = get_sunday_timestamp($start_month, $start_sunday, $year);
   475 	$dst_start = get_sunday_timestamp($start_month, $start_sunday, $year);
   476   $dst_end   = get_sunday_timestamp($end_month, $end_sunday, $year);
   476 	$dst_end   = get_sunday_timestamp($end_month, $end_sunday, $year);
   477   
   477 	
   478   if ( $dst_start > $dst_end )
   478 	if ( $dst_start > $dst_end )
   479   {
   479 	{
   480     // start time is past the end time, this means we're in the southern hemisphere
   480 		// start time is past the end time, this means we're in the southern hemisphere
   481     // as a result, if we're within the range, DST is NOT in progress.
   481 		// as a result, if we're within the range, DST is NOT in progress.
   482     return !( $time >= $dst_start && $time <= $dst_end );
   482 		return !( $time >= $dst_start && $time <= $dst_end );
   483   }
   483 	}
   484   
   484 	
   485   return $time >= $dst_start && $time <= $dst_end;
   485 	return $time >= $dst_start && $time <= $dst_end;
   486 }
   486 }
   487 
   487 
   488 /**
   488 /**
   489  * Returns a timestamp for the given *_SUNDAY index.
   489  * Returns a timestamp for the given *_SUNDAY index.
   490  * @param int Month
   490  * @param int Month
   493  * @return int
   493  * @return int
   494  */
   494  */
   495 
   495 
   496 function get_sunday_timestamp($month, $sunday, $year)
   496 function get_sunday_timestamp($month, $sunday, $year)
   497 {
   497 {
   498   $days_in_month = array(
   498 	$days_in_month = array(
   499     1 => 31,
   499 		1 => 31,
   500     2 => $year % 4 == 0 && ( $year % 100 != 0 || ( $year % 100 == 0 && $year % 400 == 0 ) ) ? 29 : 28,
   500 		2 => $year % 4 == 0 && ( $year % 100 != 0 || ( $year % 100 == 0 && $year % 400 == 0 ) ) ? 29 : 28,
   501     3 => 31,
   501 		3 => 31,
   502     4 => 30,
   502 		4 => 30,
   503     5 => 31,
   503 		5 => 31,
   504     6 => 30,
   504 		6 => 30,
   505     7 => 31,
   505 		7 => 31,
   506     8 => 31,
   506 		8 => 31,
   507     9 => 30,
   507 		9 => 30,
   508     10 => 31,
   508 		10 => 31,
   509     11 => 30,
   509 		11 => 30,
   510     12 => 31
   510 		12 => 31
   511   );
   511 	);
   512   
   512 	
   513   $result = mktime(0, 0, 0, $month, 1, $year);
   513 	$result = mktime(0, 0, 0, $month, 1, $year);
   514   
   514 	
   515   // hack. allows a specific day of the month to be set instead of a sunday. not a good place to do this.
   515 	// hack. allows a specific day of the month to be set instead of a sunday. not a good place to do this.
   516   if ( is_string($sunday) && substr($sunday, -1) === 'd' )
   516 	if ( is_string($sunday) && substr($sunday, -1) === 'd' )
   517   {
   517 	{
   518     $result += 86400 * ( intval($sunday) - 1);
   518 		$result += 86400 * ( intval($sunday) - 1);
   519     return $result;
   519 		return $result;
   520   }
   520 	}
   521   
   521 	
   522   $tick = 0;
   522 	$tick = 0;
   523   $days_remaining = $days_in_month[$month];
   523 	$days_remaining = $days_in_month[$month];
   524   while ( true )
   524 	while ( true )
   525   {
   525 	{
   526     if ( date('D', $result) == 'Sun' )
   526 		if ( date('D', $result) == 'Sun' )
   527     {
   527 		{
   528       $tick++;
   528 			$tick++;
   529       if ( ( $tick == 1 && $sunday == FIRST_SUNDAY ) ||
   529 			if ( ( $tick == 1 && $sunday == FIRST_SUNDAY ) ||
   530            ( $tick == 2 && $sunday == SECOND_SUNDAY ) ||
   530  					( $tick == 2 && $sunday == SECOND_SUNDAY ) ||
   531            ( $tick == 3 && $sunday == THIRD_SUNDAY ) ||
   531  					( $tick == 3 && $sunday == THIRD_SUNDAY ) ||
   532            ( $sunday == LAST_SUNDAY && $days_remaining < 7 ) )
   532  					( $sunday == LAST_SUNDAY && $days_remaining < 7 ) )
   533         break;
   533 				break;
   534     }
   534 		}
   535     $days_remaining--;
   535 		$days_remaining--;
   536     $result += 86400;
   536 		$result += 86400;
   537   }
   537 	}
   538   
   538 	
   539   return $result;
   539 	return $result;
   540 }
   540 }
   541 
   541 
   542 /**
   542 /**
   543  * Tells you the title for the given page ID string
   543  * Tells you the title for the given page ID string
   544  * @param string Page ID string (ex: Special:Administration)
   544  * @param string Page ID string (ex: Special:Administration)
   546  * @return string
   546  * @return string
   547  */
   547  */
   548 
   548 
   549 function get_page_title($page_id, $show_ns = true)
   549 function get_page_title($page_id, $show_ns = true)
   550 {
   550 {
   551   global $db, $session, $paths, $template, $plugins; // Common objects
   551 	global $db, $session, $paths, $template, $plugins; // Common objects
   552 
   552 
   553   $idata = RenderMan::strToPageID($page_id);
   553 	$idata = RenderMan::strToPageID($page_id);
   554   return get_page_title_ns($idata[0], $idata[1]);
   554 	return get_page_title_ns($idata[0], $idata[1]);
   555 }
   555 }
   556 
   556 
   557 /**
   557 /**
   558  * Tells you the title for the given page ID and namespace
   558  * Tells you the title for the given page ID and namespace
   559  * @param string Page ID
   559  * @param string Page ID
   561  * @return string
   561  * @return string
   562  */
   562  */
   563 
   563 
   564 function get_page_title_ns($page_id, $namespace)
   564 function get_page_title_ns($page_id, $namespace)
   565 {
   565 {
   566   global $db, $session, $paths, $template, $plugins; // Common objects
   566 	global $db, $session, $paths, $template, $plugins; // Common objects
   567   
   567 	
   568   $ns = namespace_factory($page_id, $namespace);
   568 	$ns = namespace_factory($page_id, $namespace);
   569   return $ns->title;
   569 	return $ns->title;
   570 }
   570 }
   571 
   571 
   572 /**
   572 /**
   573  * Redirect the user to the specified URL.
   573  * Redirect the user to the specified URL.
   574  * @param string $url The URL, either relative or absolute.
   574  * @param string $url The URL, either relative or absolute.
   577  * @param string $timeout Timeout, in seconds, to delay the redirect. Defaults to 3. If 0, sends a 307 Temporary Redirect.
   577  * @param string $timeout Timeout, in seconds, to delay the redirect. Defaults to 3. If 0, sends a 307 Temporary Redirect.
   578  */
   578  */
   579 
   579 
   580 function redirect($url, $title = 'etc_redirect_title', $message = 'etc_redirect_body', $timeout = 3)
   580 function redirect($url, $title = 'etc_redirect_title', $message = 'etc_redirect_body', $timeout = 3)
   581 {
   581 {
   582   global $db, $session, $paths, $template, $plugins; // Common objects
   582 	global $db, $session, $paths, $template, $plugins; // Common objects
   583   global $lang;
   583 	global $lang;
   584 
   584 
   585   // POST check added in 1.1.x because Firefox 3.0 asks us if we want to "resend the form
   585 	// POST check added in 1.1.x because Firefox 3.0 asks us if we want to "resend the form
   586   // data to the new location", which can be confusing for some users.
   586 	// data to the new location", which can be confusing for some users.
   587   $is_firefox_3 = ( strstr(@$_SERVER['HTTP_USER_AGENT'], 'Firefox/3.') ) ? true : false;
   587 	$is_firefox_3 = ( strstr(@$_SERVER['HTTP_USER_AGENT'], 'Firefox/3.') ) ? true : false;
   588   if ( $timeout == 0 && ( empty($_POST) || !$is_firefox_3 ) )
   588 	if ( $timeout == 0 && ( empty($_POST) || !$is_firefox_3 ) )
   589   {
   589 	{
   590     header('Location: ' . $url);
   590 		header('Location: ' . $url);
   591     header('Content-length: 0');
   591 		header('Content-length: 0');
   592     header('HTTP/1.1 307 Temporary Redirect');
   592 		header('HTTP/1.1 307 Temporary Redirect');
   593     
   593 		
   594     // with 3xx codes HTTP clients expect a response of 0 bytes, so just die here
   594 		// with 3xx codes HTTP clients expect a response of 0 bytes, so just die here
   595     exit();
   595 		exit();
   596   }
   596 	}
   597   
   597 	
   598   if ( !is_object($template) )
   598 	if ( !is_object($template) )
   599   {
   599 	{
   600     $template = new template_nodb();
   600 		$template = new template_nodb();
   601     $template->load_theme('oxygen', 'bleu', false);
   601 		$template->load_theme('oxygen', 'bleu', false);
   602     $template->assign_vars(array(
   602 		$template->assign_vars(array(
   603         'SITE_NAME' => 'Enano',
   603 				'SITE_NAME' => 'Enano',
   604         'SITE_DESC' => 'This site is experiencing a critical error and cannot load.',
   604 				'SITE_DESC' => 'This site is experiencing a critical error and cannot load.',
   605         'COPYRIGHT' => 'Powered by Enano CMS - &copy; 2006-2008 Dan Fuhry. This program is Free Software; see the <a href="' . scriptPath . '/install.php?mode=license">GPL file</a> included with this package for details.',
   605 				'COPYRIGHT' => 'Powered by Enano CMS - &copy; 2006-2008 Dan Fuhry. This program is Free Software; see the <a href="' . scriptPath . '/install.php?mode=license">GPL file</a> included with this package for details.',
   606         'PAGE_NAME' => htmlspecialchars($title)
   606 				'PAGE_NAME' => htmlspecialchars($title)
   607       ));
   607 			));
   608   }
   608 	}
   609 
   609 
   610   $template->add_header('<meta http-equiv="refresh" content="' . $timeout . '; url=' . str_replace('"', '\\"', $url) . '" />');
   610 	$template->add_header('<meta http-equiv="refresh" content="' . $timeout . '; url=' . str_replace('"', '\\"', $url) . '" />');
   611   $template->add_header('<script type="text/javascript">
   611 	$template->add_header('<script type="text/javascript">
   612       function __r() {
   612 			function __r() {
   613         // FUNCTION AUTOMATICALLY GENERATED
   613 				// FUNCTION AUTOMATICALLY GENERATED
   614         window.location="' . str_replace('"', '\\"', $url) . '";
   614 				window.location="' . str_replace('"', '\\"', $url) . '";
   615       }
   615 			}
   616       setTimeout(\'__r();\', ' . $timeout . '000);
   616 			setTimeout(\'__r();\', ' . $timeout . '000);
   617     </script>
   617 		</script>
   618     ');
   618 		');
   619   
   619 	
   620   if ( get_class($template) == 'template_nodb' )
   620 	if ( get_class($template) == 'template_nodb' )
   621     $template->init_vars();
   621 		$template->init_vars();
   622 
   622 
   623   $template->assign_vars(array('PAGE_NAME' => $title));
   623 	$template->assign_vars(array('PAGE_NAME' => $title));
   624   $template->header(true);
   624 	$template->header(true);
   625   echo '<p>' . $message . '</p>';
   625 	echo '<p>' . $message . '</p>';
   626   $subst = array(
   626 	$subst = array(
   627       'timeout' => $timeout,
   627 			'timeout' => $timeout,
   628       'redirect_url' => str_replace('"', '\\"', $url)
   628 			'redirect_url' => str_replace('"', '\\"', $url)
   629     );
   629 		);
   630   echo '<p>' . $lang->get('etc_redirect_timeout', $subst) . '</p>';
   630 	echo '<p>' . $lang->get('etc_redirect_timeout', $subst) . '</p>';
   631   $template->footer(true);
   631 	$template->footer(true);
   632 
   632 
   633   $db->close();
   633 	$db->close();
   634   exit(0);
   634 	exit(0);
   635 
   635 
   636 }
   636 }
   637 
   637 
   638 /**
   638 /**
   639  * Generates a confirmation form if a CSRF check fails. Will terminate execution.
   639  * Generates a confirmation form if a CSRF check fails. Will terminate execution.
   640  */
   640  */
   641 
   641 
   642 function csrf_request_confirm()
   642 function csrf_request_confirm()
   643 {
   643 {
   644   global $db, $session, $paths, $template, $plugins; // Common objects
   644 	global $db, $session, $paths, $template, $plugins; // Common objects
   645   global $lang, $output;
   645 	global $lang, $output;
   646   
   646 	
   647   // If the token was overridden with the correct one, the user confirmed the action using this form. Continue exec.
   647 	// If the token was overridden with the correct one, the user confirmed the action using this form. Continue exec.
   648   if ( isset($_POST['cstok']) || isset($_GET['cstok']) )
   648 	if ( isset($_POST['cstok']) || isset($_GET['cstok']) )
   649   {
   649 	{
   650     // using the if() check makes sure that the token isn't in a cookie, since $_REQUEST includes $_COOKIE.
   650 		// using the if() check makes sure that the token isn't in a cookie, since $_REQUEST includes $_COOKIE.
   651     $token_check =& $_REQUEST['cstok'];
   651 		$token_check =& $_REQUEST['cstok'];
   652     if ( $token_check === $session->csrf_token )
   652 		if ( $token_check === $session->csrf_token )
   653     {
   653 		{
   654       // overridden token matches, continue exec
   654 			// overridden token matches, continue exec
   655       return true;
   655 			return true;
   656     }
   656 		}
   657   }
   657 	}
   658   
   658 	
   659   @ob_end_clean();
   659 	@ob_end_clean();
   660   
   660 	
   661   $output->set_title($lang->get('user_csrf_confirm_title'));
   661 	$output->set_title($lang->get('user_csrf_confirm_title'));
   662   $output->header();
   662 	$output->header();
   663   
   663 	
   664   // initial info
   664 	// initial info
   665   echo '<p>' . $lang->get('user_csrf_confirm_body') . '</p>';
   665 	echo '<p>' . $lang->get('user_csrf_confirm_body') . '</p>';
   666   
   666 	
   667   // start form
   667 	// start form
   668   $form_method = ( empty($_POST) ) ? 'get' : 'post';
   668 	$form_method = ( empty($_POST) ) ? 'get' : 'post';
   669   echo '<form action="' . htmlspecialchars($_SERVER['REQUEST_URI']) . '" method="' . $form_method . '" enctype="multipart/form-data">';
   669 	echo '<form action="' . htmlspecialchars($_SERVER['REQUEST_URI']) . '" method="' . $form_method . '" enctype="multipart/form-data">';
   670   
   670 	
   671   echo '<fieldset enano:expand="closed">';
   671 	echo '<fieldset enano:expand="closed">';
   672   echo '<legend>' . $lang->get('user_csrf_confirm_btn_viewrequest') . '</legend><div>';
   672 	echo '<legend>' . $lang->get('user_csrf_confirm_btn_viewrequest') . '</legend><div>';
   673   
   673 	
   674   if ( empty($_POST) )
   674 	if ( empty($_POST) )
   675   {
   675 	{
   676     // GET request
   676 		// GET request
   677     echo csrf_confirm_get_recursive();
   677 		echo csrf_confirm_get_recursive();
   678   }
   678 	}
   679   else
   679 	else
   680   {
   680 	{
   681     // POST request
   681 		// POST request
   682     echo csrf_confirm_post_recursive();
   682 		echo csrf_confirm_post_recursive();
   683   }
   683 	}
   684   echo '</div></fieldset>';
   684 	echo '</div></fieldset>';
   685   // insert the right CSRF token
   685 	// insert the right CSRF token
   686   echo '<input type="hidden" name="cstok" value="' . $session->csrf_token . '" />';
   686 	echo '<input type="hidden" name="cstok" value="' . $session->csrf_token . '" />';
   687   echo '<p><input type="submit" value="' . $lang->get('user_csrf_confirm_btn_continue') . '" /></p>';
   687 	echo '<p><input type="submit" value="' . $lang->get('user_csrf_confirm_btn_continue') . '" /></p>';
   688   echo '</form><script type="text/javascript">addOnloadHook(function(){load_component(\'expander\');});</script>';
   688 	echo '</form><script type="text/javascript">addOnloadHook(function(){load_component(\'expander\');});</script>';
   689   
   689 	
   690   $output->footer();
   690 	$output->footer();
   691   
   691 	
   692   exit;
   692 	exit;
   693 }
   693 }
   694 
   694 
   695 function csrf_confirm_get_recursive($_inner = false, $pfx = false, $data = false)
   695 function csrf_confirm_get_recursive($_inner = false, $pfx = false, $data = false)
   696 {
   696 {
   697   // make posted arrays work right
   697 	// make posted arrays work right
   698   if ( !$data )
   698 	if ( !$data )
   699     ( $_inner == 'post' ) ? $data =& $_POST : $data =& $_GET;
   699 		( $_inner == 'post' ) ? $data =& $_POST : $data =& $_GET;
   700   foreach ( $data as $key => $value )
   700 	foreach ( $data as $key => $value )
   701   {
   701 	{
   702     $pfx_this = ( empty($pfx) ) ? $key : "{$pfx}[{$key}]";
   702 		$pfx_this = ( empty($pfx) ) ? $key : "{$pfx}[{$key}]";
   703     if ( is_array($value) )
   703 		if ( is_array($value) )
   704     {
   704 		{
   705       csrf_confirm_get_recursive(true, $pfx_this, $value);
   705 			csrf_confirm_get_recursive(true, $pfx_this, $value);
   706     }
   706 		}
   707     else if ( empty($value) )
   707 		else if ( empty($value) )
   708     {
   708 		{
   709       echo htmlspecialchars($pfx_this . " = <nil>") . "<br />\n";
   709 			echo htmlspecialchars($pfx_this . " = <nil>") . "<br />\n";
   710       echo '<input type="hidden" name="' . htmlspecialchars($pfx_this) . '" value="" />';
   710 			echo '<input type="hidden" name="' . htmlspecialchars($pfx_this) . '" value="" />';
   711     }
   711 		}
   712     else
   712 		else
   713     {
   713 		{
   714       echo htmlspecialchars($pfx_this . " = " . $value) . "<br />\n";
   714 			echo htmlspecialchars($pfx_this . " = " . $value) . "<br />\n";
   715       echo '<input type="hidden" name="' . htmlspecialchars($pfx_this) . '" value="' . htmlspecialchars($value) . '" />';
   715 			echo '<input type="hidden" name="' . htmlspecialchars($pfx_this) . '" value="' . htmlspecialchars($value) . '" />';
   716     }
   716 		}
   717   }
   717 	}
   718 }
   718 }
   719 
   719 
   720 function csrf_confirm_post_recursive()
   720 function csrf_confirm_post_recursive()
   721 {
   721 {
   722   csrf_confirm_get_recursive('post');
   722 	csrf_confirm_get_recursive('post');
   723 }
   723 }
   724 
   724 
   725 // Removed wikiFormat() from here, replaced with RenderMan::render
   725 // Removed wikiFormat() from here, replaced with RenderMan::render
   726 
   726 
   727 /**
   727 /**
   730  * @return bool True if the page exists, false otherwise
   730  * @return bool True if the page exists, false otherwise
   731  */
   731  */
   732 
   732 
   733 function isPage($p)
   733 function isPage($p)
   734 {
   734 {
   735   global $db, $session, $paths, $template, $plugins; // Common objects
   735 	global $db, $session, $paths, $template, $plugins; // Common objects
   736   static $ispage_cache = array();
   736 	static $ispage_cache = array();
   737   if ( isset($ispage_cache[$p]) )
   737 	if ( isset($ispage_cache[$p]) )
   738     return $ispage_cache[$p];
   738 		return $ispage_cache[$p];
   739   
   739 	
   740   list($page_id, $namespace) = RenderMan::strToPageID($p);
   740 	list($page_id, $namespace) = RenderMan::strToPageID($p);
   741   $cdata = $paths->get_cdata($page_id, $namespace);
   741 	$cdata = $paths->get_cdata($page_id, $namespace);
   742   if ( !isset($cdata['page_exists']) )
   742 	if ( !isset($cdata['page_exists']) )
   743   {
   743 	{
   744     $class = ( class_exists($_ = "Namespace_$namespace") ) ? $_ : "Namespace_Default";
   744 		$class = ( class_exists($_ = "Namespace_$namespace") ) ? $_ : "Namespace_Default";
   745     $page = new $class($page_id, $namespace);
   745 		$page = new $class($page_id, $namespace);
   746     return $page->exists();
   746 		return $page->exists();
   747   }
   747 	}
   748   
   748 	
   749   $ispage_cache[$p] = $cdata['page_exists'];
   749 	$ispage_cache[$p] = $cdata['page_exists'];
   750   return $cdata['page_exists'];
   750 	return $cdata['page_exists'];
   751 }
   751 }
   752 
   752 
   753 /**
   753 /**
   754  * Returns the appropriate Namespace_* object for a page.
   754  * Returns the appropriate Namespace_* object for a page.
   755  * @param string Page ID
   755  * @param string Page ID
   757  * @param int Revision ID
   757  * @param int Revision ID
   758  */
   758  */
   759 
   759 
   760 function namespace_factory($page_id, $namespace, $revision_id = 0)
   760 function namespace_factory($page_id, $namespace, $revision_id = 0)
   761 {
   761 {
   762   global $db, $session, $paths, $template, $plugins; // Common objects
   762 	global $db, $session, $paths, $template, $plugins; // Common objects
   763   
   763 	
   764   static $objcache = array();
   764 	static $objcache = array();
   765   $pathskey = $paths->get_pathskey($page_id, $namespace) . ":$revision_id";
   765 	$pathskey = $paths->get_pathskey($page_id, $namespace) . ":$revision_id";
   766   if ( isset($objcache[$pathskey]) )
   766 	if ( isset($objcache[$pathskey]) )
   767     return $objcache[$pathskey];
   767 		return $objcache[$pathskey];
   768   
   768 	
   769   if ( !class_exists("Namespace_$namespace") )
   769 	if ( !class_exists("Namespace_$namespace") )
   770   {
   770 	{
   771     if ( file_exists(ENANO_ROOT . "/includes/namespaces/" . strtolower($namespace) . ".php") )
   771 		if ( file_exists(ENANO_ROOT . "/includes/namespaces/" . strtolower($namespace) . ".php") )
   772     {
   772 		{
   773       require(ENANO_ROOT . "/includes/namespaces/" . strtolower($namespace) . ".php");
   773 			require(ENANO_ROOT . "/includes/namespaces/" . strtolower($namespace) . ".php");
   774     }
   774 		}
   775   }
   775 	}
   776   if ( class_exists("Namespace_$namespace") )
   776 	if ( class_exists("Namespace_$namespace") )
   777   {
   777 	{
   778     $class = "Namespace_$namespace";
   778 		$class = "Namespace_$namespace";
   779     $ns = new $class($page_id, $namespace, $revision_id);
   779 		$ns = new $class($page_id, $namespace, $revision_id);
   780     $objcache[$pathskey] = $ns;
   780 		$objcache[$pathskey] = $ns;
   781     return $ns;
   781 		return $ns;
   782   }
   782 	}
   783   else
   783 	else
   784   {
   784 	{
   785     $ns = new Namespace_Default($page_id, $namespace, $revision_id);
   785 		$ns = new Namespace_Default($page_id, $namespace, $revision_id);
   786     $objcache[$pathskey] = $ns;
   786 		$objcache[$pathskey] = $ns;
   787     return $ns;
   787 		return $ns;
   788   }
   788 	}
   789 }
   789 }
   790 
   790 
   791 /**
   791 /**
   792  * These are some old functions that were used with the Midget codebase. They are deprecated and should not be used any more.
   792  * These are some old functions that were used with the Midget codebase. They are deprecated and should not be used any more.
   793  */
   793  */
   794 
   794 
   795 function arrayItemUp($arr, $keyname) {
   795 function arrayItemUp($arr, $keyname) {
   796   $keylist = array_keys($arr);
   796 	$keylist = array_keys($arr);
   797   $keyflop = array_flip($keylist);
   797 	$keyflop = array_flip($keylist);
   798   $idx = $keyflop[$keyname];
   798 	$idx = $keyflop[$keyname];
   799   $idxm = $idx - 1;
   799 	$idxm = $idx - 1;
   800   $temp = $arr[$keylist[$idxm]];
   800 	$temp = $arr[$keylist[$idxm]];
   801   if($arr[$keylist[0]] == $arr[$keyname]) return $arr;
   801 	if($arr[$keylist[0]] == $arr[$keyname]) return $arr;
   802   $arr[$keylist[$idxm]] = $arr[$keylist[$idx]];
   802 	$arr[$keylist[$idxm]] = $arr[$keylist[$idx]];
   803   $arr[$keylist[$idx]] = $temp;
   803 	$arr[$keylist[$idx]] = $temp;
   804   return $arr;
   804 	return $arr;
   805 }
   805 }
   806 
   806 
   807 function arrayItemDown($arr, $keyname) {
   807 function arrayItemDown($arr, $keyname) {
   808   $keylist = array_keys($arr);
   808 	$keylist = array_keys($arr);
   809   $keyflop = array_flip($keylist);
   809 	$keyflop = array_flip($keylist);
   810   $idx = $keyflop[$keyname];
   810 	$idx = $keyflop[$keyname];
   811   $idxm = $idx + 1;
   811 	$idxm = $idx + 1;
   812   $temp = $arr[$keylist[$idxm]];
   812 	$temp = $arr[$keylist[$idxm]];
   813   $sz = sizeof($arr); $sz--;
   813 	$sz = sizeof($arr); $sz--;
   814   if($arr[$keylist[$sz]] == $arr[$keyname]) return $arr;
   814 	if($arr[$keylist[$sz]] == $arr[$keyname]) return $arr;
   815   $arr[$keylist[$idxm]]  =  $arr[$keylist[$idx]];
   815 	$arr[$keylist[$idxm]]  =  $arr[$keylist[$idx]];
   816   $arr[$keylist[$idx]]   =  $temp;
   816 	$arr[$keylist[$idx]]   =  $temp;
   817   return $arr;
   817 	return $arr;
   818 }
   818 }
   819 
   819 
   820 function arrayItemTop($arr, $keyname) {
   820 function arrayItemTop($arr, $keyname) {
   821   $keylist = array_keys($arr);
   821 	$keylist = array_keys($arr);
   822   $keyflop = array_flip($keylist);
   822 	$keyflop = array_flip($keylist);
   823   $idx = $keyflop[$keyname];
   823 	$idx = $keyflop[$keyname];
   824   while( $orig != $arr[$keylist[0]] ) {
   824 	while( $orig != $arr[$keylist[0]] ) {
   825     // echo 'Keyname: '.$keylist[$idx] . '<br />'; flush(); ob_flush(); // Debugger
   825 		// echo 'Keyname: '.$keylist[$idx] . '<br />'; flush(); ob_flush(); // Debugger
   826     if($idx < 0) return $arr;
   826 		if($idx < 0) return $arr;
   827     if($keylist[$idx] == '' || $keylist[$idx] < 0 || !$keylist[$idx]) {
   827 		if($keylist[$idx] == '' || $keylist[$idx] < 0 || !$keylist[$idx]) {
   828       return $arr;
   828 			return $arr;
   829     }
   829 		}
   830     $arr = arrayItemUp($arr, $keylist[$idx]);
   830 		$arr = arrayItemUp($arr, $keylist[$idx]);
   831     $idx--;
   831 		$idx--;
   832   }
   832 	}
   833   return $arr;
   833 	return $arr;
   834 }
   834 }
   835 
   835 
   836 function arrayItemBottom($arr, $keyname) {
   836 function arrayItemBottom($arr, $keyname) {
   837   $b = $arr[$keyname];
   837 	$b = $arr[$keyname];
   838   unset($arr[$keyname]);
   838 	unset($arr[$keyname]);
   839   $arr[$keyname] = $b;
   839 	$arr[$keyname] = $b;
   840   unset($b);
   840 	unset($b);
   841   return $arr;
   841 	return $arr;
   842 }
   842 }
   843 
   843 
   844 /**
   844 /**
   845  * Implementation of array_merge() that preserves key names. $arr2 takes precedence over $arr1.
   845  * Implementation of array_merge() that preserves key names. $arr2 takes precedence over $arr1.
   846  * @param array $arr1
   846  * @param array $arr1
   848  * @return array
   848  * @return array
   849  */
   849  */
   850 
   850 
   851 function enano_safe_array_merge($arr1, $arr2)
   851 function enano_safe_array_merge($arr1, $arr2)
   852 {
   852 {
   853   $arr3 = $arr1;
   853 	$arr3 = $arr1;
   854   foreach($arr2 as $k => $v)
   854 	foreach($arr2 as $k => $v)
   855   {
   855 	{
   856     $arr3[$k] = $v;
   856 		$arr3[$k] = $v;
   857   }
   857 	}
   858   return $arr3;
   858 	return $arr3;
   859 }
   859 }
   860 
   860 
   861 /**
   861 /**
   862  * Looks at all values in an array and casts them to integers if they are strings with digits. Recursive.
   862  * Looks at all values in an array and casts them to integers if they are strings with digits. Recursive.
   863  * @param array Array to process
   863  * @param array Array to process
   864  * @return array
   864  * @return array
   865  */
   865  */
   866 
   866 
   867 function integerize_array($arr)
   867 function integerize_array($arr)
   868 {
   868 {
   869   if ( !is_array($arr) )
   869 	if ( !is_array($arr) )
   870     return $arr;
   870 		return $arr;
   871   
   871 	
   872   foreach ( $arr as &$val )
   872 	foreach ( $arr as &$val )
   873   {
   873 	{
   874     if ( is_string($val) && ctype_digit($val) && strlen($val) < 10 )
   874 		if ( is_string($val) && ctype_digit($val) && strlen($val) < 10 )
   875     {
   875 		{
   876       $val = intval($val);
   876 			$val = intval($val);
   877     }
   877 		}
   878     else if ( is_array($val) )
   878 		else if ( is_array($val) )
   879     {
   879 		{
   880       $val = integerize_array($val);
   880 			$val = integerize_array($val);
   881     }
   881 		}
   882   }
   882 	}
   883   return $arr;
   883 	return $arr;
   884 }
   884 }
   885 
   885 
   886 // Convert IP address to hex string
   886 // Convert IP address to hex string
   887 // Input:  127.0.0.1  (string)
   887 // Input:  127.0.0.1  (string)
   888 // Output: 0x7f000001 (string)
   888 // Output: 0x7f000001 (string)
   889 // Updated 12/8/06 to work with PHP4 and not use eval() (blech)
   889 // Updated 12/8/06 to work with PHP4 and not use eval() (blech)
   890 function ip2hex($ip) {
   890 function ip2hex($ip) {
   891   if ( preg_match('/^([0-9a-f:]+)$/', $ip) )
   891 	if ( preg_match('/^([0-9a-f:]+)$/', $ip) )
   892   {
   892 	{
   893     // this is an ipv6 address
   893 		// this is an ipv6 address
   894     return str_replace(':', '', $ip);
   894 		return str_replace(':', '', $ip);
   895   }
   895 	}
   896   $nums = explode('.', $ip);
   896 	$nums = explode('.', $ip);
   897   if(sizeof($nums) != 4) return false;
   897 	if(sizeof($nums) != 4) return false;
   898   $str = '0x';
   898 	$str = '0x';
   899   foreach($nums as $n)
   899 	foreach($nums as $n)
   900   {
   900 	{
   901     $byte = (string)dechex($n);
   901 		$byte = (string)dechex($n);
   902     if ( strlen($byte) < 2 )
   902 		if ( strlen($byte) < 2 )
   903       $byte = '0' . $byte;
   903 			$byte = '0' . $byte;
   904   }
   904 	}
   905   return $str;
   905 	return $str;
   906 }
   906 }
   907 
   907 
   908 // Convert DWord to IP address
   908 // Convert DWord to IP address
   909 // Input:  0x7f000001
   909 // Input:  0x7f000001
   910 // Output: 127.0.0.1
   910 // Output: 127.0.0.1
   911 // Updated 12/8/06 to work with PHP4 and not use eval() (blech)
   911 // Updated 12/8/06 to work with PHP4 and not use eval() (blech)
   912 function hex2ip($in) {
   912 function hex2ip($in) {
   913   if(substr($in, 0, 2) == '0x') $ip = substr($in, 2, 8);
   913 	if(substr($in, 0, 2) == '0x') $ip = substr($in, 2, 8);
   914   else $ip = substr($in, 0, 8);
   914 	else $ip = substr($in, 0, 8);
   915   $octets = enano_str_split($ip, 2);
   915 	$octets = enano_str_split($ip, 2);
   916   $str = '';
   916 	$str = '';
   917   $newoct = Array();
   917 	$newoct = Array();
   918   foreach($octets as $o)
   918 	foreach($octets as $o)
   919   {
   919 	{
   920     $o = (int)hexdec($o);
   920 		$o = (int)hexdec($o);
   921     $newoct[] = $o;
   921 		$newoct[] = $o;
   922   }
   922 	}
   923   return implode('.', $newoct);
   923 	return implode('.', $newoct);
   924 }
   924 }
   925 
   925 
   926 // Function strip_php moved to RenderMan class
   926 // Function strip_php moved to RenderMan class
   927 
   927 
   928 /**
   928 /**
   934  * @param bool Added in 1.1.3. If true, only the error is output. Defaults to false.
   934  * @param bool Added in 1.1.3. If true, only the error is output. Defaults to false.
   935  */
   935  */
   936 
   936 
   937 function die_semicritical($t, $p, $no_wrapper = false)
   937 function die_semicritical($t, $p, $no_wrapper = false)
   938 {
   938 {
   939   global $db, $session, $paths, $template, $plugins; // Common objects
   939 	global $db, $session, $paths, $template, $plugins; // Common objects
   940   $db->close();
   940 	$db->close();
   941   
   941 	
   942   if ( @ob_get_status() )
   942 	if ( @ob_get_status() )
   943     ob_end_clean();
   943 		ob_end_clean();
   944 
   944 
   945   // If the config hasn't been fetched yet, call grinding_halt.
   945 	// If the config hasn't been fetched yet, call grinding_halt.
   946   if ( !defined('ENANO_CONFIG_FETCHED') )
   946 	if ( !defined('ENANO_CONFIG_FETCHED') )
   947   {
   947 	{
   948     grinding_halt($t, $p);
   948 		grinding_halt($t, $p);
   949   }
   949 	}
   950   
   950 	
   951   // also do grinding_halt() if we're in CLI mode
   951 	// also do grinding_halt() if we're in CLI mode
   952   if ( defined('ENANO_CLI') )
   952 	if ( defined('ENANO_CLI') )
   953   {
   953 	{
   954     grinding_halt($t, $p);
   954 		grinding_halt($t, $p);
   955   }
   955 	}
   956 
   956 
   957   if ( $no_wrapper )
   957 	if ( $no_wrapper )
   958   {
   958 	{
   959     echo '<h2>' . htmlspecialchars($t) . '</h2>';
   959 		echo '<h2>' . htmlspecialchars($t) . '</h2>';
   960     echo "<p>$p</p>";
   960 		echo "<p>$p</p>";
   961     exit;
   961 		exit;
   962   }
   962 	}
   963   
   963 	
   964   $output = new Output_Safe();
   964 	$output = new Output_Safe();
   965   $output->set_title($t);
   965 	$output->set_title($t);
   966   $output->header();
   966 	$output->header();
   967   echo $p;
   967 	echo $p;
   968   $output->footer();
   968 	$output->footer();
   969 
   969 
   970   exit;
   970 	exit;
   971 }
   971 }
   972 
   972 
   973 /**
   973 /**
   974  * Halts Enano execution with a message. This doesn't have to be an error message, it's sometimes used to indicate success at an operation.
   974  * Halts Enano execution with a message. This doesn't have to be an error message, it's sometimes used to indicate success at an operation.
   975  * @param string The title of the message
   975  * @param string The title of the message
   976  * @param string The body of the message, this can be HTML, and should be separated into paragraphs using the <p> tag
   976  * @param string The body of the message, this can be HTML, and should be separated into paragraphs using the <p> tag
   977  */
   977  */
   978 
   978 
   979 function die_friendly($t, $p)
   979 function die_friendly($t, $p)
   980 {
   980 {
   981   global $db, $session, $paths, $template, $plugins; // Common objects
   981 	global $db, $session, $paths, $template, $plugins; // Common objects
   982 
   982 
   983   if ( @ob_get_status() )
   983 	if ( @ob_get_status() )
   984     ob_end_clean();
   984 		ob_end_clean();
   985   
   985 	
   986   global $output;
   986 	global $output;
   987 
   987 
   988   $output->set_title($t);
   988 	$output->set_title($t);
   989   $template->header();
   989 	$template->header();
   990   echo $p;
   990 	echo $p;
   991   $template->footer();
   991 	$template->footer();
   992   $db->close();
   992 	$db->close();
   993 
   993 
   994   exit;
   994 	exit;
   995 }
   995 }
   996 
   996 
   997 /**
   997 /**
   998  * Immediately brings the site to a halt with an error message, and focuses on immediately closing the database connection and shutting down Enano in the event that an attack may happen. This should only be used very early on to indicate very severe errors, or if the site may be under attack (like if the DBAL detects a malicious query). In the vast majority of cases, die_semicritical() is more appropriate.
   998  * Immediately brings the site to a halt with an error message, and focuses on immediately closing the database connection and shutting down Enano in the event that an attack may happen. This should only be used very early on to indicate very severe errors, or if the site may be under attack (like if the DBAL detects a malicious query). In the vast majority of cases, die_semicritical() is more appropriate.
   999  * @param string The title of the error message
   999  * @param string The title of the error message
  1000  * @param string The body of the message, this can be HTML, and should be separated into paragraphs using the <p> tag
  1000  * @param string The body of the message, this can be HTML, and should be separated into paragraphs using the <p> tag
  1001  */
  1001  */
  1002 
  1002 
  1003 function grinding_halt($t, $p)
  1003 function grinding_halt($t, $p)
  1004 {
  1004 {
  1005   global $db, $session, $paths, $template, $plugins; // Common objects
  1005 	global $db, $session, $paths, $template, $plugins; // Common objects
  1006   
  1006 	
  1007   if ( !defined('scriptPath') )
  1007 	if ( !defined('scriptPath') )
  1008     require( ENANO_ROOT . '/config.php' );
  1008 		require( ENANO_ROOT . '/config.php' );
  1009 
  1009 
  1010   if ( is_object($db) )
  1010 	if ( is_object($db) )
  1011     $db->close();
  1011 		$db->close();
  1012   
  1012 	
  1013   if ( @ob_get_status() )
  1013 	if ( @ob_get_status() )
  1014     ob_end_clean();
  1014 		ob_end_clean();
  1015   
  1015 	
  1016   if ( defined('ENANO_CLI') )
  1016 	if ( defined('ENANO_CLI') )
  1017   {
  1017 	{
  1018     // set console color
  1018 		// set console color
  1019     echo "\x1B[31;1m";
  1019 		echo "\x1B[31;1m";
  1020     // error title
  1020 		// error title
  1021     echo "Critical error in Enano runtime: ";
  1021 		echo "Critical error in Enano runtime: ";
  1022     // unbold
  1022 		// unbold
  1023     echo "$t\n";
  1023 		echo "$t\n";
  1024     // bold
  1024 		// bold
  1025     echo "\x1B[37;1m";
  1025 		echo "\x1B[37;1m";
  1026     echo "Error: ";
  1026 		echo "Error: ";
  1027     // unbold
  1027 		// unbold
  1028     echo "\x1B[0m";
  1028 		echo "\x1B[0m";
  1029     echo "$p\n";
  1029 		echo "$p\n";
  1030     exit(1);
  1030 		exit(1);
  1031   }
  1031 	}
  1032   $theme = ( defined('ENANO_CONFIG_FETCHED') ) ? getConfig('theme_default') : 'oxygen';
  1032 	$theme = ( defined('ENANO_CONFIG_FETCHED') ) ? getConfig('theme_default') : 'oxygen';
  1033   $style = ( defined('ENANO_CONFIG_FETCHED') ) ? '__foo__' : 'bleu';
  1033 	$style = ( defined('ENANO_CONFIG_FETCHED') ) ? '__foo__' : 'bleu';
  1034   
  1034 	
  1035   $tpl = new template_nodb();
  1035 	$tpl = new template_nodb();
  1036   $tpl->load_theme($theme, $style);
  1036 	$tpl->load_theme($theme, $style);
  1037   $tpl->tpl_strings['SITE_NAME'] = 'Critical error';
  1037 	$tpl->tpl_strings['SITE_NAME'] = 'Critical error';
  1038   $tpl->tpl_strings['SITE_DESC'] = 'This website is experiencing a serious error and cannot load.';
  1038 	$tpl->tpl_strings['SITE_DESC'] = 'This website is experiencing a serious error and cannot load.';
  1039   $tpl->tpl_strings['COPYRIGHT'] = 'Unable to retrieve copyright information';
  1039 	$tpl->tpl_strings['COPYRIGHT'] = 'Unable to retrieve copyright information';
  1040   $tpl->tpl_strings['PAGE_NAME'] = $t;
  1040 	$tpl->tpl_strings['PAGE_NAME'] = $t;
  1041   $tpl->header();
  1041 	$tpl->header();
  1042   echo $p;
  1042 	echo $p;
  1043   $tpl->footer();
  1043 	$tpl->footer();
  1044   
  1044 	
  1045   exit;
  1045 	exit;
  1046 }
  1046 }
  1047 
  1047 
  1048 /**
  1048 /**
  1049  * Prints out the categorization box found on most regular pages. Doesn't take or return anything, but assumes that the page information is already set in $paths.
  1049  * Prints out the categorization box found on most regular pages. Doesn't take or return anything, but assumes that the page information is already set in $paths.
  1050  */
  1050  */
  1051 
  1051 
  1052 function show_category_info()
  1052 function show_category_info()
  1053 {
  1053 {
  1054   throw new Exception('show_category_info() is deprecated. Use Namespace_*::display_categories().');
  1054 	throw new Exception('show_category_info() is deprecated. Use Namespace_*::display_categories().');
  1055 }
  1055 }
  1056 
  1056 
  1057 /**
  1057 /**
  1058  * Prints out the file information box seen on File: pages. Doesn't take or return anything, but assumes that the page information is already set in $paths, and expects $paths->namespace to be File.
  1058  * Prints out the file information box seen on File: pages. Doesn't take or return anything, but assumes that the page information is already set in $paths, and expects $paths->namespace to be File.
  1059  */
  1059  */
  1060 
  1060 
  1061 function show_file_info($page = false)
  1061 function show_file_info($page = false)
  1062 {
  1062 {
  1063   throw new Exception('show_file_info() is deprecated. Use Namespace_File::show_info().');
  1063 	throw new Exception('show_file_info() is deprecated. Use Namespace_File::show_info().');
  1064 }
  1064 }
  1065 
  1065 
  1066 /**
  1066 /**
  1067  * Shows header information on the current page. Currently this is only the delete-vote feature. Doesn't take or return anything, but assumes that the page information is already set in $paths.
  1067  * Shows header information on the current page. Currently this is only the delete-vote feature. Doesn't take or return anything, but assumes that the page information is already set in $paths.
  1068  */
  1068  */
  1069 
  1069 
  1070 function display_page_headers()
  1070 function display_page_headers()
  1071 {
  1071 {
  1072   // Deprecated.
  1072 	// Deprecated.
  1073 }
  1073 }
  1074 
  1074 
  1075 /**
  1075 /**
  1076  * Displays page footer information including file and category info. This also has the send_page_footers hook. Doesn't take or return anything, but assumes that the page information is already set in $paths.
  1076  * Displays page footer information including file and category info. This also has the send_page_footers hook. Doesn't take or return anything, but assumes that the page information is already set in $paths.
  1077  */
  1077  */
  1078 
  1078 
  1079 function display_page_footers()
  1079 function display_page_footers()
  1080 {
  1080 {
  1081   global $db, $session, $paths, $template, $plugins; // Common objects
  1081 	global $db, $session, $paths, $template, $plugins; // Common objects
  1082   
  1082 	
  1083   if ( isset($_GET['nofooters']) )
  1083 	if ( isset($_GET['nofooters']) )
  1084   {
  1084 	{
  1085     return;
  1085 		return;
  1086   }
  1086 	}
  1087   
  1087 	
  1088   $code = $plugins->setHook('send_page_footers');
  1088 	$code = $plugins->setHook('send_page_footers');
  1089   foreach ( $code as $cmd )
  1089 	foreach ( $code as $cmd )
  1090   {
  1090 	{
  1091     eval($cmd);
  1091 		eval($cmd);
  1092   }
  1092 	}
  1093 }
  1093 }
  1094 
  1094 
  1095 /**
  1095 /**
  1096  * Show the "this is a redirector" notice
  1096  * Show the "this is a redirector" notice
  1097  * @param string Target Page ID
  1097  * @param string Target Page ID
  1098  * @param string Target Namespace
  1098  * @param string Target Namespace
  1099  */
  1099  */
  1100 
  1100 
  1101 function display_redirect_notice($page_id, $namespace)
  1101 function display_redirect_notice($page_id, $namespace)
  1102 {
  1102 {
  1103   global $db, $session, $paths, $template, $plugins; // Common objects
  1103 	global $db, $session, $paths, $template, $plugins; // Common objects
  1104   global $lang, $output;
  1104 	global $lang, $output;
  1105   
  1105 	
  1106   $url = makeUrlNS($namespace, $page_id, false, true);
  1106 	$url = makeUrlNS($namespace, $page_id, false, true);
  1107   $ns = namespace_factory($page_id, $namespace);
  1107 	$ns = namespace_factory($page_id, $namespace);
  1108   $page_data = $ns->get_cdata();
  1108 	$page_data = $ns->get_cdata();
  1109   
  1109 	
  1110   $title = $page_data['name'];
  1110 	$title = $page_data['name'];
  1111   
  1111 	
  1112   $cls = $ns->exists() ? '' : 'class="wikilink-nonexistent" ';
  1112 	$cls = $ns->exists() ? '' : 'class="wikilink-nonexistent" ';
  1113     
  1113 		
  1114   $a = '<a ' . $cls . 'href="' . $url . '">' . $title . '</a>';
  1114 	$a = '<a ' . $cls . 'href="' . $url . '">' . $title . '</a>';
  1115   $redir_html = '<br /><div class="mdg-infobox">
  1115 	$redir_html = '<br /><div class="mdg-infobox">
  1116           <table border="0" width="100%" cellspacing="0" cellpadding="0">
  1116 					<table border="0" width="100%" cellspacing="0" cellpadding="0">
  1117             <tr>
  1117 						<tr>
  1118               <td valign="top">
  1118 							<td valign="top">
  1119                 <img alt="Cute wet-floor icon" src="' . cdnPath . '/images/redirector.png" />
  1119 								<img alt="Cute wet-floor icon" src="' . cdnPath . '/images/redirector.png" />
  1120               </td>
  1120 							</td>
  1121               <td valign="top" style="padding-left: 10px;">
  1121 							<td valign="top" style="padding-left: 10px;">
  1122                 ' . $lang->get('page_msg_this_is_a_redirector', array( 'redirect_target' => $a )) . '
  1122 								' . $lang->get('page_msg_this_is_a_redirector', array( 'redirect_target' => $a )) . '
  1123               </td>
  1123 							</td>
  1124             </tr>
  1124 						</tr>
  1125           </table>
  1125 					</table>
  1126         </div>
  1126 				</div>
  1127         <br />
  1127 				<br />
  1128         <hr style="margin-left: 1em; width: 200px;" />';
  1128 				<hr style="margin-left: 1em; width: 200px;" />';
  1129   
  1129 	
  1130   $output->add_after_header($redir_html);
  1130 	$output->add_after_header($redir_html);
  1131 }
  1131 }
  1132 
  1132 
  1133 /**
  1133 /**
  1134  * Essentially an return code reader for a socket. Don't use this unless you're writing mail code and smtp_send_email doesn't cut it. Ported from phpBB's smtp.php.
  1134  * Essentially an return code reader for a socket. Don't use this unless you're writing mail code and smtp_send_email doesn't cut it. Ported from phpBB's smtp.php.
  1135  * @param socket A socket resource
  1135  * @param socket A socket resource
  1136  * @param string The expected response from the server, this needs to be exactly three characters.
  1136  * @param string The expected response from the server, this needs to be exactly three characters.
  1137  */
  1137  */
  1138 
  1138 
  1139 function smtp_get_response($socket, $response, $line = __LINE__)
  1139 function smtp_get_response($socket, $response, $line = __LINE__)
  1140 {
  1140 {
  1141   $server_response = '';
  1141 	$server_response = '';
  1142   while (substr($server_response, 3, 1) != ' ')
  1142 	while (substr($server_response, 3, 1) != ' ')
  1143   {
  1143 	{
  1144     if (!($server_response = fgets($socket, 256)))
  1144 		if (!($server_response = fgets($socket, 256)))
  1145     {
  1145 		{
  1146       die_friendly('SMTP Error', "<p>Couldn't get mail server response codes</p>");
  1146 			die_friendly('SMTP Error', "<p>Couldn't get mail server response codes</p>");
  1147     }
  1147 		}
  1148   }
  1148 	}
  1149 
  1149 
  1150   if (!(substr($server_response, 0, 3) == $response))
  1150 	if (!(substr($server_response, 0, 3) == $response))
  1151   {
  1151 	{
  1152     die_friendly('SMTP Error', "<p>Ran into problems sending mail. Response: $server_response</p>");
  1152 		die_friendly('SMTP Error', "<p>Ran into problems sending mail. Response: $server_response</p>");
  1153   }
  1153 	}
  1154 }
  1154 }
  1155 
  1155 
  1156 /**
  1156 /**
  1157  * Wrapper for smtp_send_email_core that takes the sender as the fourth parameter instead of additional headers.
  1157  * Wrapper for smtp_send_email_core that takes the sender as the fourth parameter instead of additional headers.
  1158  * @param string E-mail address to send to
  1158  * @param string E-mail address to send to
  1161  * @param string Address of the sender
  1161  * @param string Address of the sender
  1162  */
  1162  */
  1163 
  1163 
  1164 function smtp_send_email($to, $subject, $message, $from)
  1164 function smtp_send_email($to, $subject, $message, $from)
  1165 {
  1165 {
  1166   return smtp_send_email_core($to, $subject, $message, "From: <$from>\n");
  1166 	return smtp_send_email_core($to, $subject, $message, "From: <$from>\n");
  1167 }
  1167 }
  1168 
  1168 
  1169 /**
  1169 /**
  1170  * Replacement or substitute for PHP's mail() builtin function.
  1170  * Replacement or substitute for PHP's mail() builtin function.
  1171  * @param string E-mail address to send to
  1171  * @param string E-mail address to send to
  1176  * @license GPL
  1176  * @license GPL
  1177  */
  1177  */
  1178 
  1178 
  1179 function smtp_send_email_core($mail_to, $subject, $message, $headers = '')
  1179 function smtp_send_email_core($mail_to, $subject, $message, $headers = '')
  1180 {
  1180 {
  1181   // Fix any bare linefeeds in the message to make it RFC821 Compliant.
  1181 	// Fix any bare linefeeds in the message to make it RFC821 Compliant.
  1182   $message = preg_replace("#(?<!\r)\n#si", "\r\n", $message);
  1182 	$message = preg_replace("#(?<!\r)\n#si", "\r\n", $message);
  1183 
  1183 
  1184   if ($headers != '')
  1184 	if ($headers != '')
  1185   {
  1185 	{
  1186     if (is_array($headers))
  1186 		if (is_array($headers))
  1187     {
  1187 		{
  1188       if (sizeof($headers) > 1)
  1188 			if (sizeof($headers) > 1)
  1189       {
  1189 			{
  1190         $headers = join("\n", $headers);
  1190 				$headers = join("\n", $headers);
  1191       }
  1191 			}
  1192       else
  1192 			else
  1193       {
  1193 			{
  1194         $headers = $headers[0];
  1194 				$headers = $headers[0];
  1195       }
  1195 			}
  1196     }
  1196 		}
  1197     $headers = chop($headers);
  1197 		$headers = chop($headers);
  1198 
  1198 
  1199     // Make sure there are no bare linefeeds in the headers
  1199 		// Make sure there are no bare linefeeds in the headers
  1200     $headers = preg_replace('#(?<!\r)\n#si', "\r\n", $headers);
  1200 		$headers = preg_replace('#(?<!\r)\n#si', "\r\n", $headers);
  1201 
  1201 
  1202     // Ok this is rather confusing all things considered,
  1202 		// Ok this is rather confusing all things considered,
  1203     // but we have to grab bcc and cc headers and treat them differently
  1203 		// but we have to grab bcc and cc headers and treat them differently
  1204     // Something we really didn't take into consideration originally
  1204 		// Something we really didn't take into consideration originally
  1205     $header_array = explode("\r\n", $headers);
  1205 		$header_array = explode("\r\n", $headers);
  1206     @reset($header_array);
  1206 		@reset($header_array);
  1207 
  1207 
  1208     $headers = '';
  1208 		$headers = '';
  1209     $cc = '';
  1209 		$cc = '';
  1210     $bcc = '';
  1210 		$bcc = '';
  1211     while(list(, $header) = each($header_array))
  1211 		while(list(, $header) = each($header_array))
  1212     {
  1212 		{
  1213       if (preg_match('#^cc:#si', $header))
  1213 			if (preg_match('#^cc:#si', $header))
  1214       {
  1214 			{
  1215         $cc = preg_replace('#^cc:(.*)#si', '\1', $header);
  1215 				$cc = preg_replace('#^cc:(.*)#si', '\1', $header);
  1216       }
  1216 			}
  1217       else if (preg_match('#^bcc:#si', $header))
  1217 			else if (preg_match('#^bcc:#si', $header))
  1218       {
  1218 			{
  1219         $bcc = preg_replace('#^bcc:(.*)#si', '\1', $header);
  1219 				$bcc = preg_replace('#^bcc:(.*)#si', '\1', $header);
  1220         $header = '';
  1220 				$header = '';
  1221       }
  1221 			}
  1222       $headers .= ($header != '') ? $header . "\r\n" : '';
  1222 			$headers .= ($header != '') ? $header . "\r\n" : '';
  1223     }
  1223 		}
  1224 
  1224 
  1225     $headers = chop($headers);
  1225 		$headers = chop($headers);
  1226     $cc = explode(', ', $cc);
  1226 		$cc = explode(', ', $cc);
  1227     $bcc = explode(', ', $bcc);
  1227 		$bcc = explode(', ', $bcc);
  1228   }
  1228 	}
  1229 
  1229 
  1230   if (trim($subject) == '')
  1230 	if (trim($subject) == '')
  1231   {
  1231 	{
  1232     die_friendly(GENERAL_ERROR, "No email Subject specified");
  1232 		die_friendly(GENERAL_ERROR, "No email Subject specified");
  1233   }
  1233 	}
  1234 
  1234 
  1235   if (trim($message) == '')
  1235 	if (trim($message) == '')
  1236   {
  1236 	{
  1237     die_friendly(GENERAL_ERROR, "Email message was blank");
  1237 		die_friendly(GENERAL_ERROR, "Email message was blank");
  1238   }
  1238 	}
  1239 
  1239 
  1240   // setup SMTP
  1240 	// setup SMTP
  1241   $host = getConfig('smtp_server');
  1241 	$host = getConfig('smtp_server');
  1242   if ( empty($host) )
  1242 	if ( empty($host) )
  1243     return 'No smtp_host in config';
  1243 		return 'No smtp_host in config';
  1244   if ( strstr($host, ':' ) )
  1244 	if ( strstr($host, ':' ) )
  1245   {
  1245 	{
  1246     $n = explode(':', $host);
  1246 		$n = explode(':', $host);
  1247     $smtp_host = $n[0];
  1247 		$smtp_host = $n[0];
  1248     $port = intval($n[1]);
  1248 		$port = intval($n[1]);
  1249   }
  1249 	}
  1250   else
  1250 	else
  1251   {
  1251 	{
  1252     $smtp_host = $host;
  1252 		$smtp_host = $host;
  1253     $port = 25;
  1253 		$port = 25;
  1254   }
  1254 	}
  1255 
  1255 
  1256   $smtp_user = getConfig('smtp_user');
  1256 	$smtp_user = getConfig('smtp_user');
  1257   $smtp_pass = getConfig('smtp_password');
  1257 	$smtp_pass = getConfig('smtp_password');
  1258 
  1258 
  1259   // Ok we have error checked as much as we can to this point let's get on
  1259 	// Ok we have error checked as much as we can to this point let's get on
  1260   // it already.
  1260 	// it already.
  1261   if( !$socket = @fsockopen($smtp_host, $port, $errno, $errstr, 20) )
  1261 	if( !$socket = @fsockopen($smtp_host, $port, $errno, $errstr, 20) )
  1262   {
  1262 	{
  1263     die_friendly(GENERAL_ERROR, "Could not connect to smtp host : $errno : $errstr");
  1263 		die_friendly(GENERAL_ERROR, "Could not connect to smtp host : $errno : $errstr");
  1264   }
  1264 	}
  1265 
  1265 
  1266   // Wait for reply
  1266 	// Wait for reply
  1267   smtp_get_response($socket, "220", __LINE__);
  1267 	smtp_get_response($socket, "220", __LINE__);
  1268 
  1268 
  1269   // Do we want to use AUTH?, send RFC2554 EHLO, else send RFC821 HELO
  1269 	// Do we want to use AUTH?, send RFC2554 EHLO, else send RFC821 HELO
  1270   // This improved as provided by SirSir to accomodate
  1270 	// This improved as provided by SirSir to accomodate
  1271   if( !empty($smtp_user) && !empty($smtp_pass) )
  1271 	if( !empty($smtp_user) && !empty($smtp_pass) )
  1272   {
  1272 	{
  1273     enano_fputs($socket, "EHLO " . $smtp_host . "\r\n");
  1273 		enano_fputs($socket, "EHLO " . $smtp_host . "\r\n");
  1274     smtp_get_response($socket, "250", __LINE__);
  1274 		smtp_get_response($socket, "250", __LINE__);
  1275 
  1275 
  1276     enano_fputs($socket, "AUTH LOGIN\r\n");
  1276 		enano_fputs($socket, "AUTH LOGIN\r\n");
  1277     smtp_get_response($socket, "334", __LINE__);
  1277 		smtp_get_response($socket, "334", __LINE__);
  1278 
  1278 
  1279     enano_fputs($socket, base64_encode($smtp_user) . "\r\n");
  1279 		enano_fputs($socket, base64_encode($smtp_user) . "\r\n");
  1280     smtp_get_response($socket, "334", __LINE__);
  1280 		smtp_get_response($socket, "334", __LINE__);
  1281 
  1281 
  1282     enano_fputs($socket, base64_encode($smtp_pass) . "\r\n");
  1282 		enano_fputs($socket, base64_encode($smtp_pass) . "\r\n");
  1283     smtp_get_response($socket, "235", __LINE__);
  1283 		smtp_get_response($socket, "235", __LINE__);
  1284   }
  1284 	}
  1285   else
  1285 	else
  1286   {
  1286 	{
  1287     enano_fputs($socket, "HELO " . $smtp_host . "\r\n");
  1287 		enano_fputs($socket, "HELO " . $smtp_host . "\r\n");
  1288     smtp_get_response($socket, "250", __LINE__);
  1288 		smtp_get_response($socket, "250", __LINE__);
  1289   }
  1289 	}
  1290 
  1290 
  1291   // From this point onward most server response codes should be 250
  1291 	// From this point onward most server response codes should be 250
  1292   // Specify who the mail is from....
  1292 	// Specify who the mail is from....
  1293   enano_fputs($socket, "MAIL FROM: <" . getConfig('contact_email') . ">\r\n");
  1293 	enano_fputs($socket, "MAIL FROM: <" . getConfig('contact_email') . ">\r\n");
  1294   smtp_get_response($socket, "250", __LINE__);
  1294 	smtp_get_response($socket, "250", __LINE__);
  1295 
  1295 
  1296   // Specify each user to send to and build to header.
  1296 	// Specify each user to send to and build to header.
  1297   $to_header = '';
  1297 	$to_header = '';
  1298 
  1298 
  1299   // Add an additional bit of error checking to the To field.
  1299 	// Add an additional bit of error checking to the To field.
  1300   $mail_to = (trim($mail_to) == '') ? 'Undisclosed-recipients:;' : trim($mail_to);
  1300 	$mail_to = (trim($mail_to) == '') ? 'Undisclosed-recipients:;' : trim($mail_to);
  1301   if (preg_match('#[^ ]+\@[^ ]+#', $mail_to))
  1301 	if (preg_match('#[^ ]+\@[^ ]+#', $mail_to))
  1302   {
  1302 	{
  1303     enano_fputs($socket, "RCPT TO: <$mail_to>\r\n");
  1303 		enano_fputs($socket, "RCPT TO: <$mail_to>\r\n");
  1304     smtp_get_response($socket, "250", __LINE__);
  1304 		smtp_get_response($socket, "250", __LINE__);
  1305   }
  1305 	}
  1306 
  1306 
  1307   // Ok now do the CC and BCC fields...
  1307 	// Ok now do the CC and BCC fields...
  1308   @reset($bcc);
  1308 	@reset($bcc);
  1309   while(list(, $bcc_address) = each($bcc))
  1309 	while(list(, $bcc_address) = each($bcc))
  1310   {
  1310 	{
  1311     // Add an additional bit of error checking to bcc header...
  1311 		// Add an additional bit of error checking to bcc header...
  1312     $bcc_address = trim($bcc_address);
  1312 		$bcc_address = trim($bcc_address);
  1313     if (preg_match('#[^ ]+\@[^ ]+#', $bcc_address))
  1313 		if (preg_match('#[^ ]+\@[^ ]+#', $bcc_address))
  1314     {
  1314 		{
  1315       enano_fputs($socket, "RCPT TO: <$bcc_address>\r\n");
  1315 			enano_fputs($socket, "RCPT TO: <$bcc_address>\r\n");
  1316       smtp_get_response($socket, "250", __LINE__);
  1316 			smtp_get_response($socket, "250", __LINE__);
  1317     }
  1317 		}
  1318   }
  1318 	}
  1319 
  1319 
  1320   @reset($cc);
  1320 	@reset($cc);
  1321   while(list(, $cc_address) = each($cc))
  1321 	while(list(, $cc_address) = each($cc))
  1322   {
  1322 	{
  1323     // Add an additional bit of error checking to cc header
  1323 		// Add an additional bit of error checking to cc header
  1324     $cc_address = trim($cc_address);
  1324 		$cc_address = trim($cc_address);
  1325     if (preg_match('#[^ ]+\@[^ ]+#', $cc_address))
  1325 		if (preg_match('#[^ ]+\@[^ ]+#', $cc_address))
  1326     {
  1326 		{
  1327       enano_fputs($socket, "RCPT TO: <$cc_address>\r\n");
  1327 			enano_fputs($socket, "RCPT TO: <$cc_address>\r\n");
  1328       smtp_get_response($socket, "250", __LINE__);
  1328 			smtp_get_response($socket, "250", __LINE__);
  1329     }
  1329 		}
  1330   }
  1330 	}
  1331 
  1331 
  1332   // Ok now we tell the server we are ready to start sending data
  1332 	// Ok now we tell the server we are ready to start sending data
  1333   enano_fputs($socket, "DATA\r\n");
  1333 	enano_fputs($socket, "DATA\r\n");
  1334 
  1334 
  1335   // This is the last response code we look for until the end of the message.
  1335 	// This is the last response code we look for until the end of the message.
  1336   smtp_get_response($socket, "354", __LINE__);
  1336 	smtp_get_response($socket, "354", __LINE__);
  1337 
  1337 
  1338   // Send the Subject Line...
  1338 	// Send the Subject Line...
  1339   enano_fputs($socket, "Subject: $subject\r\n");
  1339 	enano_fputs($socket, "Subject: $subject\r\n");
  1340 
  1340 
  1341   // Now the To Header.
  1341 	// Now the To Header.
  1342   enano_fputs($socket, "To: $mail_to\r\n");
  1342 	enano_fputs($socket, "To: $mail_to\r\n");
  1343 
  1343 
  1344   // Now any custom headers....
  1344 	// Now any custom headers....
  1345   enano_fputs($socket, "$headers\r\n\r\n");
  1345 	enano_fputs($socket, "$headers\r\n\r\n");
  1346 
  1346 
  1347   // Ok now we are ready for the message...
  1347 	// Ok now we are ready for the message...
  1348   enano_fputs($socket, "$message\r\n");
  1348 	enano_fputs($socket, "$message\r\n");
  1349 
  1349 
  1350   // Ok the all the ingredients are mixed in let's cook this puppy...
  1350 	// Ok the all the ingredients are mixed in let's cook this puppy...
  1351   enano_fputs($socket, ".\r\n");
  1351 	enano_fputs($socket, ".\r\n");
  1352   smtp_get_response($socket, "250", __LINE__);
  1352 	smtp_get_response($socket, "250", __LINE__);
  1353 
  1353 
  1354   // Now tell the server we are done and close the socket...
  1354 	// Now tell the server we are done and close the socket...
  1355   enano_fputs($socket, "QUIT\r\n");
  1355 	enano_fputs($socket, "QUIT\r\n");
  1356   fclose($socket);
  1356 	fclose($socket);
  1357 
  1357 
  1358   return TRUE;
  1358 	return TRUE;
  1359 }
  1359 }
  1360 
  1360 
  1361 /**
  1361 /**
  1362  * Tell which version of Enano we're running.
  1362  * Tell which version of Enano we're running.
  1363  * @param bool $long if true, uses English version names (e.g. alpha, beta, release candidate). If false (default) uses abbreviations (1.0a1, 1.0b3, 1.0RC2, etc.)
  1363  * @param bool $long if true, uses English version names (e.g. alpha, beta, release candidate). If false (default) uses abbreviations (1.0a1, 1.0b3, 1.0RC2, etc.)
  1365  * @return string
  1365  * @return string
  1366  */
  1366  */
  1367 
  1367 
  1368 function enano_version($long = false, $no_nightly = false)
  1368 function enano_version($long = false, $no_nightly = false)
  1369 {
  1369 {
  1370   if ( !defined('ENANO_CONFIG_FETCHED') )
  1370 	if ( !defined('ENANO_CONFIG_FETCHED') )
  1371   {
  1371 	{
  1372     return function_exists('installer_enano_version') ? installer_enano_version() : $GLOBALS['enano_version'];
  1372 		return function_exists('installer_enano_version') ? installer_enano_version() : $GLOBALS['enano_version'];
  1373   }
  1373 	}
  1374   $r = getConfig('enano_version');
  1374 	$r = getConfig('enano_version');
  1375   $rc = ( $long ) ? ' release candidate ' : 'RC';
  1375 	$rc = ( $long ) ? ' release candidate ' : 'RC';
  1376   $b = ( $long ) ? ' beta ' : 'b';
  1376 	$b = ( $long ) ? ' beta ' : 'b';
  1377   $a = ( $long ) ? ' alpha ' : 'a';
  1377 	$a = ( $long ) ? ' alpha ' : 'a';
  1378   if($v = getConfig('enano_rc_version')) $r .= $rc.$v;
  1378 	if($v = getConfig('enano_rc_version')) $r .= $rc.$v;
  1379   if($v = getConfig('enano_beta_version')) $r .= $b.$v;
  1379 	if($v = getConfig('enano_beta_version')) $r .= $b.$v;
  1380   if($v = getConfig('enano_alpha_version')) $r .= $a.$v;
  1380 	if($v = getConfig('enano_alpha_version')) $r .= $a.$v;
  1381   if ( defined('ENANO_NIGHTLY') && !$no_nightly )
  1381 	if ( defined('ENANO_NIGHTLY') && !$no_nightly )
  1382   {
  1382 	{
  1383     $nightlytag  = ENANO_NIGHTLY_MONTH . '-' . ENANO_NIGHTLY_DAY . '-' . ENANO_NIGHTLY_YEAR;
  1383 		$nightlytag  = ENANO_NIGHTLY_MONTH . '-' . ENANO_NIGHTLY_DAY . '-' . ENANO_NIGHTLY_YEAR;
  1384     $nightlylong = ' nightly; build date: ' . ENANO_NIGHTLY_MONTH . '-' . ENANO_NIGHTLY_DAY . '-' . ENANO_NIGHTLY_YEAR;
  1384 		$nightlylong = ' nightly; build date: ' . ENANO_NIGHTLY_MONTH . '-' . ENANO_NIGHTLY_DAY . '-' . ENANO_NIGHTLY_YEAR;
  1385     $r = ( $long ) ? $r . $nightlylong : $r . '-nightly-' . $nightlytag;
  1385 		$r = ( $long ) ? $r . $nightlylong : $r . '-nightly-' . $nightlytag;
  1386   }
  1386 	}
  1387   return $r;
  1387 	return $r;
  1388 }
  1388 }
  1389 
  1389 
  1390 /**
  1390 /**
  1391  * Give the codename of the release of Enano being run.
  1391  * Give the codename of the release of Enano being run.
  1392  * @return string
  1392  * @return string
  1393  */
  1393  */
  1394 
  1394 
  1395 function enano_codename()
  1395 function enano_codename()
  1396 {
  1396 {
  1397   $names = array(
  1397 	$names = array(
  1398       '1.0RC1' => 'Leprechaun',
  1398 			'1.0RC1' => 'Leprechaun',
  1399       '1.0RC2' => 'Clurichaun',
  1399 			'1.0RC2' => 'Clurichaun',
  1400       '1.0RC3' => 'Druid',
  1400 			'1.0RC3' => 'Druid',
  1401       '1.0'    => 'Banshee',
  1401 			'1.0'    => 'Banshee',
  1402       '1.0.1'  => 'Loch Ness',
  1402 			'1.0.1'  => 'Loch Ness',
  1403       '1.0.1.1'=> 'Loch Ness internal bugfix build',
  1403 			'1.0.1.1'=> 'Loch Ness internal bugfix build',
  1404       '1.0.2b1'=> 'Coblynau unstable',
  1404 			'1.0.2b1'=> 'Coblynau unstable',
  1405       '1.0.2'  => 'Coblynau',
  1405 			'1.0.2'  => 'Coblynau',
  1406       '1.0.3'  => 'Dyrad',
  1406 			'1.0.3'  => 'Dyrad',
  1407       '1.1.1'  => 'Caoineag alpha 1',
  1407 			'1.1.1'  => 'Caoineag alpha 1',
  1408       '1.1.2'  => 'Caoineag alpha 2',
  1408 			'1.1.2'  => 'Caoineag alpha 2',
  1409       '1.1.3'  => 'Caoineag alpha 3',
  1409 			'1.1.3'  => 'Caoineag alpha 3',
  1410       '1.1.4'  => 'Caoineag alpha 4',
  1410 			'1.1.4'  => 'Caoineag alpha 4',
  1411       '1.1.5'  => 'Caoineag alpha 5',
  1411 			'1.1.5'  => 'Caoineag alpha 5',
  1412       '1.1.6'  => 'Caoineag beta 1',
  1412 			'1.1.6'  => 'Caoineag beta 1',
  1413       '1.1.7'  => 'Caoineag beta 2',
  1413 			'1.1.7'  => 'Caoineag beta 2',
  1414       '1.1.8'  => 'Caoineag beta 3',
  1414 			'1.1.8'  => 'Caoineag beta 3',
  1415     );
  1415 		);
  1416   $version = enano_version();
  1416 	$version = enano_version();
  1417   if ( isset($names[$version]) )
  1417 	if ( isset($names[$version]) )
  1418   {
  1418 	{
  1419     return $names[$version];
  1419 		return $names[$version];
  1420   }
  1420 	}
  1421   return 'Anonymous build';
  1421 	return 'Anonymous build';
  1422 }
  1422 }
  1423 
  1423 
  1424 /**
  1424 /**
  1425  * Badly named function to send back eval'able Javascript code with an error message. Deprecated, use JSON instead.
  1425  * Badly named function to send back eval'able Javascript code with an error message. Deprecated, use JSON instead.
  1426  * @param string Message to send
  1426  * @param string Message to send
  1427  */
  1427  */
  1428 
  1428 
  1429 function _die($t) {
  1429 function _die($t) {
  1430   $_ob = 'document.getElementById("ajaxEditContainer").innerHTML = unescape(\'' . rawurlencode('' . $t . '') . '\')';
  1430 	$_ob = 'document.getElementById("ajaxEditContainer").innerHTML = unescape(\'' . rawurlencode('' . $t . '') . '\')';
  1431   die($_ob);
  1431 	die($_ob);
  1432 }
  1432 }
  1433 
  1433 
  1434 /**
  1434 /**
  1435  * Same as _die(), but sends an SQL backtrace with the error message, and doesn't halt execution.
  1435  * Same as _die(), but sends an SQL backtrace with the error message, and doesn't halt execution.
  1436  * @param string Message to send
  1436  * @param string Message to send
  1437  */
  1437  */
  1438 
  1438 
  1439 function jsdie($text) {
  1439 function jsdie($text) {
  1440   global $db, $session, $paths, $template, $plugins; // Common objects
  1440 	global $db, $session, $paths, $template, $plugins; // Common objects
  1441   $text = rawurlencode($text . "\n\nSQL Backtrace:\n" . $db->sql_backtrace());
  1441 	$text = rawurlencode($text . "\n\nSQL Backtrace:\n" . $db->sql_backtrace());
  1442   echo 'document.getElementById("ajaxEditContainer").innerHTML = unescape(\''.$text.'\');';
  1442 	echo 'document.getElementById("ajaxEditContainer").innerHTML = unescape(\''.$text.'\');';
  1443 }
  1443 }
  1444 
  1444 
  1445 /**
  1445 /**
  1446  * Capitalizes the first letter of a string
  1446  * Capitalizes the first letter of a string
  1447  * @param $text string the text to be transformed
  1447  * @param $text string the text to be transformed
  1448  * @return string
  1448  * @return string
  1449  */
  1449  */
  1450 
  1450 
  1451 function capitalize_first_letter($text)
  1451 function capitalize_first_letter($text)
  1452 {
  1452 {
  1453   return strtoupper(substr($text, 0, 1)) . substr($text, 1);
  1453 	return strtoupper(substr($text, 0, 1)) . substr($text, 1);
  1454 }
  1454 }
  1455 
  1455 
  1456 /**
  1456 /**
  1457  * Checks if a value in a bitfield is on or off
  1457  * Checks if a value in a bitfield is on or off
  1458  * @param $bitfield int the bit-field value
  1458  * @param $bitfield int the bit-field value
  1460  * @return bool
  1460  * @return bool
  1461  */
  1461  */
  1462 
  1462 
  1463 function is_bit($bitfield, $value)
  1463 function is_bit($bitfield, $value)
  1464 {
  1464 {
  1465   return ( $bitfield & $value ) ? true : false;
  1465 	return ( $bitfield & $value ) ? true : false;
  1466 }
  1466 }
  1467 
  1467 
  1468 /**
  1468 /**
  1469  * Trims spaces/newlines from the beginning and end of a string
  1469  * Trims spaces/newlines from the beginning and end of a string
  1470  * @param $text the text to process
  1470  * @param $text the text to process
  1471  * @return string
  1471  * @return string
  1472  */
  1472  */
  1473 
  1473 
  1474 function trim_spaces($text)
  1474 function trim_spaces($text)
  1475 {
  1475 {
  1476   $d = true;
  1476 	$d = true;
  1477   while($d)
  1477 	while($d)
  1478   {
  1478 	{
  1479     $c = substr($text, 0, 1);
  1479 		$c = substr($text, 0, 1);
  1480     $a = substr($text, strlen($text)-1, strlen($text));
  1480 		$a = substr($text, strlen($text)-1, strlen($text));
  1481     if($c == "\n" || $c == "\r" || $c == "\t" || $c == ' ') $text = substr($text, 1, strlen($text));
  1481 		if($c == "\n" || $c == "\r" || $c == "\t" || $c == ' ') $text = substr($text, 1, strlen($text));
  1482     elseif($a == "\n" || $a == "\r" || $a == "\t" || $a == ' ') $text = substr($text, 0, strlen($text)-1);
  1482 		elseif($a == "\n" || $a == "\r" || $a == "\t" || $a == ' ') $text = substr($text, 0, strlen($text)-1);
  1483     else $d = false;
  1483 		else $d = false;
  1484   }
  1484 	}
  1485   return $text;
  1485 	return $text;
  1486 }
  1486 }
  1487 
  1487 
  1488 /**
  1488 /**
  1489  * Enano-ese equivalent of str_split() which is only found in PHP5
  1489  * Enano-ese equivalent of str_split() which is only found in PHP5
  1490  * @param $text string the text to split
  1490  * @param $text string the text to split
  1492  * @return array
  1492  * @return array
  1493  */
  1493  */
  1494 
  1494 
  1495 function enano_str_split($text, $inc = 1)
  1495 function enano_str_split($text, $inc = 1)
  1496 {
  1496 {
  1497   if($inc < 1)
  1497 	if($inc < 1)
  1498   {
  1498 	{
  1499     return false;
  1499 		return false;
  1500   }
  1500 	}
  1501   if($inc >= strlen($text))
  1501 	if($inc >= strlen($text))
  1502   {
  1502 	{
  1503     return Array($text);
  1503 		return Array($text);
  1504   }
  1504 	}
  1505   $len = ceil(strlen($text) / $inc);
  1505 	$len = ceil(strlen($text) / $inc);
  1506   $ret = Array();
  1506 	$ret = Array();
  1507   for ( $i = 0; $i < strlen($text); $i = $i + $inc )
  1507 	for ( $i = 0; $i < strlen($text); $i = $i + $inc )
  1508   {
  1508 	{
  1509     $ret[] = substr($text, $i, $inc);
  1509 		$ret[] = substr($text, $i, $inc);
  1510   }
  1510 	}
  1511   return $ret;
  1511 	return $ret;
  1512 }
  1512 }
  1513 
  1513 
  1514 /**
  1514 /**
  1515  * Converts a hexadecimal number to a binary string.
  1515  * Converts a hexadecimal number to a binary string.
  1516  * @param text string hexadecimal number
  1516  * @param text string hexadecimal number
  1517  * @return string
  1517  * @return string
  1518  */
  1518  */
  1519 function hex2bin($text)
  1519 function hex2bin($text)
  1520 {
  1520 {
  1521   $arr = enano_str_split($text, 2);
  1521 	$arr = enano_str_split($text, 2);
  1522   $ret = '';
  1522 	$ret = '';
  1523   for ($i=0; $i<sizeof($arr); $i++)
  1523 	for ($i=0; $i<sizeof($arr); $i++)
  1524   {
  1524 	{
  1525     $ret .= chr(hexdec($arr[$i]));
  1525 		$ret .= chr(hexdec($arr[$i]));
  1526   }
  1526 	}
  1527   return $ret;
  1527 	return $ret;
  1528 }
  1528 }
  1529 
  1529 
  1530 /**
  1530 /**
  1531  * Generates and/or prints a human-readable backtrace
  1531  * Generates and/or prints a human-readable backtrace
  1532  * @param bool $return - if true, this function returns a string, otherwise returns null and prints the backtrace
  1532  * @param bool $return - if true, this function returns a string, otherwise returns null and prints the backtrace
  1533  * @return mixed
  1533  * @return mixed
  1534  */
  1534  */
  1535 
  1535 
  1536 function enano_debug_print_backtrace($return = false)
  1536 function enano_debug_print_backtrace($return = false)
  1537 {
  1537 {
  1538   ob_start();
  1538 	ob_start();
  1539   if ( !$return )
  1539 	if ( !$return )
  1540     echo '<pre>';
  1540 		echo '<pre>';
  1541   if ( function_exists('debug_print_backtrace') )
  1541 	if ( function_exists('debug_print_backtrace') )
  1542   {
  1542 	{
  1543     debug_print_backtrace();
  1543 		debug_print_backtrace();
  1544   }
  1544 	}
  1545   else
  1545 	else
  1546   {
  1546 	{
  1547     echo '<b>Warning:</b> No debug_print_backtrace() support!';
  1547 		echo '<b>Warning:</b> No debug_print_backtrace() support!';
  1548   }
  1548 	}
  1549   if ( !$return )
  1549 	if ( !$return )
  1550     echo '</pre>';
  1550 		echo '</pre>';
  1551   $c = ob_get_contents();
  1551 	$c = ob_get_contents();
  1552   ob_end_clean();
  1552 	ob_end_clean();
  1553   if($return) return $c;
  1553 	if($return) return $c;
  1554   else echo $c;
  1554 	else echo $c;
  1555   return null;
  1555 	return null;
  1556 }
  1556 }
  1557 
  1557 
  1558 /**
  1558 /**
  1559  * Like rawurlencode(), but encodes all characters
  1559  * Like rawurlencode(), but encodes all characters
  1560  * @param string $text the text to encode
  1560  * @param string $text the text to encode
  1563  * @return string
  1563  * @return string
  1564  */
  1564  */
  1565 
  1565 
  1566 function hexencode($text, $prefix = '%', $suffix = '')
  1566 function hexencode($text, $prefix = '%', $suffix = '')
  1567 {
  1567 {
  1568   $arr = enano_str_split($text);
  1568 	$arr = enano_str_split($text);
  1569   $r = '';
  1569 	$r = '';
  1570   foreach($arr as $a)
  1570 	foreach($arr as $a)
  1571   {
  1571 	{
  1572     $nibble = (string)dechex(ord($a));
  1572 		$nibble = (string)dechex(ord($a));
  1573     if(strlen($nibble) == 1) $nibble = '0' . $nibble;
  1573 		if(strlen($nibble) == 1) $nibble = '0' . $nibble;
  1574     $r .= $prefix . $nibble . $suffix;
  1574 		$r .= $prefix . $nibble . $suffix;
  1575   }
  1575 	}
  1576   return $r;
  1576 	return $r;
  1577 }
  1577 }
  1578 
  1578 
  1579 /**
  1579 /**
  1580  * Enano-ese equivalent of get_magic_quotes_gpc()
  1580  * Enano-ese equivalent of get_magic_quotes_gpc()
  1581  * @return bool
  1581  * @return bool
  1582  */
  1582  */
  1583 
  1583 
  1584 function enano_get_magic_quotes_gpc()
  1584 function enano_get_magic_quotes_gpc()
  1585 {
  1585 {
  1586   if(function_exists('get_magic_quotes_gpc'))
  1586 	if(function_exists('get_magic_quotes_gpc'))
  1587   {
  1587 	{
  1588     return ( get_magic_quotes_gpc() == 1 );
  1588 		return ( get_magic_quotes_gpc() == 1 );
  1589   }
  1589 	}
  1590   else
  1590 	else
  1591   {
  1591 	{
  1592     return ( strtolower(@ini_get('magic_quotes_gpc')) == '1' );
  1592 		return ( strtolower(@ini_get('magic_quotes_gpc')) == '1' );
  1593   }
  1593 	}
  1594 }
  1594 }
  1595 
  1595 
  1596 /**
  1596 /**
  1597  * Recursive stripslashes()
  1597  * Recursive stripslashes()
  1598  * @param array
  1598  * @param array
  1599  * @return array
  1599  * @return array
  1600  */
  1600  */
  1601 
  1601 
  1602 function stripslashes_recurse($arr)
  1602 function stripslashes_recurse($arr)
  1603 {
  1603 {
  1604   foreach($arr as $k => $xxxx)
  1604 	foreach($arr as $k => $xxxx)
  1605   {
  1605 	{
  1606     $val =& $arr[$k];
  1606 		$val =& $arr[$k];
  1607     if(is_string($val))
  1607 		if(is_string($val))
  1608       $val = stripslashes($val);
  1608 			$val = stripslashes($val);
  1609     elseif(is_array($val))
  1609 		elseif(is_array($val))
  1610       $val = stripslashes_recurse($val);
  1610 			$val = stripslashes_recurse($val);
  1611   }
  1611 	}
  1612   return $arr;
  1612 	return $arr;
  1613 }
  1613 }
  1614 
  1614 
  1615 /**
  1615 /**
  1616  * Recursive function to remove all NUL bytes from a string
  1616  * Recursive function to remove all NUL bytes from a string
  1617  * @param array
  1617  * @param array
  1618  * @return array
  1618  * @return array
  1619  */
  1619  */
  1620 
  1620 
  1621 function strip_nul_chars($arr)
  1621 function strip_nul_chars($arr)
  1622 {
  1622 {
  1623   foreach($arr as $k => $xxxx_unused)
  1623 	foreach($arr as $k => $xxxx_unused)
  1624   {
  1624 	{
  1625     $val =& $arr[$k];
  1625 		$val =& $arr[$k];
  1626     if(is_string($val))
  1626 		if(is_string($val))
  1627       $val = str_replace("\000", '', $val);
  1627 			$val = str_replace("\000", '', $val);
  1628     elseif(is_array($val))
  1628 		elseif(is_array($val))
  1629       $val = strip_nul_chars($val);
  1629 			$val = strip_nul_chars($val);
  1630   }
  1630 	}
  1631   return $arr;
  1631 	return $arr;
  1632 }
  1632 }
  1633 
  1633 
  1634 /**
  1634 /**
  1635  * If magic_quotes_gpc is on, calls stripslashes() on everything in $_GET/$_POST/$_COOKIE. Also strips any NUL characters from incoming requests, as these are typically malicious.
  1635  * If magic_quotes_gpc is on, calls stripslashes() on everything in $_GET/$_POST/$_COOKIE. Also strips any NUL characters from incoming requests, as these are typically malicious.
  1636  * @ignore - this doesn't work too well in my tests
  1636  * @ignore - this doesn't work too well in my tests
  1637  * @todo port version from the PHP manual
  1637  * @todo port version from the PHP manual
  1638  * @return void
  1638  * @return void
  1639  */
  1639  */
  1640 function strip_magic_quotes_gpc()
  1640 function strip_magic_quotes_gpc()
  1641 {
  1641 {
  1642   if(enano_get_magic_quotes_gpc())
  1642 	if(enano_get_magic_quotes_gpc())
  1643   {
  1643 	{
  1644     $_POST    = stripslashes_recurse($_POST);
  1644 		$_POST    = stripslashes_recurse($_POST);
  1645     $_GET     = stripslashes_recurse($_GET);
  1645 		$_GET     = stripslashes_recurse($_GET);
  1646     $_COOKIE  = stripslashes_recurse($_COOKIE);
  1646 		$_COOKIE  = stripslashes_recurse($_COOKIE);
  1647     $_REQUEST = stripslashes_recurse($_REQUEST);
  1647 		$_REQUEST = stripslashes_recurse($_REQUEST);
  1648   }
  1648 	}
  1649   $_POST    = strip_nul_chars($_POST);
  1649 	$_POST    = strip_nul_chars($_POST);
  1650   $_GET     = strip_nul_chars($_GET);
  1650 	$_GET     = strip_nul_chars($_GET);
  1651   $_COOKIE  = strip_nul_chars($_COOKIE);
  1651 	$_COOKIE  = strip_nul_chars($_COOKIE);
  1652   $_REQUEST = strip_nul_chars($_REQUEST);
  1652 	$_REQUEST = strip_nul_chars($_REQUEST);
  1653   $_POST    = decode_unicode_array($_POST);
  1653 	$_POST    = decode_unicode_array($_POST);
  1654   $_GET     = decode_unicode_array($_GET);
  1654 	$_GET     = decode_unicode_array($_GET);
  1655   $_COOKIE  = decode_unicode_array($_COOKIE);
  1655 	$_COOKIE  = decode_unicode_array($_COOKIE);
  1656   $_REQUEST = decode_unicode_array($_REQUEST);
  1656 	$_REQUEST = decode_unicode_array($_REQUEST);
  1657 }
  1657 }
  1658 
  1658 
  1659 /**
  1659 /**
  1660  * A very basic single-character compression algorithm for binary strings/bitfields
  1660  * A very basic single-character compression algorithm for binary strings/bitfields
  1661  * @param string $bits the text to compress, should be only 1s and 0s
  1661  * @param string $bits the text to compress, should be only 1s and 0s
  1662  * @return string
  1662  * @return string
  1663  */
  1663  */
  1664  
  1664  
  1665 function compress_bitfield($bits)
  1665 function compress_bitfield($bits)
  1666 {
  1666 {
  1667   if ( !preg_match('/^[01]+$/', $bits) )
  1667 	if ( !preg_match('/^[01]+$/', $bits) )
  1668     return false;
  1668 		return false;
  1669   
  1669 	
  1670   $current = intval($bits{0});
  1670 	$current = intval($bits{0});
  1671   $clen = 0;
  1671 	$clen = 0;
  1672   $out = '';
  1672 	$out = '';
  1673   for ( $i = 0; $i < strlen($bits); $i++ )
  1673 	for ( $i = 0; $i < strlen($bits); $i++ )
  1674   {
  1674 	{
  1675     $cbit = intval($bits{$i});
  1675 		$cbit = intval($bits{$i});
  1676     if ( $cbit !== $current || $clen == 127 || $i == strlen($bits) - 1 )
  1676 		if ( $cbit !== $current || $clen == 127 || $i == strlen($bits) - 1 )
  1677     {
  1677 		{
  1678       if ( $i == strlen($bits) - 1 && $cbit === $current )
  1678 			if ( $i == strlen($bits) - 1 && $cbit === $current )
  1679       {
  1679 			{
  1680         $clen++;
  1680 				$clen++;
  1681       }
  1681 			}
  1682       // write chunk
  1682 			// write chunk
  1683       $byte = $clen;
  1683 			$byte = $clen;
  1684       if ( $current === 1 )
  1684 			if ( $current === 1 )
  1685         $byte |= 0x80;
  1685 				$byte |= 0x80;
  1686       $out .= chr($byte);
  1686 			$out .= chr($byte);
  1687       
  1687 			
  1688       if ( $i == strlen($bits) - 1 && $cbit !== $current )
  1688 			if ( $i == strlen($bits) - 1 && $cbit !== $current )
  1689       {
  1689 			{
  1690         $out .= ( $cbit === 1 ) ? chr(0x81) : chr(0x1);
  1690 				$out .= ( $cbit === 1 ) ? chr(0x81) : chr(0x1);
  1691       }
  1691 			}
  1692       
  1692 			
  1693       // reset
  1693 			// reset
  1694       $current = intval($cbit);
  1694 			$current = intval($cbit);
  1695       $clen = 0;
  1695 			$clen = 0;
  1696     }
  1696 		}
  1697     $clen++;
  1697 		$clen++;
  1698   }
  1698 	}
  1699   $crc = dechex(crc32($out));
  1699 	$crc = dechex(crc32($out));
  1700   while ( strlen($crc) < 8 )
  1700 	while ( strlen($crc) < 8 )
  1701     $crc = "0$crc";
  1701 		$crc = "0$crc";
  1702   return "cbf2:{$crc}" . hexencode($out, '', '');
  1702 	return "cbf2:{$crc}" . hexencode($out, '', '');
  1703 }
  1703 }
  1704 
  1704 
  1705 // test case
  1705 // test case
  1706 // $bf = '0111100010000000000000000000000100000000000000001110000000000000000101100000010100001100010000000000000000000000000000111111111111111111111100100001000000000000000000000000000000000000';
  1706 // $bf = '0111100010000000000000000000000100000000000000001110000000000000000101100000010100001100010000000000000000000000000000111111111111111111111100100001000000000000000000000000000000000000';
  1707 // die('<pre>Original:  ' . " $bf\nCompressed: " . compress_bitfield($bf) . "\nProcessed:  ".uncompress_bitfield(compress_bitfield($bf)).'</pre>');
  1707 // die('<pre>Original:  ' . " $bf\nCompressed: " . compress_bitfield($bf) . "\nProcessed:  ".uncompress_bitfield(compress_bitfield($bf)).'</pre>');
  1712  * @return string the uncompressed, original (we hope) bitfield OR bool false on error
  1712  * @return string the uncompressed, original (we hope) bitfield OR bool false on error
  1713  */
  1713  */
  1714 
  1714 
  1715 function uncompress_bitfield($bits)
  1715 function uncompress_bitfield($bits)
  1716 {
  1716 {
  1717   if ( substr($bits, 0, 4) == 'cbf:' )
  1717 	if ( substr($bits, 0, 4) == 'cbf:' )
  1718   {
  1718 	{
  1719     return uncompress_bitfield_old($bits);
  1719 		return uncompress_bitfield_old($bits);
  1720   }
  1720 	}
  1721   if ( substr($bits, 0, 5) != 'cbf2:' )
  1721 	if ( substr($bits, 0, 5) != 'cbf2:' )
  1722   {
  1722 	{
  1723     echo __FUNCTION__.'(): ERROR: Invalid stream';
  1723 		echo __FUNCTION__.'(): ERROR: Invalid stream';
  1724     return false;
  1724 		return false;
  1725   }
  1725 	}
  1726   $bits = substr($bits, 5);
  1726 	$bits = substr($bits, 5);
  1727   $crc = substr($bits, 0, 8);
  1727 	$crc = substr($bits, 0, 8);
  1728   $bits = substr($bits, 8);
  1728 	$bits = substr($bits, 8);
  1729   $bits = hexdecode($bits);
  1729 	$bits = hexdecode($bits);
  1730   if ( dechex(crc32($bits)) !== $crc )
  1730 	if ( dechex(crc32($bits)) !== $crc )
  1731   {
  1731 	{
  1732     echo __FUNCTION__."(): ERROR: CRC failed";
  1732 		echo __FUNCTION__."(): ERROR: CRC failed";
  1733     return false;
  1733 		return false;
  1734   }
  1734 	}
  1735   $out = '';
  1735 	$out = '';
  1736   for ( $i = 0; $i < strlen($bits); $i++ )
  1736 	for ( $i = 0; $i < strlen($bits); $i++ )
  1737   {
  1737 	{
  1738     $byte = ord($bits{$i});
  1738 		$byte = ord($bits{$i});
  1739     $char = $byte & 0x80 ? '1' : '0';
  1739 		$char = $byte & 0x80 ? '1' : '0';
  1740     $byte &= ~0x80;
  1740 		$byte &= ~0x80;
  1741     for ( $j = 0; $j < $byte; $j++ )
  1741 		for ( $j = 0; $j < $byte; $j++ )
  1742     {
  1742 		{
  1743       $out .= $char;
  1743 			$out .= $char;
  1744     }
  1744 		}
  1745   }
  1745 	}
  1746   return $out;
  1746 	return $out;
  1747 }
  1747 }
  1748 
  1748 
  1749 /**
  1749 /**
  1750  * Decompressor for old-format bitfields.
  1750  * Decompressor for old-format bitfields.
  1751  * @param string
  1751  * @param string
  1753  * @access private
  1753  * @access private
  1754  */
  1754  */
  1755 
  1755 
  1756 function uncompress_bitfield_old($bits)
  1756 function uncompress_bitfield_old($bits)
  1757 {
  1757 {
  1758   if(substr($bits, 0, 4) != 'cbf:')
  1758 	if(substr($bits, 0, 4) != 'cbf:')
  1759   {
  1759 	{
  1760     echo __FUNCTION__.'(): ERROR: Invalid stream';
  1760 		echo __FUNCTION__.'(): ERROR: Invalid stream';
  1761     return false;
  1761 		return false;
  1762   }
  1762 	}
  1763   $len = intval(substr($bits, strpos($bits, 'len=')+4, strpos($bits, ';')-strpos($bits, 'len=')-4));
  1763 	$len = intval(substr($bits, strpos($bits, 'len=')+4, strpos($bits, ';')-strpos($bits, 'len=')-4));
  1764   $crc = substr($bits, strpos($bits, 'crc=')+4, 8);
  1764 	$crc = substr($bits, strpos($bits, 'crc=')+4, 8);
  1765   $data = substr($bits, strpos($bits, 'data=')+5, strpos($bits, '|end')-strpos($bits, 'data=')-5);
  1765 	$data = substr($bits, strpos($bits, 'data=')+5, strpos($bits, '|end')-strpos($bits, 'data=')-5);
  1766   $data = explode(',', $data);
  1766 	$data = explode(',', $data);
  1767   foreach($data as $a => $b)
  1767 	foreach($data as $a => $b)
  1768   {
  1768 	{
  1769     $d =& $data[$a];
  1769 		$d =& $data[$a];
  1770     $char = substr($d, 0, 1);
  1770 		$char = substr($d, 0, 1);
  1771     $dlen = intval(substr($d, 2, strlen($d)-1));
  1771 		$dlen = intval(substr($d, 2, strlen($d)-1));
  1772     $s = '';
  1772 		$s = '';
  1773     for($i=0;$i<$dlen;$i++,$s.=$char);
  1773 		for($i=0;$i<$dlen;$i++,$s.=$char);
  1774     $d = $s;
  1774 		$d = $s;
  1775     unset($s, $dlen, $char);
  1775 		unset($s, $dlen, $char);
  1776   }
  1776 	}
  1777   $decompressed = implode('', $data);
  1777 	$decompressed = implode('', $data);
  1778   $decompressed = substr($decompressed, 0, -1);
  1778 	$decompressed = substr($decompressed, 0, -1);
  1779   $dcrc = (string)dechex(crc32($decompressed));
  1779 	$dcrc = (string)dechex(crc32($decompressed));
  1780   if($dcrc != $crc)
  1780 	if($dcrc != $crc)
  1781   {
  1781 	{
  1782     echo __FUNCTION__.'(): ERROR: CRC check failed<br />debug info:<br />original crc: '.$crc.'<br />decomp\'ed crc: '.$dcrc.'<br />';
  1782 		echo __FUNCTION__.'(): ERROR: CRC check failed<br />debug info:<br />original crc: '.$crc.'<br />decomp\'ed crc: '.$dcrc.'<br />';
  1783     return false;
  1783 		return false;
  1784   }
  1784 	}
  1785   return $decompressed;
  1785 	return $decompressed;
  1786 }
  1786 }
  1787 
  1787 
  1788 /**
  1788 /**
  1789  * Exports a MySQL table into a SQL string.
  1789  * Exports a MySQL table into a SQL string.
  1790  * @param string $table The name of the table to export
  1790  * @param string $table The name of the table to export
  1794  * @return string
  1794  * @return string
  1795  */
  1795  */
  1796 
  1796 
  1797 function export_table($table, $structure = true, $data = true, $compact = false)
  1797 function export_table($table, $structure = true, $data = true, $compact = false)
  1798 {
  1798 {
  1799   global $db, $session, $paths, $template, $plugins; // Common objects
  1799 	global $db, $session, $paths, $template, $plugins; // Common objects
  1800   $struct_keys = '';
  1800 	$struct_keys = '';
  1801   $divider   = (!$compact) ? "\n" : "\n";
  1801 	$divider   = (!$compact) ? "\n" : "\n";
  1802   $spacer1   = (!$compact) ? "\n" : " ";
  1802 	$spacer1   = (!$compact) ? "\n" : " ";
  1803   $spacer2   = (!$compact) ? "  " : " ";
  1803 	$spacer2   = (!$compact) ? "  " : " ";
  1804   $rowspacer = (!$compact) ? "\n  " : " ";
  1804 	$rowspacer = (!$compact) ? "\n  " : " ";
  1805   $index_list = Array();
  1805 	$index_list = Array();
  1806   $cols = $db->sql_query('SHOW COLUMNS IN '.$table.';');
  1806 	$cols = $db->sql_query('SHOW COLUMNS IN '.$table.';');
  1807   if(!$cols)
  1807 	if(!$cols)
  1808   {
  1808 	{
  1809     echo 'export_table(): Error getting column list: '.$db->get_error_text().'<br />';
  1809 		echo 'export_table(): Error getting column list: '.$db->get_error_text().'<br />';
  1810     return false;
  1810 		return false;
  1811   }
  1811 	}
  1812   $col = Array();
  1812 	$col = Array();
  1813   $sqlcol = Array();
  1813 	$sqlcol = Array();
  1814   $collist = Array();
  1814 	$collist = Array();
  1815   $pri_keys = Array();
  1815 	$pri_keys = Array();
  1816   // Using fetchrow_num() here to compensate for MySQL l10n
  1816 	// Using fetchrow_num() here to compensate for MySQL l10n
  1817   while( $row = $db->fetchrow_num() )
  1817 	while( $row = $db->fetchrow_num() )
  1818   {
  1818 	{
  1819     $field =& $row[0];
  1819 		$field =& $row[0];
  1820     $type  =& $row[1];
  1820 		$type  =& $row[1];
  1821     $null  =& $row[2];
  1821 		$null  =& $row[2];
  1822     $key   =& $row[3];
  1822 		$key   =& $row[3];
  1823     $def   =& $row[4];
  1823 		$def   =& $row[4];
  1824     $extra =& $row[5];
  1824 		$extra =& $row[5];
  1825     $col[] = Array(
  1825 		$col[] = Array(
  1826       'name'=>$field,
  1826 			'name'=>$field,
  1827       'type'=>$type,
  1827 			'type'=>$type,
  1828       'null'=>$null,
  1828 			'null'=>$null,
  1829       'key'=>$key,
  1829 			'key'=>$key,
  1830       'default'=>$def,
  1830 			'default'=>$def,
  1831       'extra'=>$extra,
  1831 			'extra'=>$extra,
  1832       );
  1832 			);
  1833     $collist[] = $field;
  1833 		$collist[] = $field;
  1834   }
  1834 	}
  1835 
  1835 
  1836   if ( $structure )
  1836 	if ( $structure )
  1837   {
  1837 	{
  1838     $db->sql_query('SET SQL_QUOTE_SHOW_CREATE = 0;');
  1838 		$db->sql_query('SET SQL_QUOTE_SHOW_CREATE = 0;');
  1839     $struct = $db->sql_query('SHOW CREATE TABLE '.$table.';');
  1839 		$struct = $db->sql_query('SHOW CREATE TABLE '.$table.';');
  1840     if ( !$struct )
  1840 		if ( !$struct )
  1841       $db->_die();
  1841 			$db->_die();
  1842     $row = $db->fetchrow_num();
  1842 		$row = $db->fetchrow_num();
  1843     $db->free_result();
  1843 		$db->free_result();
  1844     $struct = $row[1];
  1844 		$struct = $row[1];
  1845     $struct = preg_replace("/\n\) ENGINE=(.+)$/", "\n);", $struct);
  1845 		$struct = preg_replace("/\n\) ENGINE=(.+)$/", "\n);", $struct);
  1846     unset($row);
  1846 		unset($row);
  1847     if ( $compact )
  1847 		if ( $compact )
  1848     {
  1848 		{
  1849       $struct_arr = explode("\n", $struct);
  1849 			$struct_arr = explode("\n", $struct);
  1850       foreach ( $struct_arr as $i => $leg )
  1850 			foreach ( $struct_arr as $i => $leg )
  1851       {
  1851 			{
  1852         if ( $i == 0 )
  1852 				if ( $i == 0 )
  1853           continue;
  1853 					continue;
  1854         $test = trim($leg);
  1854 				$test = trim($leg);
  1855         if ( empty($test) )
  1855 				if ( empty($test) )
  1856         {
  1856 				{
  1857           unset($struct_arr[$i]);
  1857 					unset($struct_arr[$i]);
  1858           continue;
  1858 					continue;
  1859         }
  1859 				}
  1860         $struct_arr[$i] = preg_replace('/^([\s]*)/', ' ', $leg);
  1860 				$struct_arr[$i] = preg_replace('/^([\s]*)/', ' ', $leg);
  1861       }
  1861 			}
  1862       $struct = implode("", $struct_arr);
  1862 			$struct = implode("", $struct_arr);
  1863     }
  1863 		}
  1864   }
  1864 	}
  1865 
  1865 
  1866   // Structuring complete
  1866 	// Structuring complete
  1867   if($data)
  1867 	if($data)
  1868   {
  1868 	{
  1869     $datq = $db->sql_query('SELECT * FROM '.$table.';');
  1869 		$datq = $db->sql_query('SELECT * FROM '.$table.';');
  1870     if(!$datq)
  1870 		if(!$datq)
  1871     {
  1871 		{
  1872       echo 'export_table(): Error getting column list: '.$db->get_error_text().'<br />';
  1872 			echo 'export_table(): Error getting column list: '.$db->get_error_text().'<br />';
  1873       return false;
  1873 			return false;
  1874     }
  1874 		}
  1875     if($db->numrows() < 1)
  1875 		if($db->numrows() < 1)
  1876     {
  1876 		{
  1877       if($structure) return $struct;
  1877 			if($structure) return $struct;
  1878       else return '';
  1878 			else return '';
  1879     }
  1879 		}
  1880     $rowdata = Array();
  1880 		$rowdata = Array();
  1881     $dataqs = Array();
  1881 		$dataqs = Array();
  1882     $insert_strings = Array();
  1882 		$insert_strings = Array();
  1883     $z = false;
  1883 		$z = false;
  1884     while($row = $db->fetchrow_num())
  1884 		while($row = $db->fetchrow_num())
  1885     {
  1885 		{
  1886       $z = false;
  1886 			$z = false;
  1887       foreach($row as $i => $cell)
  1887 			foreach($row as $i => $cell)
  1888       {
  1888 			{
  1889         $str = mysql_encode_column($cell, $col[$i]['type']);
  1889 				$str = mysql_encode_column($cell, $col[$i]['type']);
  1890         $rowdata[] = $str;
  1890 				$rowdata[] = $str;
  1891       }
  1891 			}
  1892       $dataqs2 = implode(",$rowspacer", $dataqs) . ",$rowspacer" . '( ' . implode(', ', $rowdata) . ' )';
  1892 			$dataqs2 = implode(",$rowspacer", $dataqs) . ",$rowspacer" . '( ' . implode(', ', $rowdata) . ' )';
  1893       $ins = 'INSERT INTO '.$table.'( '.implode(',', $collist).' ) VALUES' . $dataqs2 . ";";
  1893 			$ins = 'INSERT INTO '.$table.'( '.implode(',', $collist).' ) VALUES' . $dataqs2 . ";";
  1894       if ( strlen( $ins ) > MYSQL_MAX_PACKET_SIZE )
  1894 			if ( strlen( $ins ) > MYSQL_MAX_PACKET_SIZE )
  1895       {
  1895 			{
  1896         // We've exceeded the maximum allowed packet size for MySQL - separate this into a different query
  1896 				// We've exceeded the maximum allowed packet size for MySQL - separate this into a different query
  1897         $insert_strings[] = 'INSERT INTO '.$table.'( '.implode(',', $collist).' ) VALUES' . implode(",$rowspacer", $dataqs) . ";";;
  1897 				$insert_strings[] = 'INSERT INTO '.$table.'( '.implode(',', $collist).' ) VALUES' . implode(",$rowspacer", $dataqs) . ";";;
  1898         $dataqs = Array('( ' . implode(', ', $rowdata) . ' )');
  1898 				$dataqs = Array('( ' . implode(', ', $rowdata) . ' )');
  1899         $z = true;
  1899 				$z = true;
  1900       }
  1900 			}
  1901       else
  1901 			else
  1902       {
  1902 			{
  1903         $dataqs[] = '( ' . implode(', ', $rowdata) . ' )';
  1903 				$dataqs[] = '( ' . implode(', ', $rowdata) . ' )';
  1904       }
  1904 			}
  1905       $rowdata = Array();
  1905 			$rowdata = Array();
  1906     }
  1906 		}
  1907     if ( !$z )
  1907 		if ( !$z )
  1908     {
  1908 		{
  1909       $insert_strings[] = 'INSERT INTO '.$table.'( '.implode(',', $collist).' ) VALUES' . implode(",$rowspacer", $dataqs) . ";";;
  1909 			$insert_strings[] = 'INSERT INTO '.$table.'( '.implode(',', $collist).' ) VALUES' . implode(",$rowspacer", $dataqs) . ";";;
  1910       $dataqs = Array();
  1910 			$dataqs = Array();
  1911     }
  1911 		}
  1912     $datstring = implode($divider, $insert_strings);
  1912 		$datstring = implode($divider, $insert_strings);
  1913   }
  1913 	}
  1914   if($structure && !$data) return $struct;
  1914 	if($structure && !$data) return $struct;
  1915   elseif(!$structure && $data) return $datstring;
  1915 	elseif(!$structure && $data) return $datstring;
  1916   elseif($structure && $data) return $struct . $divider . $datstring;
  1916 	elseif($structure && $data) return $struct . $divider . $datstring;
  1917   elseif(!$structure && !$data) return '';
  1917 	elseif(!$structure && !$data) return '';
  1918 }
  1918 }
  1919 
  1919 
  1920 /**
  1920 /**
  1921  * Encodes a string value for use in an INSERT statement for given column type $type.
  1921  * Encodes a string value for use in an INSERT statement for given column type $type.
  1922  * @access private
  1922  * @access private
  1923  */
  1923  */
  1924 
  1924 
  1925 function mysql_encode_column($input, $type)
  1925 function mysql_encode_column($input, $type)
  1926 {
  1926 {
  1927   global $db, $session, $paths, $template, $plugins; // Common objects
  1927 	global $db, $session, $paths, $template, $plugins; // Common objects
  1928   // Decide whether to quote the string or not
  1928 	// Decide whether to quote the string or not
  1929   if(substr($type, 0, 7) == 'varchar' || $type == 'datetime' || $type == 'text' || $type == 'tinytext' || $type == 'smalltext' || $type == 'longtext' || substr($type, 0, 4) == 'char')
  1929 	if(substr($type, 0, 7) == 'varchar' || $type == 'datetime' || $type == 'text' || $type == 'tinytext' || $type == 'smalltext' || $type == 'longtext' || substr($type, 0, 4) == 'char')
  1930   {
  1930 	{
  1931     $str = "'" . $db->escape($input) . "'";
  1931 		$str = "'" . $db->escape($input) . "'";
  1932   }
  1932 	}
  1933   elseif(in_array($type, Array('blob', 'longblob', 'mediumblob', 'smallblob')) || substr($type, 0, 6) == 'binary' || substr($type, 0, 9) == 'varbinary')
  1933 	elseif(in_array($type, Array('blob', 'longblob', 'mediumblob', 'smallblob')) || substr($type, 0, 6) == 'binary' || substr($type, 0, 9) == 'varbinary')
  1934   {
  1934 	{
  1935     $str = '0x' . hexencode($input, '', '');
  1935 		$str = '0x' . hexencode($input, '', '');
  1936   }
  1936 	}
  1937   elseif(is_null($input))
  1937 	elseif(is_null($input))
  1938   {
  1938 	{
  1939     $str = 'NULL';
  1939 		$str = 'NULL';
  1940   }
  1940 	}
  1941   else
  1941 	else
  1942   {
  1942 	{
  1943     $str = (string)$input;
  1943 		$str = (string)$input;
  1944   }
  1944 	}
  1945   return $str;
  1945 	return $str;
  1946 }
  1946 }
  1947 
  1947 
  1948 /**
  1948 /**
  1949  * Creates an associative array defining which file extensions are allowed and which ones aren't
  1949  * Creates an associative array defining which file extensions are allowed and which ones aren't
  1950  * @return array keyname will be a file extension, value will be true or false
  1950  * @return array keyname will be a file extension, value will be true or false
  1951  */
  1951  */
  1952 
  1952 
  1953 function fetch_allowed_extensions()
  1953 function fetch_allowed_extensions()
  1954 {
  1954 {
  1955   global $mime_types;
  1955 	global $mime_types;
  1956   $bits = getConfig('allowed_mime_types');
  1956 	$bits = getConfig('allowed_mime_types');
  1957   if(!$bits) return Array(false);
  1957 	if(!$bits) return Array(false);
  1958   $bits = uncompress_bitfield($bits);
  1958 	$bits = uncompress_bitfield($bits);
  1959   if(!$bits) return Array(false);
  1959 	if(!$bits) return Array(false);
  1960   $bits = enano_str_split($bits, 1);
  1960 	$bits = enano_str_split($bits, 1);
  1961   $ret = Array();
  1961 	$ret = Array();
  1962   $mt = array_keys($mime_types);
  1962 	$mt = array_keys($mime_types);
  1963   foreach($bits as $i => $b)
  1963 	foreach($bits as $i => $b)
  1964   {
  1964 	{
  1965     $ret[$mt[$i]] = ( $b == '1' ) ? true : false;
  1965 		$ret[$mt[$i]] = ( $b == '1' ) ? true : false;
  1966   }
  1966 	}
  1967   return $ret;
  1967 	return $ret;
  1968 }
  1968 }
  1969 
  1969 
  1970 /**
  1970 /**
  1971  * Generates a random key suitable for encryption
  1971  * Generates a random key suitable for encryption
  1972  * @param int $len the length of the key
  1972  * @param int $len the length of the key
  1973  * @return string a BINARY key
  1973  * @return string a BINARY key
  1974  */
  1974  */
  1975 
  1975 
  1976 function randkey($len = 32)
  1976 function randkey($len = 32)
  1977 {
  1977 {
  1978   $key = '';
  1978 	$key = '';
  1979   for($i=0;$i<$len;$i++)
  1979 	for($i=0;$i<$len;$i++)
  1980   {
  1980 	{
  1981     $key .= chr(mt_rand(0, 255));
  1981 		$key .= chr(mt_rand(0, 255));
  1982   }
  1982 	}
  1983   return $key;
  1983 	return $key;
  1984 }
  1984 }
  1985 
  1985 
  1986 /**
  1986 /**
  1987  * Decodes a hex string.
  1987  * Decodes a hex string.
  1988  * @param string $hex The hex code to decode
  1988  * @param string $hex The hex code to decode
  1989  * @return string
  1989  * @return string
  1990  */
  1990  */
  1991 
  1991 
  1992 function hexdecode($hex)
  1992 function hexdecode($hex)
  1993 {
  1993 {
  1994   $hex = enano_str_split($hex, 2);
  1994 	$hex = enano_str_split($hex, 2);
  1995   $bin_key = '';
  1995 	$bin_key = '';
  1996   foreach($hex as $nibble)
  1996 	foreach($hex as $nibble)
  1997   {
  1997 	{
  1998     $byte = chr(hexdec($nibble));
  1998 		$byte = chr(hexdec($nibble));
  1999     $bin_key .= $byte;
  1999 		$bin_key .= $byte;
  2000   }
  2000 	}
  2001   return $bin_key;
  2001 	return $bin_key;
  2002 }
  2002 }
  2003 
  2003 
  2004 /**
  2004 /**
  2005  * Enano's own (almost) bulletproof HTML sanitizer.
  2005  * Enano's own (almost) bulletproof HTML sanitizer.
  2006  * @param string $html The input HTML
  2006  * @param string $html The input HTML
  2007  * @return string cleaned HTML
  2007  * @return string cleaned HTML
  2008  */
  2008  */
  2009 
  2009 
  2010 function sanitize_html($html, $filter_php = true)
  2010 function sanitize_html($html, $filter_php = true)
  2011 {
  2011 {
  2012   // Random seed for substitution
  2012 	// Random seed for substitution
  2013   $rand_seed = md5( sha1(microtime()) . mt_rand() );
  2013 	$rand_seed = md5( sha1(microtime()) . mt_rand() );
  2014   
  2014 	
  2015   // We need MediaWiki
  2015 	// We need MediaWiki
  2016   require_once(ENANO_ROOT . '/includes/wikiengine/TagSanitizer.php');
  2016 	require_once(ENANO_ROOT . '/includes/wikiengine/TagSanitizer.php');
  2017   
  2017 	
  2018   // Strip out comments that are already escaped
  2018 	// Strip out comments that are already escaped
  2019   preg_match_all('/&lt;!--(.*?)--&gt;/', $html, $comment_match);
  2019 	preg_match_all('/&lt;!--(.*?)--&gt;/', $html, $comment_match);
  2020   $i = 0;
  2020 	$i = 0;
  2021   foreach ( $comment_match[0] as $comment )
  2021 	foreach ( $comment_match[0] as $comment )
  2022   {
  2022 	{
  2023     $html = str_replace_once($comment, "{HTMLCOMMENT:$i:$rand_seed}", $html);
  2023 		$html = str_replace_once($comment, "{HTMLCOMMENT:$i:$rand_seed}", $html);
  2024     $i++;
  2024 		$i++;
  2025   }
  2025 	}
  2026   
  2026 	
  2027   // Strip out code sections that will be postprocessed by Text_Wiki
  2027 	// Strip out code sections that will be postprocessed by Text_Wiki
  2028   preg_match_all(';^<code(\s[^>]*)?>((?:(?R)|.)*?)</code>(\s|$);msi', $html, $code_match);
  2028 	preg_match_all(';^<code(\s[^>]*)?>((?:(?R)|.)*?)</code>(\s|$);msi', $html, $code_match);
  2029   $i = 0;
  2029 	$i = 0;
  2030   foreach ( $code_match[0] as $code )
  2030 	foreach ( $code_match[0] as $code )
  2031   {
  2031 	{
  2032     $html = str_replace_once($code, "{TW_CODE:$i:$rand_seed}", $html);
  2032 		$html = str_replace_once($code, "{TW_CODE:$i:$rand_seed}", $html);
  2033     $i++;
  2033 		$i++;
  2034   }
  2034 	}
  2035 
  2035 
  2036   $html = preg_replace('#<([a-z]+)([\s]+)([^>]+?)'.htmlalternatives('javascript:').'(.+?)>(.*?)</\\1>#is', '&lt;\\1\\2\\3javascript:\\59&gt;\\60&lt;/\\1&gt;', $html);
  2036 	$html = preg_replace('#<([a-z]+)([\s]+)([^>]+?)'.htmlalternatives('javascript:').'(.+?)>(.*?)</\\1>#is', '&lt;\\1\\2\\3javascript:\\59&gt;\\60&lt;/\\1&gt;', $html);
  2037   $html = preg_replace('#<([a-z]+)([\s]+)([^>]+?)'.htmlalternatives('javascript:').'(.+?)>#is', '&lt;\\1\\2\\3javascript:\\59&gt;', $html);
  2037 	$html = preg_replace('#<([a-z]+)([\s]+)([^>]+?)'.htmlalternatives('javascript:').'(.+?)>#is', '&lt;\\1\\2\\3javascript:\\59&gt;', $html);
  2038 
  2038 
  2039   if($filter_php)
  2039 	if($filter_php)
  2040     $html = str_replace(
  2040 		$html = str_replace(
  2041       Array('<?php',    '<?',    '<%',    '?>',    '%>'),
  2041 			Array('<?php',    '<?',    '<%',    '?>',    '%>'),
  2042       Array('&lt;?php', '&lt;?', '&lt;%', '?&gt;', '%&gt;'),
  2042 			Array('&lt;?php', '&lt;?', '&lt;%', '?&gt;', '%&gt;'),
  2043       $html);
  2043 			$html);
  2044 
  2044 
  2045   $tag_whitelist = array_keys ( setupAttributeWhitelist() );
  2045 	$tag_whitelist = array_keys ( setupAttributeWhitelist() );
  2046   if ( !$filter_php )
  2046 	if ( !$filter_php )
  2047     $tag_whitelist[] = '?php';
  2047 		$tag_whitelist[] = '?php';
  2048   // allow HTML comments
  2048 	// allow HTML comments
  2049   $tag_whitelist[] = '!--';
  2049 	$tag_whitelist[] = '!--';
  2050   $len = strlen($html);
  2050 	$len = strlen($html);
  2051   $in_quote = false;
  2051 	$in_quote = false;
  2052   $quote_char = '';
  2052 	$quote_char = '';
  2053   $tag_start = 0;
  2053 	$tag_start = 0;
  2054   $tag_name = '';
  2054 	$tag_name = '';
  2055   $in_tag = false;
  2055 	$in_tag = false;
  2056   $trk_name = false;
  2056 	$trk_name = false;
  2057   for ( $i = 0; $i < $len; $i++ )
  2057 	for ( $i = 0; $i < $len; $i++ )
  2058   {
  2058 	{
  2059     $chr = $html{$i};
  2059 		$chr = $html{$i};
  2060     $prev = ( $i == 0 ) ? '' : $html{ $i - 1 };
  2060 		$prev = ( $i == 0 ) ? '' : $html{ $i - 1 };
  2061     $next = ( ( $i + 1 ) == $len ) ? '' : $html { $i + 1 };
  2061 		$next = ( ( $i + 1 ) == $len ) ? '' : $html { $i + 1 };
  2062     if ( $in_quote && $in_tag )
  2062 		if ( $in_quote && $in_tag )
  2063     {
  2063 		{
  2064       if ( $quote_char == $chr && $prev != '\\' )
  2064 			if ( $quote_char == $chr && $prev != '\\' )
  2065         $in_quote = false;
  2065 				$in_quote = false;
  2066     }
  2066 		}
  2067     elseif ( ( $chr == '"' || $chr == "'" ) && $prev != '\\' && $in_tag )
  2067 		elseif ( ( $chr == '"' || $chr == "'" ) && $prev != '\\' && $in_tag )
  2068     {
  2068 		{
  2069       $in_quote = true;
  2069 			$in_quote = true;
  2070       $quote_char = $chr;
  2070 			$quote_char = $chr;
  2071     }
  2071 		}
  2072     if ( $chr == '<' && !$in_tag && $next != '/' )
  2072 		if ( $chr == '<' && !$in_tag && $next != '/' )
  2073     {
  2073 		{
  2074       // start of a tag
  2074 			// start of a tag
  2075       $tag_start = $i;
  2075 			$tag_start = $i;
  2076       $in_tag = true;
  2076 			$in_tag = true;
  2077       $trk_name = true;
  2077 			$trk_name = true;
  2078     }
  2078 		}
  2079     elseif ( !$in_quote && $in_tag && $chr == '>' )
  2079 		elseif ( !$in_quote && $in_tag && $chr == '>' )
  2080     {
  2080 		{
  2081       $full_tag = substr($html, $tag_start, ( $i - $tag_start ) + 1 );
  2081 			$full_tag = substr($html, $tag_start, ( $i - $tag_start ) + 1 );
  2082       $l = strlen($tag_name) + 2;
  2082 			$l = strlen($tag_name) + 2;
  2083       $attribs_only = trim( substr($full_tag, $l, ( strlen($full_tag) - $l - 1 ) ) );
  2083 			$attribs_only = trim( substr($full_tag, $l, ( strlen($full_tag) - $l - 1 ) ) );
  2084 
  2084 
  2085       // Debugging message
  2085 			// Debugging message
  2086       // echo htmlspecialchars($full_tag) . '<br />';
  2086 			// echo htmlspecialchars($full_tag) . '<br />';
  2087 
  2087 
  2088       if ( !in_array($tag_name, $tag_whitelist) && substr($tag_name, 0, 3) != '!--' )
  2088 			if ( !in_array($tag_name, $tag_whitelist) && substr($tag_name, 0, 3) != '!--' )
  2089       {
  2089 			{
  2090         // Illegal tag
  2090 				// Illegal tag
  2091         //echo $tag_name . ' ';
  2091 				//echo $tag_name . ' ';
  2092 
  2092 
  2093         $s = ( empty($attribs_only) ) ? '' : ' ';
  2093 				$s = ( empty($attribs_only) ) ? '' : ' ';
  2094 
  2094 
  2095         $sanitized = '&lt;' . $tag_name . $s . $attribs_only . '&gt;';
  2095 				$sanitized = '&lt;' . $tag_name . $s . $attribs_only . '&gt;';
  2096 
  2096 
  2097         $html = substr($html, 0, $tag_start) . $sanitized . substr($html, $i + 1);
  2097 				$html = substr($html, 0, $tag_start) . $sanitized . substr($html, $i + 1);
  2098         $html = str_replace('</' . $tag_name . '>', '&lt;/' . $tag_name . '&gt;', $html);
  2098 				$html = str_replace('</' . $tag_name . '>', '&lt;/' . $tag_name . '&gt;', $html);
  2099         $new_i = $tag_start + strlen($sanitized);
  2099 				$new_i = $tag_start + strlen($sanitized);
  2100 
  2100 
  2101         $len = strlen($html);
  2101 				$len = strlen($html);
  2102         $i = $new_i;
  2102 				$i = $new_i;
  2103 
  2103 
  2104         $in_tag = false;
  2104 				$in_tag = false;
  2105         $tag_name = '';
  2105 				$tag_name = '';
  2106         continue;
  2106 				continue;
  2107       }
  2107 			}
  2108       else
  2108 			else
  2109       {
  2109 			{
  2110         // If not filtering PHP, don't bother to strip
  2110 				// If not filtering PHP, don't bother to strip
  2111         if ( $tag_name == '?php' && !$filter_php )
  2111 				if ( $tag_name == '?php' && !$filter_php )
  2112           continue;
  2112 					continue;
  2113         // If this is a comment, likewise skip this "tag"
  2113 				// If this is a comment, likewise skip this "tag"
  2114         if ( $tag_name == '!--' )
  2114 				if ( $tag_name == '!--' )
  2115           continue;
  2115 					continue;
  2116         $f = fixTagAttributes( $attribs_only, $tag_name );
  2116 				$f = fixTagAttributes( $attribs_only, $tag_name );
  2117         $s = ( empty($f) ) ? '' : ' ';
  2117 				$s = ( empty($f) ) ? '' : ' ';
  2118 
  2118 
  2119         $sanitized = '<' . $tag_name . $f . '>';
  2119 				$sanitized = '<' . $tag_name . $f . '>';
  2120         $new_i = $tag_start + strlen($sanitized);
  2120 				$new_i = $tag_start + strlen($sanitized);
  2121 
  2121 
  2122         $html = substr($html, 0, $tag_start) . $sanitized . substr($html, $i + 1);
  2122 				$html = substr($html, 0, $tag_start) . $sanitized . substr($html, $i + 1);
  2123         $len = strlen($html);
  2123 				$len = strlen($html);
  2124         $i = $new_i;
  2124 				$i = $new_i;
  2125 
  2125 
  2126         $in_tag = false;
  2126 				$in_tag = false;
  2127         $tag_name = '';
  2127 				$tag_name = '';
  2128         continue;
  2128 				continue;
  2129       }
  2129 			}
  2130     }
  2130 		}
  2131     elseif ( $in_tag && $trk_name )
  2131 		elseif ( $in_tag && $trk_name )
  2132     {
  2132 		{
  2133       $is_alphabetical = ( strtolower($chr) != strtoupper($chr) || in_array($chr, array('0', '1', '2', '3', '4', '5', '6', '7', '8', '9')) || $chr == '?' || $chr == '!' || $chr == '-' );
  2133 			$is_alphabetical = ( strtolower($chr) != strtoupper($chr) || in_array($chr, array('0', '1', '2', '3', '4', '5', '6', '7', '8', '9')) || $chr == '?' || $chr == '!' || $chr == '-' );
  2134       if ( $is_alphabetical )
  2134 			if ( $is_alphabetical )
  2135         $tag_name .= $chr;
  2135 				$tag_name .= $chr;
  2136       else
  2136 			else
  2137       {
  2137 			{
  2138         $trk_name = false;
  2138 				$trk_name = false;
  2139       }
  2139 			}
  2140     }
  2140 		}
  2141 
  2141 
  2142   }
  2142 	}
  2143   
  2143 	
  2144   // Vulnerability from ha.ckers.org/xss.html:
  2144 	// Vulnerability from ha.ckers.org/xss.html:
  2145   // <script src="http://foo.com/xss.js"
  2145 	// <script src="http://foo.com/xss.js"
  2146   // <
  2146 	// <
  2147   // The rule is so specific because everything else will have been filtered by now
  2147 	// The rule is so specific because everything else will have been filtered by now
  2148   $html = preg_replace('/<(script|iframe)(.+?)src=([^>]*)</i', '&lt;\\1\\2src=\\3&lt;', $html);
  2148 	$html = preg_replace('/<(script|iframe)(.+?)src=([^>]*)</i', '&lt;\\1\\2src=\\3&lt;', $html);
  2149   
  2149 	
  2150   // Vulnerability reported by fuzion from nukeit.org:
  2150 	// Vulnerability reported by fuzion from nukeit.org:
  2151   // XSS in closing HTML tag style attribute
  2151 	// XSS in closing HTML tag style attribute
  2152   // Fix: escape all closing tags with non-whitelisted characters
  2152 	// Fix: escape all closing tags with non-whitelisted characters
  2153   $html = preg_replace('!</((?:[^>]*)([^a-z0-9_:>-]+)(?:[^>]*))>!i', '&lt;/\\1&gt;', $html);
  2153 	$html = preg_replace('!</((?:[^>]*)([^a-z0-9_:>-]+)(?:[^>]*))>!i', '&lt;/\\1&gt;', $html);
  2154 
  2154 
  2155   // Restore stripped comments
  2155 	// Restore stripped comments
  2156   $i = 0;
  2156 	$i = 0;
  2157   foreach ( $comment_match[0] as $comment )
  2157 	foreach ( $comment_match[0] as $comment )
  2158   {
  2158 	{
  2159     $html = str_replace_once("{HTMLCOMMENT:$i:$rand_seed}", $comment, $html);
  2159 		$html = str_replace_once("{HTMLCOMMENT:$i:$rand_seed}", $comment, $html);
  2160     $i++;
  2160 		$i++;
  2161   }
  2161 	}
  2162   
  2162 	
  2163   // Restore stripped code
  2163 	// Restore stripped code
  2164   $i = 0;
  2164 	$i = 0;
  2165   foreach ( $code_match[0] as $code )
  2165 	foreach ( $code_match[0] as $code )
  2166   {
  2166 	{
  2167     $html = str_replace_once("{TW_CODE:$i:$rand_seed}", $code, $html);
  2167 		$html = str_replace_once("{TW_CODE:$i:$rand_seed}", $code, $html);
  2168     $i++;
  2168 		$i++;
  2169   }
  2169 	}
  2170 
  2170 
  2171   return $html;
  2171 	return $html;
  2172 }
  2172 }
  2173 
  2173 
  2174 /**
  2174 /**
  2175  * Using the same parsing code as sanitize_html(), this function adds <litewiki> tags around certain block-level elements
  2175  * Using the same parsing code as sanitize_html(), this function adds <litewiki> tags around certain block-level elements
  2176  * @param string $html The input HTML
  2176  * @param string $html The input HTML
  2178  */
  2178  */
  2179 
  2179 
  2180 function wikiformat_process_block($html)
  2180 function wikiformat_process_block($html)
  2181 {
  2181 {
  2182 
  2182 
  2183   $tok1 = "<litewiki>";
  2183 	$tok1 = "<litewiki>";
  2184   $tok2 = "</litewiki>";
  2184 	$tok2 = "</litewiki>";
  2185 
  2185 
  2186   $block_tags = array('div', 'p', 'table', 'blockquote', 'pre');
  2186 	$block_tags = array('div', 'p', 'table', 'blockquote', 'pre');
  2187 
  2187 
  2188   $len = strlen($html);
  2188 	$len = strlen($html);
  2189   $in_quote = false;
  2189 	$in_quote = false;
  2190   $quote_char = '';
  2190 	$quote_char = '';
  2191   $tag_start = 0;
  2191 	$tag_start = 0;
  2192   $tag_name = '';
  2192 	$tag_name = '';
  2193   $in_tag = false;
  2193 	$in_tag = false;
  2194   $trk_name = false;
  2194 	$trk_name = false;
  2195 
  2195 
  2196   $diag = 0;
  2196 	$diag = 0;
  2197 
  2197 
  2198   $block_tagname = '';
  2198 	$block_tagname = '';
  2199   $in_blocksec = 0;
  2199 	$in_blocksec = 0;
  2200   $block_start = 0;
  2200 	$block_start = 0;
  2201 
  2201 
  2202   for ( $i = 0; $i < $len; $i++ )
  2202 	for ( $i = 0; $i < $len; $i++ )
  2203   {
  2203 	{
  2204     $chr = $html{$i};
  2204 		$chr = $html{$i};
  2205     $prev = ( $i == 0 ) ? '' : $html{ $i - 1 };
  2205 		$prev = ( $i == 0 ) ? '' : $html{ $i - 1 };
  2206     $next = ( ( $i + 1 ) == $len ) ? '' : $html { $i + 1 };
  2206 		$next = ( ( $i + 1 ) == $len ) ? '' : $html { $i + 1 };
  2207 
  2207 
  2208     // Are we inside of a quoted section?
  2208 		// Are we inside of a quoted section?
  2209     if ( $in_quote && $in_tag )
  2209 		if ( $in_quote && $in_tag )
  2210     {
  2210 		{
  2211       if ( $quote_char == $chr && $prev != '\\' )
  2211 			if ( $quote_char == $chr && $prev != '\\' )
  2212         $in_quote = false;
  2212 				$in_quote = false;
  2213     }
  2213 		}
  2214     elseif ( ( $chr == '"' || $chr == "'" ) && $prev != '\\' && $in_tag )
  2214 		elseif ( ( $chr == '"' || $chr == "'" ) && $prev != '\\' && $in_tag )
  2215     {
  2215 		{
  2216       $in_quote = true;
  2216 			$in_quote = true;
  2217       $quote_char = $chr;
  2217 			$quote_char = $chr;
  2218     }
  2218 		}
  2219 
  2219 
  2220     if ( $chr == '<' && !$in_tag && $next == '/' )
  2220 		if ( $chr == '<' && !$in_tag && $next == '/' )
  2221     {
  2221 		{
  2222       // Iterate through until we've got a tag name
  2222 			// Iterate through until we've got a tag name
  2223       $tag_name = '';
  2223 			$tag_name = '';
  2224       $i++;
  2224 			$i++;
  2225       while(true)
  2225 			while(true)
  2226       {
  2226 			{
  2227         $i++;
  2227 				$i++;
  2228         // echo $i . ' ';
  2228 				// echo $i . ' ';
  2229         $chr = $html{$i};
  2229 				$chr = $html{$i};
  2230         $prev = ( $i == 0 ) ? '' : $html{ $i - 1 };
  2230 				$prev = ( $i == 0 ) ? '' : $html{ $i - 1 };
  2231         $next = ( ( $i + 1 ) == $len ) ? '' : $html { $i + 1 };
  2231 				$next = ( ( $i + 1 ) == $len ) ? '' : $html { $i + 1 };
  2232         $tag_name .= $chr;
  2232 				$tag_name .= $chr;
  2233         if ( $next == '>' )
  2233 				if ( $next == '>' )
  2234           break;
  2234 					break;
  2235       }
  2235 			}
  2236       // echo '<br />';
  2236 			// echo '<br />';
  2237       if ( in_array($tag_name, $block_tags) )
  2237 			if ( in_array($tag_name, $block_tags) )
  2238       {
  2238 			{
  2239         if ( $block_tagname == $tag_name )
  2239 				if ( $block_tagname == $tag_name )
  2240         {
  2240 				{
  2241           $in_blocksec -= 1;
  2241 					$in_blocksec -= 1;
  2242           if ( $in_blocksec == 0 )
  2242 					if ( $in_blocksec == 0 )
  2243           {
  2243 					{
  2244             $block_tagname = '';
  2244 						$block_tagname = '';
  2245             $i += 2;
  2245 						$i += 2;
  2246             // echo 'Finished wiki litewiki wraparound calc at pos: ' . $i;
  2246 						// echo 'Finished wiki litewiki wraparound calc at pos: ' . $i;
  2247             $full_litewiki = substr($html, $block_start, ( $i - $block_start ));
  2247 						$full_litewiki = substr($html, $block_start, ( $i - $block_start ));
  2248             $new_text = "{$tok1}{$full_litewiki}{$tok2}";
  2248 						$new_text = "{$tok1}{$full_litewiki}{$tok2}";
  2249             $html = substr($html, 0, $block_start) . $new_text . substr($html, $i);
  2249 						$html = substr($html, 0, $block_start) . $new_text . substr($html, $i);
  2250 
  2250 
  2251             $i += ( strlen($tok1) + strlen($tok2) ) - 1;
  2251 						$i += ( strlen($tok1) + strlen($tok2) ) - 1;
  2252             $len = strlen($html);
  2252 						$len = strlen($html);
  2253 
  2253 
  2254             //die('<pre>' . htmlspecialchars($html) . '</pre>');
  2254 						//die('<pre>' . htmlspecialchars($html) . '</pre>');
  2255           }
  2255 					}
  2256         }
  2256 				}
  2257       }
  2257 			}
  2258 
  2258 
  2259       $in_tag = false;
  2259 			$in_tag = false;
  2260       $in_quote = false;
  2260 			$in_quote = false;
  2261       $tag_name = '';
  2261 			$tag_name = '';
  2262 
  2262 
  2263       continue;
  2263 			continue;
  2264     }
  2264 		}
  2265     else if ( $chr == '<' && !$in_tag && $next != '/' )
  2265 		else if ( $chr == '<' && !$in_tag && $next != '/' )
  2266     {
  2266 		{
  2267       // start of a tag
  2267 			// start of a tag
  2268       $tag_start = $i;
  2268 			$tag_start = $i;
  2269       $in_tag = true;
  2269 			$in_tag = true;
  2270       $trk_name = true;
  2270 			$trk_name = true;
  2271     }
  2271 		}
  2272     else if ( !$in_quote && $in_tag && $chr == '>' )
  2272 		else if ( !$in_quote && $in_tag && $chr == '>' )
  2273     {
  2273 		{
  2274       if ( !in_array($tag_name, $block_tags) )
  2274 			if ( !in_array($tag_name, $block_tags) )
  2275       {
  2275 			{
  2276         // Inline tag - reset and go to the next one
  2276 				// Inline tag - reset and go to the next one
  2277         // echo '&lt;inline ' . $tag_name . '&gt; ';
  2277 				// echo '&lt;inline ' . $tag_name . '&gt; ';
  2278 
  2278 
  2279         $in_tag = false;
  2279 				$in_tag = false;
  2280         $tag_name = '';
  2280 				$tag_name = '';
  2281         continue;
  2281 				continue;
  2282       }
  2282 			}
  2283       else
  2283 			else
  2284       {
  2284 			{
  2285         // echo '&lt;block: ' . $tag_name . ' @ ' . $i . '&gt;<br/>';
  2285 				// echo '&lt;block: ' . $tag_name . ' @ ' . $i . '&gt;<br/>';
  2286         if ( $in_blocksec == 0 )
  2286 				if ( $in_blocksec == 0 )
  2287         {
  2287 				{
  2288           //die('Found a starting tag for a block element: ' . $tag_name . ' at pos ' . $tag_start);
  2288 					//die('Found a starting tag for a block element: ' . $tag_name . ' at pos ' . $tag_start);
  2289           $block_tagname = $tag_name;
  2289 					$block_tagname = $tag_name;
  2290           $block_start = $tag_start;
  2290 					$block_start = $tag_start;
  2291           $in_blocksec++;
  2291 					$in_blocksec++;
  2292         }
  2292 				}
  2293         else if ( $block_tagname == $tag_name )
  2293 				else if ( $block_tagname == $tag_name )
  2294         {
  2294 				{
  2295           $in_blocksec++;
  2295 					$in_blocksec++;
  2296         }
  2296 				}
  2297 
  2297 
  2298         $in_tag = false;
  2298 				$in_tag = false;
  2299         $tag_name = '';
  2299 				$tag_name = '';
  2300         continue;
  2300 				continue;
  2301       }
  2301 			}
  2302     }
  2302 		}
  2303     elseif ( $in_tag && $trk_name )
  2303 		elseif ( $in_tag && $trk_name )
  2304     {
  2304 		{
  2305       $is_alphabetical = ( strtolower($chr) != strtoupper($chr) || in_array($chr, array('0', '1', '2', '3', '4', '5', '6', '7', '8', '9')) || $chr == '?' || $chr == '!' || $chr == '-' );
  2305 			$is_alphabetical = ( strtolower($chr) != strtoupper($chr) || in_array($chr, array('0', '1', '2', '3', '4', '5', '6', '7', '8', '9')) || $chr == '?' || $chr == '!' || $chr == '-' );
  2306       if ( $is_alphabetical )
  2306 			if ( $is_alphabetical )
  2307         $tag_name .= $chr;
  2307 				$tag_name .= $chr;
  2308       else
  2308 			else
  2309       {
  2309 			{
  2310         $trk_name = false;
  2310 				$trk_name = false;
  2311       }
  2311 			}
  2312     }
  2312 		}
  2313 
  2313 
  2314     // Tokenization complete
  2314 		// Tokenization complete
  2315 
  2315 
  2316   }
  2316 	}
  2317 
  2317 
  2318   $regex = '/' . str_replace('/', '\\/', preg_quote($tok2)) . '([\s]*)' . preg_quote($tok1) . '/is';
  2318 	$regex = '/' . str_replace('/', '\\/', preg_quote($tok2)) . '([\s]*)' . preg_quote($tok1) . '/is';
  2319   // die(htmlspecialchars($regex));
  2319 	// die(htmlspecialchars($regex));
  2320   $html = preg_replace($regex, '\\1', $html);
  2320 	$html = preg_replace($regex, '\\1', $html);
  2321 
  2321 
  2322   return $html;
  2322 	return $html;
  2323 
  2323 
  2324 }
  2324 }
  2325 
  2325 
  2326 function htmlalternatives($string)
  2326 function htmlalternatives($string)
  2327 {
  2327 {
  2328   $ret = '';
  2328 	$ret = '';
  2329   for ( $i = 0; $i < strlen($string); $i++ )
  2329 	for ( $i = 0; $i < strlen($string); $i++ )
  2330   {
  2330 	{
  2331     $chr = $string{$i};
  2331 		$chr = $string{$i};
  2332     $ch1 = ord($chr);
  2332 		$ch1 = ord($chr);
  2333     $ch2 = dechex($ch1);
  2333 		$ch2 = dechex($ch1);
  2334     $byte = '(&\\#([0]*){0,7}' . $ch1 . ';|\\\\([0]*){0,7}' . $ch1 . ';|\\\\([0]*){0,7}' . $ch2 . ';|&\\#x([0]*){0,7}' . $ch2 . ';|%([0]*){0,7}' . $ch2 . '|' . preg_quote($chr) . ')';
  2334 		$byte = '(&\\#([0]*){0,7}' . $ch1 . ';|\\\\([0]*){0,7}' . $ch1 . ';|\\\\([0]*){0,7}' . $ch2 . ';|&\\#x([0]*){0,7}' . $ch2 . ';|%([0]*){0,7}' . $ch2 . '|' . preg_quote($chr) . ')';
  2335     $ret .= $byte;
  2335 		$ret .= $byte;
  2336     $ret .= '([\s]){0,2}';
  2336 		$ret .= '([\s]){0,2}';
  2337   }
  2337 	}
  2338   return $ret;
  2338 	return $ret;
  2339 }
  2339 }
  2340 
  2340 
  2341 /**
  2341 /**
  2342  * Generate HTML for a sprite image.
  2342  * Generate HTML for a sprite image.
  2343  * @param string Path to sprite image
  2343  * @param string Path to sprite image
  2348  * @return object HTMLImageElement
  2348  * @return object HTMLImageElement
  2349  */
  2349  */
  2350 
  2350 
  2351 function gen_sprite($path, $width, $height, $xpos, $ypos)
  2351 function gen_sprite($path, $width, $height, $xpos, $ypos)
  2352 {
  2352 {
  2353   $html = '<img src="' . scriptPath . '/images/spacer.gif" width="' . $width . '" height="' . $height . '" ';
  2353 	$html = '<img src="' . scriptPath . '/images/spacer.gif" width="' . $width . '" height="' . $height . '" ';
  2354   $xpos = ( $xpos == 0 ) ? '0' : '-' . strval($xpos);
  2354 	$xpos = ( $xpos == 0 ) ? '0' : '-' . strval($xpos);
  2355   $ypos = ( $ypos == 0 ) ? '0' : '-' . strval($ypos);
  2355 	$ypos = ( $ypos == 0 ) ? '0' : '-' . strval($ypos);
  2356   $html .= 'style="background-image: url(' . $path . '); background-repeat: no-repeat; background-position: ' . $ypos . 'px ' . $xpos . 'px;"';
  2356 	$html .= 'style="background-image: url(' . $path . '); background-repeat: no-repeat; background-position: ' . $ypos . 'px ' . $xpos . 'px;"';
  2357   $html .= ' />';
  2357 	$html .= ' />';
  2358   
  2358 	
  2359   return $html;
  2359 	return $html;
  2360 }
  2360 }
  2361 
  2361 
  2362 /**
  2362 /**
  2363  * Portal function allowing spam-filtering plugins.
  2363  * Portal function allowing spam-filtering plugins.
  2364  * Hooking guide:
  2364  * Hooking guide:
  2367  * @example
  2367  * @example
  2368  <code>
  2368  <code>
  2369  $plugins->attachHook('spam_check', 'return my_spam_check($string);');
  2369  $plugins->attachHook('spam_check', 'return my_spam_check($string);');
  2370  function my_spam_check($string)
  2370  function my_spam_check($string)
  2371  {
  2371  {
  2372    if ( stristr($string, 'viagra') )
  2372  	if ( stristr($string, 'viagra') )
  2373      return false;
  2373  		return false;
  2374    
  2374  	
  2375    return true;
  2375  	return true;
  2376  }
  2376  }
  2377  </code>
  2377  </code>
  2378  * @param string String to check for spam
  2378  * @param string String to check for spam
  2379  * @param string Author name
  2379  * @param string Author name
  2380  * @param string Author e-mail
  2380  * @param string Author e-mail
  2383  * @return bool
  2383  * @return bool
  2384  */
  2384  */
  2385 
  2385 
  2386 function spamalyze($string, $name = false, $email = false, $url = false, $ip = false)
  2386 function spamalyze($string, $name = false, $email = false, $url = false, $ip = false)
  2387 {
  2387 {
  2388   global $db, $session, $paths, $template, $plugins; // Common objects
  2388 	global $db, $session, $paths, $template, $plugins; // Common objects
  2389   if ( !$ip )
  2389 	if ( !$ip )
  2390     $ip =& $_SERVER['REMOTE_ADDR'];
  2390 		$ip =& $_SERVER['REMOTE_ADDR'];
  2391   
  2391 	
  2392   $code = $plugins->setHook('spam_check');
  2392 	$code = $plugins->setHook('spam_check');
  2393   foreach ( $code as $cmd )
  2393 	foreach ( $code as $cmd )
  2394   {
  2394 	{
  2395     $result = eval($cmd);
  2395 		$result = eval($cmd);
  2396     if ( !$result )
  2396 		if ( !$result )
  2397       return false;
  2397 			return false;
  2398   }
  2398 	}
  2399   return true;
  2399 	return true;
  2400 }
  2400 }
  2401 
  2401 
  2402 /**
  2402 /**
  2403  * Generates the HTML of a pagination control.
  2403  * Generates the HTML of a pagination control.
  2404  * @param int Current page
  2404  * @param int Current page
  2409  * @return string HTML
  2409  * @return string HTML
  2410  */
  2410  */
  2411 
  2411 
  2412 function generate_paginator($current_page, $num_pages, $result_url, $start_mult = 1, $start_add = 1)
  2412 function generate_paginator($current_page, $num_pages, $result_url, $start_mult = 1, $start_add = 1)
  2413 {
  2413 {
  2414   global $db, $session, $paths, $template, $plugins; // Common objects
  2414 	global $db, $session, $paths, $template, $plugins; // Common objects
  2415   global $lang;
  2415 	global $lang;
  2416   
  2416 	
  2417   $out = '';
  2417 	$out = '';
  2418   $i = 0;
  2418 	$i = 0;
  2419 
  2419 
  2420   // Build paginator
  2420 	// Build paginator
  2421   $pg_css = ( strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE') ) ?
  2421 	$pg_css = ( strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE') ) ?
  2422             // IE-specific hack
  2422 						// IE-specific hack
  2423             'display: block; width: 1px;':
  2423 						'display: block; width: 1px;':
  2424             // Other browsers
  2424 						// Other browsers
  2425             'display: table; margin: 10px 0 0 auto;';
  2425 						'display: table; margin: 10px 0 0 auto;';
  2426   
  2426 	
  2427   $begin = '<div class="tblholder" style="'. $pg_css . '">
  2427 	$begin = '<div class="tblholder" style="'. $pg_css . '">
  2428     <table border="0" cellspacing="1" cellpadding="4">
  2428 		<table border="0" cellspacing="1" cellpadding="4">
  2429       <tr><th>' . $lang->get('paginate_lbl_page') . '</th>';
  2429 			<tr><th>' . $lang->get('paginate_lbl_page') . '</th>';
  2430   $block = '<td class="row1" style="text-align: center;">{LINK}</td>';
  2430 	$block = '<td class="row1" style="text-align: center;">{LINK}</td>';
  2431   $end = '</tr></table></div>';
  2431 	$end = '</tr></table></div>';
  2432   $blk = $template->makeParserText($block);
  2432 	$blk = $template->makeParserText($block);
  2433   $inner = '';
  2433 	$inner = '';
  2434   $cls = 'row2';
  2434 	$cls = 'row2';
  2435   if ( $num_pages < 5 )
  2435 	if ( $num_pages < 5 )
  2436   {
  2436 	{
  2437     for ( $i = 0; $i < $num_pages; $i++ )
  2437 		for ( $i = 0; $i < $num_pages; $i++ )
  2438     {
  2438 		{
  2439       $cls = ( $cls == 'row1' ) ? 'row2' : 'row1';
  2439 			$cls = ( $cls == 'row1' ) ? 'row2' : 'row1';
  2440       $offset = strval(($i * $start_mult) + $start_add);
  2440 			$offset = strval(($i * $start_mult) + $start_add);
  2441       $url = htmlspecialchars(sprintf($result_url, $offset));
  2441 			$url = htmlspecialchars(sprintf($result_url, $offset));
  2442       $j = $i + 1;
  2442 			$j = $i + 1;
  2443       $link = ( $i == $current_page ) ? "<b>$j</b>" : "<a href=".'"'."$url".'"'." style='text-decoration: none;'>$j</a>";
  2443 			$link = ( $i == $current_page ) ? "<b>$j</b>" : "<a href=".'"'."$url".'"'." style='text-decoration: none;'>$j</a>";
  2444       $blk->assign_vars(array(
  2444 			$blk->assign_vars(array(
  2445         'CLASS'=>$cls,
  2445 				'CLASS'=>$cls,
  2446         'LINK'=>$link
  2446 				'LINK'=>$link
  2447         ));
  2447 				));
  2448       $inner .= $blk->run();
  2448 			$inner .= $blk->run();
  2449     }
  2449 		}
  2450   }
  2450 	}
  2451   else
  2451 	else
  2452   {
  2452 	{
  2453     if ( $current_page + 5 > $num_pages )
  2453 		if ( $current_page + 5 > $num_pages )
  2454     {
  2454 		{
  2455       $list = Array();
  2455 			$list = Array();
  2456       $tp = $current_page;
  2456 			$tp = $current_page;
  2457       if ( $current_page + 0 == $num_pages ) $tp = $tp - 3;
  2457 			if ( $current_page + 0 == $num_pages ) $tp = $tp - 3;
  2458       if ( $current_page + 1 == $num_pages ) $tp = $tp - 2;
  2458 			if ( $current_page + 1 == $num_pages ) $tp = $tp - 2;
  2459       if ( $current_page + 2 == $num_pages ) $tp = $tp - 1;
  2459 			if ( $current_page + 2 == $num_pages ) $tp = $tp - 1;
  2460       for ( $i = $tp - 1; $i <= $tp + 1; $i++ )
  2460 			for ( $i = $tp - 1; $i <= $tp + 1; $i++ )
  2461       {
  2461 			{
  2462         $list[] = $i;
  2462 				$list[] = $i;
  2463       }
  2463 			}
  2464     }
  2464 		}
  2465     else
  2465 		else
  2466     {
  2466 		{
  2467       $list = Array();
  2467 			$list = Array();
  2468       $current = $current_page;
  2468 			$current = $current_page;
  2469       $lower = ( $current < 3 ) ? 1 : $current - 1;
  2469 			$lower = ( $current < 3 ) ? 1 : $current - 1;
  2470       for ( $i = 0; $i < 3; $i++ )
  2470 			for ( $i = 0; $i < 3; $i++ )
  2471       {
  2471 			{
  2472         $list[] = $lower + $i;
  2472 				$list[] = $lower + $i;
  2473       }
  2473 			}
  2474     }
  2474 		}
  2475     $url = sprintf($result_url, $start_add);
  2475 		$url = sprintf($result_url, $start_add);
  2476     $link = ( 0 == $current_page ) ? "<b>" . $lang->get('paginate_btn_first') . "</b>" : "<a href=".'"'."$url".'"'." style='text-decoration: none;'>&laquo; " . $lang->get('paginate_btn_first') . "</a>";
  2476 		$link = ( 0 == $current_page ) ? "<b>" . $lang->get('paginate_btn_first') . "</b>" : "<a href=".'"'."$url".'"'." style='text-decoration: none;'>&laquo; " . $lang->get('paginate_btn_first') . "</a>";
  2477     $blk->assign_vars(array(
  2477 		$blk->assign_vars(array(
  2478       'CLASS'=>$cls,
  2478 			'CLASS'=>$cls,
  2479       'LINK'=>$link
  2479 			'LINK'=>$link
  2480       ));
  2480 			));
  2481     $inner .= $blk->run();
  2481 		$inner .= $blk->run();
  2482 
  2482 
  2483     foreach ( $list as $i )
  2483 		foreach ( $list as $i )
  2484     {
  2484 		{
  2485       if ( $i == $num_pages )
  2485 			if ( $i == $num_pages )
  2486         break;
  2486 				break;
  2487       $cls = ( $cls == 'row1' ) ? 'row2' : 'row1';
  2487 			$cls = ( $cls == 'row1' ) ? 'row2' : 'row1';
  2488       $offset = strval(($i * $start_mult) + $start_add);
  2488 			$offset = strval(($i * $start_mult) + $start_add);
  2489       $url = sprintf($result_url, $offset);
  2489 			$url = sprintf($result_url, $offset);
  2490       $j = $i + 1;
  2490 			$j = $i + 1;
  2491       $link = ( $i == $current_page ) ? "<b>$j</b>" : "<a href=".'"'."$url".'"'." style='text-decoration: none;'>$j</a>";
  2491 			$link = ( $i == $current_page ) ? "<b>$j</b>" : "<a href=".'"'."$url".'"'." style='text-decoration: none;'>$j</a>";
  2492       $blk->assign_vars(array(
  2492 			$blk->assign_vars(array(
  2493         'CLASS'=>$cls,
  2493 				'CLASS'=>$cls,
  2494         'LINK'=>$link
  2494 				'LINK'=>$link
  2495         ));
  2495 				));
  2496       $inner .= $blk->run();
  2496 			$inner .= $blk->run();
  2497     }
  2497 		}
  2498 
  2498 
  2499     // "Last" button
  2499 		// "Last" button
  2500     $total = (($num_pages - 1) * $start_mult) + $start_add;
  2500 		$total = (($num_pages - 1) * $start_mult) + $start_add;
  2501 
  2501 
  2502     if ( $current_page < $num_pages )
  2502 		if ( $current_page < $num_pages )
  2503     {
  2503 		{
  2504       $cls = ( $cls == 'row1' ) ? 'row2' : 'row1';
  2504 			$cls = ( $cls == 'row1' ) ? 'row2' : 'row1';
  2505       $offset = strval($total);
  2505 			$offset = strval($total);
  2506       $url = sprintf($result_url, $offset);
  2506 			$url = sprintf($result_url, $offset);
  2507       $link = ( $num_pages - 1 == $current_page ) ? "<b>" . $lang->get('paginate_btn_last') . "</b>" : "<a href=".'"'."$url".'"'." style='text-decoration: none;'>" . $lang->get('paginate_btn_last') . " &raquo;</a>";
  2507 			$link = ( $num_pages - 1 == $current_page ) ? "<b>" . $lang->get('paginate_btn_last') . "</b>" : "<a href=".'"'."$url".'"'." style='text-decoration: none;'>" . $lang->get('paginate_btn_last') . " &raquo;</a>";
  2508       $blk->assign_vars(array(
  2508 			$blk->assign_vars(array(
  2509         'CLASS'=>$cls,
  2509 				'CLASS'=>$cls,
  2510         'LINK'=>$link
  2510 				'LINK'=>$link
  2511         ));
  2511 				));
  2512       $inner .= $blk->run();
  2512 			$inner .= $blk->run();
  2513     }
  2513 		}
  2514 
  2514 
  2515   }
  2515 	}
  2516 
  2516 
  2517   $inner .= '<td class="row2" style="cursor: pointer;" onclick="paginator_goto(this, '.$current_page.', '.$num_pages.', '.$start_mult.', '.$start_add.', unescape(\'' . rawurlencode($result_url) . '\'));">&darr;</td>';
  2517 	$inner .= '<td class="row2" style="cursor: pointer;" onclick="paginator_goto(this, '.$current_page.', '.$num_pages.', '.$start_mult.', '.$start_add.', unescape(\'' . rawurlencode($result_url) . '\'));">&darr;</td>';
  2518 
  2518 
  2519   $paginator = "\n$begin$inner$end\n";
  2519 	$paginator = "\n$begin$inner$end\n";
  2520   return $paginator;
  2520 	return $paginator;
  2521 }
  2521 }
  2522 
  2522 
  2523 /**
  2523 /**
  2524  * Paginates (breaks into multiple pages) a MySQL result resource, which is treated as unbuffered.
  2524  * Paginates (breaks into multiple pages) a MySQL result resource, which is treated as unbuffered.
  2525  * @param resource The MySQL result resource. This should preferably be an unbuffered query.
  2525  * @param resource The MySQL result resource. This should preferably be an unbuffered query.
  2534  * @return string
  2534  * @return string
  2535  */
  2535  */
  2536 
  2536 
  2537 function paginate($q, $tpl_text, $num_results, $result_url, $start = 0, $perpage = 10, $callers = Array(), $header = '', $footer = '')
  2537 function paginate($q, $tpl_text, $num_results, $result_url, $start = 0, $perpage = 10, $callers = Array(), $header = '', $footer = '')
  2538 {
  2538 {
  2539   global $db, $session, $paths, $template, $plugins; // Common objects
  2539 	global $db, $session, $paths, $template, $plugins; // Common objects
  2540   
  2540 	
  2541   $parser = $template->makeParserText($tpl_text);
  2541 	$parser = $template->makeParserText($tpl_text);
  2542   
  2542 	
  2543   $num_pages = ceil ( $num_results / $perpage );
  2543 	$num_pages = ceil ( $num_results / $perpage );
  2544   $out = '';
  2544 	$out = '';
  2545   $this_page = ceil ( $start / $perpage );
  2545 	$this_page = ceil ( $start / $perpage );
  2546   $i = 0;
  2546 	$i = 0;
  2547   
  2547 	
  2548   if ( $num_results > 0 )
  2548 	if ( $num_results > 0 )
  2549   {
  2549 	{
  2550     $paginator = generate_paginator($this_page, $num_pages, $result_url, $perpage, 0);
  2550 		$paginator = generate_paginator($this_page, $num_pages, $result_url, $perpage, 0);
  2551     $out .= $paginator;
  2551 		$out .= $paginator;
  2552   }
  2552 	}
  2553 
  2553 
  2554   $cls = 'row2';
  2554 	$cls = 'row2';
  2555 
  2555 
  2556   if ( $row = $db->fetchrow($q) )
  2556 	if ( $row = $db->fetchrow($q) )
  2557   {
  2557 	{
  2558     $i = 0;
  2558 		$i = 0;
  2559     $out .= $header;
  2559 		$out .= $header;
  2560     do {
  2560 		do {
  2561       $i++;
  2561 			$i++;
  2562       if ( $i <= $start )
  2562 			if ( $i <= $start )
  2563       {
  2563 			{
  2564         continue;
  2564 				continue;
  2565       }
  2565 			}
  2566       if ( ( $i - $start ) > $perpage )
  2566 			if ( ( $i - $start ) > $perpage )
  2567       {
  2567 			{
  2568         break;
  2568 				break;
  2569       }
  2569 			}
  2570       $cls = ( $cls == 'row1' ) ? 'row2' : 'row1';
  2570 			$cls = ( $cls == 'row1' ) ? 'row2' : 'row1';
  2571       foreach ( $row as $j => $val )
  2571 			foreach ( $row as $j => $val )
  2572       {
  2572 			{
  2573         if ( isset($callers[$j]) )
  2573 				if ( isset($callers[$j]) )
  2574         {
  2574 				{
  2575           $tmp = ( is_callable($callers[$j]) ) ? call_user_func($callers[$j], $val, $row) : $val;
  2575 					$tmp = ( is_callable($callers[$j]) ) ? call_user_func($callers[$j], $val, $row) : $val;
  2576 
  2576 
  2577           if ( is_string($tmp) )
  2577 					if ( is_string($tmp) )
  2578           {
  2578 					{
  2579             $row[$j] = $tmp;
  2579 						$row[$j] = $tmp;
  2580           }
  2580 					}
  2581         }
  2581 				}
  2582       }
  2582 			}
  2583       $parser->assign_vars($row);
  2583 			$parser->assign_vars($row);
  2584       $parser->assign_vars(array('_css_class' => $cls));
  2584 			$parser->assign_vars(array('_css_class' => $cls));
  2585       $out .= $parser->run();
  2585 			$out .= $parser->run();
  2586     } while ( $row = @$db->fetchrow($q) );
  2586 		} while ( $row = @$db->fetchrow($q) );
  2587     $out .= $footer;
  2587 		$out .= $footer;
  2588   }
  2588 	}
  2589 
  2589 
  2590   if ( $num_results > 0 )
  2590 	if ( $num_results > 0 )
  2591     $out .= $paginator;
  2591 		$out .= $paginator;
  2592 
  2592 
  2593   return $out;
  2593 	return $out;
  2594 }
  2594 }
  2595 
  2595 
  2596 /**
  2596 /**
  2597  * This is the same as paginate(), but it processes an array instead of a MySQL result resource.
  2597  * This is the same as paginate(), but it processes an array instead of a MySQL result resource.
  2598  * @param array The results. Each value is simply echoed.
  2598  * @param array The results. Each value is simply echoed.
  2605  * @return string
  2605  * @return string
  2606  */
  2606  */
  2607 
  2607 
  2608 function paginate_array($q, $num_results, $result_url, $start = 0, $perpage = 10, $header = '', $footer = '')
  2608 function paginate_array($q, $num_results, $result_url, $start = 0, $perpage = 10, $header = '', $footer = '')
  2609 {
  2609 {
  2610   global $db, $session, $paths, $template, $plugins; // Common objects
  2610 	global $db, $session, $paths, $template, $plugins; // Common objects
  2611   global $lang;
  2611 	global $lang;
  2612   
  2612 	
  2613   $num_pages = ceil ( $num_results / $perpage );
  2613 	$num_pages = ceil ( $num_results / $perpage );
  2614   $out = '';
  2614 	$out = '';
  2615   $i = 0;
  2615 	$i = 0;
  2616   $this_page = ceil ( $start / $perpage );
  2616 	$this_page = ceil ( $start / $perpage );
  2617 
  2617 
  2618   $paginator = generate_paginator($this_page, $num_pages, $result_url, $perpage, 0);
  2618 	$paginator = generate_paginator($this_page, $num_pages, $result_url, $perpage, 0);
  2619   
  2619 	
  2620   if ( $num_results > 1 )
  2620 	if ( $num_results > 1 )
  2621   {
  2621 	{
  2622     $out .= $paginator;
  2622 		$out .= $paginator;
  2623   }
  2623 	}
  2624 
  2624 
  2625   $cls = 'row2';
  2625 	$cls = 'row2';
  2626 
  2626 
  2627   if ( sizeof($q) > 0 )
  2627 	if ( sizeof($q) > 0 )
  2628   {
  2628 	{
  2629     $i = 0;
  2629 		$i = 0;
  2630     $out .= $header;
  2630 		$out .= $header;
  2631     foreach ( $q as $val ) {
  2631 		foreach ( $q as $val ) {
  2632       $i++;
  2632 			$i++;
  2633       if ( $i <= $start )
  2633 			if ( $i <= $start )
  2634       {
  2634 			{
  2635         continue;
  2635 				continue;
  2636       }
  2636 			}
  2637       if ( ( $i - $start ) > $perpage )
  2637 			if ( ( $i - $start ) > $perpage )
  2638       {
  2638 			{
  2639         break;
  2639 				break;
  2640       }
  2640 			}
  2641       $out .= $val;
  2641 			$out .= $val;
  2642     }
  2642 		}
  2643     $out .= $footer;
  2643 		$out .= $footer;
  2644   }
  2644 	}
  2645 
  2645 
  2646   if ( $num_results > 1 )
  2646 	if ( $num_results > 1 )
  2647     $out .= $paginator;
  2647 		$out .= $paginator;
  2648 
  2648 
  2649   return $out;
  2649 	return $out;
  2650 }
  2650 }
  2651 
  2651 
  2652 /**
  2652 /**
  2653  * Enano version of fputs for debugging
  2653  * Enano version of fputs for debugging
  2654  */
  2654  */
  2655 
  2655 
  2656 function enano_fputs($socket, $data)
  2656 function enano_fputs($socket, $data)
  2657 {
  2657 {
  2658   // echo '<pre>' . htmlspecialchars($data) . '</pre>';
  2658 	// echo '<pre>' . htmlspecialchars($data) . '</pre>';
  2659   // flush();
  2659 	// flush();
  2660   // ob_flush();
  2660 	// ob_flush();
  2661   // ob_end_flush();
  2661 	// ob_end_flush();
  2662   return fputs($socket, $data);
  2662 	return fputs($socket, $data);
  2663 }
  2663 }
  2664 
  2664 
  2665 /**
  2665 /**
  2666  * Sanitizes a page URL string so that it can safely be stored in the database.
  2666  * Sanitizes a page URL string so that it can safely be stored in the database.
  2667  * @param string Page ID to sanitize
  2667  * @param string Page ID to sanitize
  2668  * @return string Cleaned text
  2668  * @return string Cleaned text
  2669  */
  2669  */
  2670 
  2670 
  2671 function sanitize_page_id($page_id)
  2671 function sanitize_page_id($page_id)
  2672 {
  2672 {
  2673   global $db, $session, $paths, $template, $plugins; // Common objects
  2673 	global $db, $session, $paths, $template, $plugins; // Common objects
  2674   
  2674 	
  2675   if ( isset($paths->nslist['User']) )
  2675 	if ( isset($paths->nslist['User']) )
  2676   {
  2676 	{
  2677     if ( preg_match('/^' . str_replace('/', '\\/', preg_quote($paths->nslist['User'])) . '/', $page_id) )
  2677 		if ( preg_match('/^' . str_replace('/', '\\/', preg_quote($paths->nslist['User'])) . '/', $page_id) )
  2678     {
  2678 		{
  2679       $ip = preg_replace('/^' . str_replace('/', '\\/', preg_quote($paths->nslist['User'])) . '/', '', $page_id);
  2679 			$ip = preg_replace('/^' . str_replace('/', '\\/', preg_quote($paths->nslist['User'])) . '/', '', $page_id);
  2680       if ( is_valid_ip($ip) )
  2680 			if ( is_valid_ip($ip) )
  2681       {
  2681 			{
  2682         return $page_id;
  2682 				return $page_id;
  2683       }
  2683 			}
  2684     }
  2684 		}
  2685   }
  2685 	}
  2686   
  2686 	
  2687   if ( empty($page_id) )
  2687 	if ( empty($page_id) )
  2688     return '';
  2688 		return '';
  2689   
  2689 	
  2690   // Remove character escapes
  2690 	// Remove character escapes
  2691   $page_id = dirtify_page_id($page_id);
  2691 	$page_id = dirtify_page_id($page_id);
  2692 
  2692 
  2693   $pid_clean = preg_replace('/[\w\.\/:;\(\)@\[\]=_-]/', 'X', $page_id);
  2693 	$pid_clean = preg_replace('/[\w\.\/:;\(\)@\[\]=_-]/', 'X', $page_id);
  2694   $pid_dirty = enano_str_split($pid_clean, 1);
  2694 	$pid_dirty = enano_str_split($pid_clean, 1);
  2695   
  2695 	
  2696   foreach ( $pid_dirty as $id => $char )
  2696 	foreach ( $pid_dirty as $id => $char )
  2697   {
  2697 	{
  2698     if ( $char == 'X' )
  2698 		if ( $char == 'X' )
  2699       continue;
  2699 			continue;
  2700     $cid = ord($char);
  2700 		$cid = ord($char);
  2701     $cid = dechex($cid);
  2701 		$cid = dechex($cid);
  2702     $cid = strval($cid);
  2702 		$cid = strval($cid);
  2703     if ( strlen($cid) < 2 )
  2703 		if ( strlen($cid) < 2 )
  2704     {
  2704 		{
  2705       $cid = strtoupper("0$cid");
  2705 			$cid = strtoupper("0$cid");
  2706     }
  2706 		}
  2707     $pid_dirty[$id] = ".$cid";
  2707 		$pid_dirty[$id] = ".$cid";
  2708   }
  2708 	}
  2709 
  2709 
  2710   $pid_chars = enano_str_split($page_id, 1);
  2710 	$pid_chars = enano_str_split($page_id, 1);
  2711   $page_id_cleaned = '';
  2711 	$page_id_cleaned = '';
  2712 
  2712 
  2713   foreach ( $pid_chars as $id => $char )
  2713 	foreach ( $pid_chars as $id => $char )
  2714   {
  2714 	{
  2715     if ( $pid_dirty[$id] == 'X' )
  2715 		if ( $pid_dirty[$id] == 'X' )
  2716       $page_id_cleaned .= $char;
  2716 			$page_id_cleaned .= $char;
  2717     else
  2717 		else
  2718       $page_id_cleaned .= $pid_dirty[$id];
  2718 			$page_id_cleaned .= $pid_dirty[$id];
  2719   }
  2719 	}
  2720   
  2720 	
  2721   // global $mime_types;
  2721 	// global $mime_types;
  2722 
  2722 
  2723   // $exts = array_keys($mime_types);
  2723 	// $exts = array_keys($mime_types);
  2724   // $exts = '(' . implode('|', $exts) . ')';
  2724 	// $exts = '(' . implode('|', $exts) . ')';
  2725 
  2725 
  2726   // $page_id_cleaned = preg_replace('/\.2e' . $exts . '$/', '.\\1', $page_id_cleaned);
  2726 	// $page_id_cleaned = preg_replace('/\.2e' . $exts . '$/', '.\\1', $page_id_cleaned);
  2727 
  2727 
  2728   return $page_id_cleaned;
  2728 	return $page_id_cleaned;
  2729 }
  2729 }
  2730 
  2730 
  2731 /**
  2731 /**
  2732  * Removes character escapes in a page ID string
  2732  * Removes character escapes in a page ID string
  2733  * @param string Page ID string to dirty up
  2733  * @param string Page ID string to dirty up
  2734  * @return string
  2734  * @return string
  2735  */
  2735  */
  2736 
  2736 
  2737 function dirtify_page_id($page_id)
  2737 function dirtify_page_id($page_id)
  2738 {
  2738 {
  2739   global $db, $session, $paths, $template, $plugins; // Common objects
  2739 	global $db, $session, $paths, $template, $plugins; // Common objects
  2740   // First, replace spaces with underscores
  2740 	// First, replace spaces with underscores
  2741   $page_id = str_replace(' ', '_', $page_id);
  2741 	$page_id = str_replace(' ', '_', $page_id);
  2742 
  2742 
  2743   // Exception for userpages for IP addresses
  2743 	// Exception for userpages for IP addresses
  2744   $pid_ip_check = ( is_object($paths) ) ? preg_replace('+^' . preg_quote($paths->nslist['User']) . '+', '', $page_id) : $page_id;
  2744 	$pid_ip_check = ( is_object($paths) ) ? preg_replace('+^' . preg_quote($paths->nslist['User']) . '+', '', $page_id) : $page_id;
  2745   if ( is_valid_ip($pid_ip_check) )
  2745 	if ( is_valid_ip($pid_ip_check) )
  2746   {
  2746 	{
  2747     return $page_id;
  2747 		return $page_id;
  2748   }
  2748 	}
  2749 
  2749 
  2750   preg_match_all('/\.[a-f0-9][a-f0-9]/', $page_id, $matches);
  2750 	preg_match_all('/\.[a-f0-9][a-f0-9]/', $page_id, $matches);
  2751 
  2751 
  2752   foreach ( $matches[0] as $id => $char )
  2752 	foreach ( $matches[0] as $id => $char )
  2753   {
  2753 	{
  2754     $char = substr($char, 1);
  2754 		$char = substr($char, 1);
  2755     $char = strtolower($char);
  2755 		$char = strtolower($char);
  2756     $char = intval(hexdec($char));
  2756 		$char = intval(hexdec($char));
  2757     $char = chr($char);
  2757 		$char = chr($char);
  2758     if ( preg_match('/^[\w\.\/:;\(\)@\[\]=_-]$/', $char) )
  2758 		if ( preg_match('/^[\w\.\/:;\(\)@\[\]=_-]$/', $char) )
  2759       continue;
  2759 			continue;
  2760     $page_id = str_replace($matches[0][$id], $char, $page_id);
  2760 		$page_id = str_replace($matches[0][$id], $char, $page_id);
  2761   }
  2761 	}
  2762   
  2762 	
  2763   return $page_id;
  2763 	return $page_id;
  2764 }
  2764 }
  2765 
  2765 
  2766 /**
  2766 /**
  2767  * Inserts commas into a number to make it more human-readable. Floating point-safe and doesn't flirt with the number like number_format() does.
  2767  * Inserts commas into a number to make it more human-readable. Floating point-safe and doesn't flirt with the number like number_format() does.
  2768  * @param int The number to process
  2768  * @param int The number to process
  2769  * @return string Input number with commas added
  2769  * @return string Input number with commas added
  2770  */
  2770  */
  2771 
  2771 
  2772 function commatize($num)
  2772 function commatize($num)
  2773 {
  2773 {
  2774   $num = (string)$num;
  2774 	$num = (string)$num;
  2775   if ( strpos($num, '.') )
  2775 	if ( strpos($num, '.') )
  2776   {
  2776 	{
  2777     $whole = explode('.', $num);
  2777 		$whole = explode('.', $num);
  2778     $num = $whole[0];
  2778 		$num = $whole[0];
  2779     $dec = $whole[1];
  2779 		$dec = $whole[1];
  2780   }
  2780 	}
  2781   else
  2781 	else
  2782   {
  2782 	{
  2783     $whole = $num;
  2783 		$whole = $num;
  2784   }
  2784 	}
  2785   $offset = ( strlen($num) ) % 3;
  2785 	$offset = ( strlen($num) ) % 3;
  2786   $len = strlen($num);
  2786 	$len = strlen($num);
  2787   $offset = ( $offset == 0 )
  2787 	$offset = ( $offset == 0 )
  2788     ? 3
  2788 		? 3
  2789     : $offset;
  2789 		: $offset;
  2790   for ( $i = $offset; $i < $len; $i=$i+3 )
  2790 	for ( $i = $offset; $i < $len; $i=$i+3 )
  2791   {
  2791 	{
  2792     $num = substr($num, 0, $i) . ',' . substr($num, $i, $len);
  2792 		$num = substr($num, 0, $i) . ',' . substr($num, $i, $len);
  2793     $len = strlen($num);
  2793 		$len = strlen($num);
  2794     $i++;
  2794 		$i++;
  2795   }
  2795 	}
  2796   if ( isset($dec) )
  2796 	if ( isset($dec) )
  2797   {
  2797 	{
  2798     return $num . '.' . $dec;
  2798 		return $num . '.' . $dec;
  2799   }
  2799 	}
  2800   else
  2800 	else
  2801   {
  2801 	{
  2802     return $num;
  2802 		return $num;
  2803   }
  2803 	}
  2804 }
  2804 }
  2805 
  2805 
  2806 /**
  2806 /**
  2807  * Converts a number to a human file size.
  2807  * Converts a number to a human file size.
  2808  * @param int File size
  2808  * @param int File size
  2809  * @return string
  2809  * @return string
  2810  */
  2810  */
  2811 
  2811 
  2812 function humanize_filesize($size)
  2812 function humanize_filesize($size)
  2813 {
  2813 {
  2814   global $lang;
  2814 	global $lang;
  2815   
  2815 	
  2816   if ( $size > ( 1099511627776 * 0.9 ) )
  2816 	if ( $size > ( 1099511627776 * 0.9 ) )
  2817   {
  2817 	{
  2818     return number_format($size / 1099511627776, 1) . $lang->get('etc_unit_terabytes_short');
  2818 		return number_format($size / 1099511627776, 1) . $lang->get('etc_unit_terabytes_short');
  2819   }
  2819 	}
  2820   if ( $size > ( 1073741824 * 0.9 ) )
  2820 	if ( $size > ( 1073741824 * 0.9 ) )
  2821   {
  2821 	{
  2822     return number_format($size / 1073741824, 1) . $lang->get('etc_unit_gigabytes_short');
  2822 		return number_format($size / 1073741824, 1) . $lang->get('etc_unit_gigabytes_short');
  2823   }
  2823 	}
  2824   if ( $size > ( 1048576 * 0.9 ) )
  2824 	if ( $size > ( 1048576 * 0.9 ) )
  2825   {
  2825 	{
  2826     return number_format($size / 1048576, 1) . $lang->get('etc_unit_megabytes_short');
  2826 		return number_format($size / 1048576, 1) . $lang->get('etc_unit_megabytes_short');
  2827   }
  2827 	}
  2828   if ( $size > ( 1024 * 0.9 ) )
  2828 	if ( $size > ( 1024 * 0.9 ) )
  2829   {
  2829 	{
  2830     return number_format($size / 1024, 1) . $lang->get('etc_unit_kilobytes_short');
  2830 		return number_format($size / 1024, 1) . $lang->get('etc_unit_kilobytes_short');
  2831   }
  2831 	}
  2832   return "$size " . $lang->get('etc_unit_bytes');
  2832 	return "$size " . $lang->get('etc_unit_bytes');
  2833 }
  2833 }
  2834 
  2834 
  2835 /**
  2835 /**
  2836  * Injects a string into another string at the specified position.
  2836  * Injects a string into another string at the specified position.
  2837  * @param string The haystack
  2837  * @param string The haystack
  2839  * @param int    Position at which to insert the needle
  2839  * @param int    Position at which to insert the needle
  2840  */
  2840  */
  2841 
  2841 
  2842 function inject_substr($haystack, $needle, $pos)
  2842 function inject_substr($haystack, $needle, $pos)
  2843 {
  2843 {
  2844   $str1 = substr($haystack, 0, $pos);
  2844 	$str1 = substr($haystack, 0, $pos);
  2845   $pos++;
  2845 	$pos++;
  2846   $str2 = substr($haystack, $pos);
  2846 	$str2 = substr($haystack, $pos);
  2847   return "{$str1}{$needle}{$str2}";
  2847 	return "{$str1}{$needle}{$str2}";
  2848 }
  2848 }
  2849 
  2849 
  2850 /**
  2850 /**
  2851  * Tells if a given IP address is valid.
  2851  * Tells if a given IP address is valid.
  2852  * @param string suspected IP address
  2852  * @param string suspected IP address
  2853  * @return bool true if valid, false otherwise
  2853  * @return bool true if valid, false otherwise
  2854  */
  2854  */
  2855 
  2855 
  2856 function is_valid_ip($ip)
  2856 function is_valid_ip($ip)
  2857 {
  2857 {
  2858   // This next one came from phpBB3.
  2858 	// This next one came from phpBB3.
  2859   $ipv4 = '(?:(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])';
  2859 	$ipv4 = '(?:(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])';
  2860   $ipv6 = '(?:[a-f0-9]{0,4}):(?:[a-f0-9]{0,4}):(?:[a-f0-9]{0,4}:|:)?(?:[a-f0-9]{0,4}:|:)?(?:[a-f0-9]{0,4}:|:)?(?:[a-f0-9]{0,4}:|:)?(?:[a-f0-9]{0,4}:|:)?(?:[a-f0-9]{1,4})';
  2860 	$ipv6 = '(?:[a-f0-9]{0,4}):(?:[a-f0-9]{0,4}):(?:[a-f0-9]{0,4}:|:)?(?:[a-f0-9]{0,4}:|:)?(?:[a-f0-9]{0,4}:|:)?(?:[a-f0-9]{0,4}:|:)?(?:[a-f0-9]{0,4}:|:)?(?:[a-f0-9]{1,4})';
  2861 
  2861 
  2862   if ( preg_match("/^{$ipv4}$/", $ip) || preg_match("/^{$ipv6}$/", $ip) )
  2862 	if ( preg_match("/^{$ipv4}$/", $ip) || preg_match("/^{$ipv6}$/", $ip) )
  2863     return true;
  2863 		return true;
  2864   else
  2864 	else
  2865     return false;
  2865 		return false;
  2866 }
  2866 }
  2867 
  2867 
  2868 /**
  2868 /**
  2869  * Replaces the FIRST given occurrence of needle within haystack with thread
  2869  * Replaces the FIRST given occurrence of needle within haystack with thread
  2870  * @param string Needle
  2870  * @param string Needle
  2872  * @param string Haystack
  2872  * @param string Haystack
  2873  */
  2873  */
  2874 
  2874 
  2875 function str_replace_once($needle, $thread, $haystack)
  2875 function str_replace_once($needle, $thread, $haystack)
  2876 {
  2876 {
  2877   $needle_len = strlen($needle);
  2877 	$needle_len = strlen($needle);
  2878   if ( $pos = strstr($haystack, $needle) )
  2878 	if ( $pos = strstr($haystack, $needle) )
  2879   {
  2879 	{
  2880     $upto = substr($haystack, 0, ( strlen($haystack) - strlen($pos) ));
  2880 		$upto = substr($haystack, 0, ( strlen($haystack) - strlen($pos) ));
  2881     $from = substr($pos, $needle_len);
  2881 		$from = substr($pos, $needle_len);
  2882     return "{$upto}{$thread}{$from}";
  2882 		return "{$upto}{$thread}{$from}";
  2883   }
  2883 	}
  2884   return $haystack;
  2884 	return $haystack;
  2885 }
  2885 }
  2886 
  2886 
  2887 /**
  2887 /**
  2888  * Replaces all given occurences of needle in haystack, case insensitively.
  2888  * Replaces all given occurences of needle in haystack, case insensitively.
  2889  * @param string Needle
  2889  * @param string Needle
  2892  * @return string
  2892  * @return string
  2893  */
  2893  */
  2894 
  2894 
  2895 function str_replace_i($needle, $thread, $haystack)
  2895 function str_replace_i($needle, $thread, $haystack)
  2896 {
  2896 {
  2897   $needle_len = strlen($needle);
  2897 	$needle_len = strlen($needle);
  2898   $haystack_len = strlen($haystack);
  2898 	$haystack_len = strlen($haystack);
  2899   for ( $i = 0; $i < $haystack_len; $i++ )
  2899 	for ( $i = 0; $i < $haystack_len; $i++ )
  2900   {
  2900 	{
  2901     $test = substr($haystack, $i, $needle_len);
  2901 		$test = substr($haystack, $i, $needle_len);
  2902     if ( strtolower($test) == strtolower($needle) )
  2902 		if ( strtolower($test) == strtolower($needle) )
  2903     {
  2903 		{
  2904       // Got it!
  2904 			// Got it!
  2905       $upto = substr($haystack, 0, $i);
  2905 			$upto = substr($haystack, 0, $i);
  2906       $from = substr($haystack, ( $i + $needle_len ));
  2906 			$from = substr($haystack, ( $i + $needle_len ));
  2907       $haystack = "{$upto}{$thread}{$from}";
  2907 			$haystack = "{$upto}{$thread}{$from}";
  2908       $haystack_len = strlen($haystack);
  2908 			$haystack_len = strlen($haystack);
  2909       $i = $i + strlen($thread);
  2909 			$i = $i + strlen($thread);
  2910     }
  2910 		}
  2911   }
  2911 	}
  2912   return $haystack;
  2912 	return $haystack;
  2913 }
  2913 }
  2914 
  2914 
  2915 /**
  2915 /**
  2916  * Highlights a term in a string.
  2916  * Highlights a term in a string.
  2917  * @param string Needle (term to highlight)
  2917  * @param string Needle (term to highlight)
  2921  * @return string
  2921  * @return string
  2922  */
  2922  */
  2923 
  2923 
  2924 function highlight_term($needle, $haystack, $start_tag = '<b>', $end_tag = '</b>')
  2924 function highlight_term($needle, $haystack, $start_tag = '<b>', $end_tag = '</b>')
  2925 {
  2925 {
  2926   $needle_len = strlen($needle);
  2926 	$needle_len = strlen($needle);
  2927   $haystack_len = strlen($haystack);
  2927 	$haystack_len = strlen($haystack);
  2928   for ( $i = 0; $i < $haystack_len; $i++ )
  2928 	for ( $i = 0; $i < $haystack_len; $i++ )
  2929   {
  2929 	{
  2930     $test = substr($haystack, $i, $needle_len);
  2930 		$test = substr($haystack, $i, $needle_len);
  2931     if ( strtolower($test) == strtolower($needle) )
  2931 		if ( strtolower($test) == strtolower($needle) )
  2932     {
  2932 		{
  2933       // Got it!
  2933 			// Got it!
  2934       $upto = substr($haystack, 0, $i);
  2934 			$upto = substr($haystack, 0, $i);
  2935       $from = substr($haystack, ( $i + $needle_len ));
  2935 			$from = substr($haystack, ( $i + $needle_len ));
  2936       $haystack = "{$upto}{$start_tag}{$test}{$end_tag}{$from}";
  2936 			$haystack = "{$upto}{$start_tag}{$test}{$end_tag}{$from}";
  2937       $haystack_len = strlen($haystack);
  2937 			$haystack_len = strlen($haystack);
  2938       $i = $i + strlen($needle) + strlen($start_tag) + strlen($end_tag);
  2938 			$i = $i + strlen($needle) + strlen($start_tag) + strlen($end_tag);
  2939     }
  2939 		}
  2940   }
  2940 	}
  2941   return $haystack;
  2941 	return $haystack;
  2942 }
  2942 }
  2943 
  2943 
  2944 /**
  2944 /**
  2945  * Registers a new type of search result. Because this is so tricky to do but keep clean, this function takes an associative array as its
  2945  * Registers a new type of search result. Because this is so tricky to do but keep clean, this function takes an associative array as its
  2946  * only parameter. This array configures the function. The required keys are:
  2946  * only parameter. This array configures the function. The required keys are:
  2959  *                    a fully formatted and sanitized blob of HTML. If formatcallback is a TPL string, variables will be named after table
  2959  *                    a fully formatted and sanitized blob of HTML. If formatcallback is a TPL string, variables will be named after table
  2960  *                    columns.
  2960  *                    columns.
  2961  *  - additionalwhere: additional SQL to inject into WHERE clause, in the format of "AND foo = bar"
  2961  *  - additionalwhere: additional SQL to inject into WHERE clause, in the format of "AND foo = bar"
  2962  * @example Working example of adding users to search results:
  2962  * @example Working example of adding users to search results:
  2963  <code>
  2963  <code>
  2964   register_search_handler(array(
  2964 	register_search_handler(array(
  2965       'table' => 'users',
  2965 			'table' => 'users',
  2966       'titlecolumn' => 'username',
  2966 			'titlecolumn' => 'username',
  2967       'uniqueid' => 'ns=User;cid={username}',
  2967 			'uniqueid' => 'ns=User;cid={username}',
  2968       'additionalcolumns' => array('user_id'),
  2968 			'additionalcolumns' => array('user_id'),
  2969       'resultnote' => '[Member]',
  2969 			'resultnote' => '[Member]',
  2970       'linkformat' => array(
  2970 			'linkformat' => array(
  2971           'page_id' => '{username}',
  2971 					'page_id' => '{username}',
  2972           'namespace' => 'User'
  2972 					'namespace' => 'User'
  2973         ),
  2973 				),
  2974       'formatcallback' => 'format_user_search_result',
  2974 			'formatcallback' => 'format_user_search_result',
  2975     ));
  2975 		));
  2976   
  2976 	
  2977   function format_user_search_result($row)
  2977 	function format_user_search_result($row)
  2978   {
  2978 	{
  2979     global $session, $lang;
  2979 		global $session, $lang;
  2980     $rankdata = $session->get_user_rank(intval($row['user_id']));
  2980 		$rankdata = $session->get_user_rank(intval($row['user_id']));
  2981     $rankspan = '<span style="' . $rankdata['rank_style'] . '">' . $lang->get($rankdata['rank_title']) . '</span>';
  2981 		$rankspan = '<span style="' . $rankdata['rank_style'] . '">' . $lang->get($rankdata['rank_title']) . '</span>';
  2982     if ( empty($rankdata['user_title']) )
  2982 		if ( empty($rankdata['user_title']) )
  2983     {
  2983 		{
  2984       return $rankspan;
  2984 			return $rankspan;
  2985     }
  2985 		}
  2986     else
  2986 		else
  2987     {
  2987 		{
  2988       return '"' . htmlspecialchars($rankdata['user_title']) . "\" (<b>$rankspan</b>)";
  2988 			return '"' . htmlspecialchars($rankdata['user_title']) . "\" (<b>$rankspan</b>)";
  2989     }
  2989 		}
  2990   }
  2990 	}
  2991  </code>
  2991  </code>
  2992  * @param array Options array - see function documentation
  2992  * @param array Options array - see function documentation
  2993  * @return null
  2993  * @return null
  2994  */
  2994  */
  2995 
  2995 
  2996 global $search_handlers;
  2996 global $search_handlers;
  2997 $search_handlers = array();
  2997 $search_handlers = array();
  2998 
  2998 
  2999 function register_search_handler($options)
  2999 function register_search_handler($options)
  3000 {
  3000 {
  3001   global $search_handlers;
  3001 	global $search_handlers;
  3002   
  3002 	
  3003   $required = array('table', 'titlecolumn', 'uniqueid', 'linkformat');
  3003 	$required = array('table', 'titlecolumn', 'uniqueid', 'linkformat');
  3004   foreach ( $required as $key )
  3004 	foreach ( $required as $key )
  3005   {
  3005 	{
  3006     if ( !isset($options[$key]) )
  3006 		if ( !isset($options[$key]) )
  3007     {
  3007 		{
  3008       throw new Exception("Required search handler option '$key' is missing");
  3008 			throw new Exception("Required search handler option '$key' is missing");
  3009     }
  3009 		}
  3010   }
  3010 	}
  3011   $search_handlers[] = $options;
  3011 	$search_handlers[] = $options;
  3012   return null;
  3012 	return null;
  3013 }
  3013 }
  3014 
  3014 
  3015 /**
  3015 /**
  3016  * From http://us2.php.net/urldecode - decode %uXXXX
  3016  * From http://us2.php.net/urldecode - decode %uXXXX
  3017  * @param string The urlencoded string
  3017  * @param string The urlencoded string
  3018  * @return string
  3018  * @return string
  3019  */
  3019  */
  3020 
  3020 
  3021 function decode_unicode_url($str)
  3021 function decode_unicode_url($str)
  3022 {
  3022 {
  3023   $res = '';
  3023 	$res = '';
  3024 
  3024 
  3025   $i = 0;
  3025 	$i = 0;
  3026   $max = strlen($str) - 6;
  3026 	$max = strlen($str) - 6;
  3027   while ($i <= $max)
  3027 	while ($i <= $max)
  3028   {
  3028 	{
  3029     $character = $str[$i];
  3029 		$character = $str[$i];
  3030     if ($character == '%' && $str[$i + 1] == 'u')
  3030 		if ($character == '%' && $str[$i + 1] == 'u')
  3031     {
  3031 		{
  3032       if ( !preg_match('/^([a-f0-9]{2})+$/', substr($str, $i + 2, 4)) )
  3032 			if ( !preg_match('/^([a-f0-9]{2})+$/', substr($str, $i + 2, 4)) )
  3033       {
  3033 			{
  3034         $res .= substr($str, $i, 6);
  3034 				$res .= substr($str, $i, 6);
  3035         $i += 6;
  3035 				$i += 6;
  3036         continue;
  3036 				continue;
  3037       }
  3037 			}
  3038       
  3038 			
  3039       $value = hexdec(substr($str, $i + 2, 4));
  3039 			$value = hexdec(substr($str, $i + 2, 4));
  3040       $i += 6;
  3040 			$i += 6;
  3041 
  3041 
  3042       if ($value < 0x0080)
  3042 			if ($value < 0x0080)
  3043       {
  3043 			{
  3044         // 1 byte: 0xxxxxxx
  3044 				// 1 byte: 0xxxxxxx
  3045         $character = chr($value);
  3045 				$character = chr($value);
  3046       }
  3046 			}
  3047       else if ($value < 0x0800)
  3047 			else if ($value < 0x0800)
  3048       {
  3048 			{
  3049         // 2 bytes: 110xxxxx 10xxxxxx
  3049 				// 2 bytes: 110xxxxx 10xxxxxx
  3050         $character =
  3050 				$character =
  3051             chr((($value & 0x07c0) >> 6) | 0xc0)
  3051 						chr((($value & 0x07c0) >> 6) | 0xc0)
  3052           . chr(($value & 0x3f) | 0x80);
  3052 					. chr(($value & 0x3f) | 0x80);
  3053       }
  3053 			}
  3054       else
  3054 			else
  3055       {
  3055 			{
  3056         // 3 bytes: 1110xxxx 10xxxxxx 10xxxxxx
  3056 				// 3 bytes: 1110xxxx 10xxxxxx 10xxxxxx
  3057         $character =
  3057 				$character =
  3058             chr((($value & 0xf000) >> 12) | 0xe0)
  3058 						chr((($value & 0xf000) >> 12) | 0xe0)
  3059           . chr((($value & 0x0fc0) >> 6) | 0x80)
  3059 					. chr((($value & 0x0fc0) >> 6) | 0x80)
  3060           . chr(($value & 0x3f) | 0x80);
  3060 					. chr(($value & 0x3f) | 0x80);
  3061       }
  3061 			}
  3062     }
  3062 		}
  3063     else
  3063 		else
  3064     {
  3064 		{
  3065       $i++;
  3065 			$i++;
  3066     }
  3066 		}
  3067 
  3067 
  3068     $res .= $character;
  3068 		$res .= $character;
  3069   }
  3069 	}
  3070 
  3070 
  3071   return $res . substr($str, $i);
  3071 	return $res . substr($str, $i);
  3072 }
  3072 }
  3073 
  3073 
  3074 /**
  3074 /**
  3075  * Recursively decodes an array with UTF-8 characters in its strings
  3075  * Recursively decodes an array with UTF-8 characters in its strings
  3076  * @param array Can be multi-depth
  3076  * @param array Can be multi-depth
  3077  * @return array
  3077  * @return array
  3078  */
  3078  */
  3079 
  3079 
  3080 function decode_unicode_array($array)
  3080 function decode_unicode_array($array)
  3081 {
  3081 {
  3082   foreach ( $array as $i => $val )
  3082 	foreach ( $array as $i => $val )
  3083   {
  3083 	{
  3084     if ( is_string($val) )
  3084 		if ( is_string($val) )
  3085     {
  3085 		{
  3086       $array[$i] = decode_unicode_url($val);
  3086 			$array[$i] = decode_unicode_url($val);
  3087     }
  3087 		}
  3088     else if ( is_array($val) )
  3088 		else if ( is_array($val) )
  3089     {
  3089 		{
  3090       $array[$i] = decode_unicode_array($val);
  3090 			$array[$i] = decode_unicode_array($val);
  3091     }
  3091 		}
  3092   }
  3092 	}
  3093   return $array;
  3093 	return $array;
  3094 }
  3094 }
  3095 
  3095 
  3096 /**
  3096 /**
  3097  * Sanitizes a page tag.
  3097  * Sanitizes a page tag.
  3098  * @param string
  3098  * @param string
  3099  * @return string
  3099  * @return string
  3100  */
  3100  */
  3101 
  3101 
  3102 function sanitize_tag($tag)
  3102 function sanitize_tag($tag)
  3103 {
  3103 {
  3104   $tag = strtolower($tag);
  3104 	$tag = strtolower($tag);
  3105   $tag = preg_replace('/[^\w @\$%\^&-]+/', '', $tag);
  3105 	$tag = preg_replace('/[^\w @\$%\^&-]+/', '', $tag);
  3106   $tag = str_replace('_', ' ', $tag);
  3106 	$tag = str_replace('_', ' ', $tag);
  3107   $tag = trim($tag);
  3107 	$tag = trim($tag);
  3108   return $tag;
  3108 	return $tag;
  3109 }
  3109 }
  3110 
  3110 
  3111 /**
  3111 /**
  3112  * Replacement for gzencode() which doesn't always work.
  3112  * Replacement for gzencode() which doesn't always work.
  3113  * @param string Data to compress
  3113  * @param string Data to compress
  3117  * @return string Compressed data
  3117  * @return string Compressed data
  3118  */
  3118  */
  3119 
  3119 
  3120 function enano_gzencode($data = "", $level = 6, $filename = "", $comments = "")
  3120 function enano_gzencode($data = "", $level = 6, $filename = "", $comments = "")
  3121 {
  3121 {
  3122   $flags = (empty($comment)? 0 : 16) + (empty($filename)? 0 : 8);
  3122 	$flags = (empty($comment)? 0 : 16) + (empty($filename)? 0 : 8);
  3123   $mtime = time();
  3123 	$mtime = time();
  3124   
  3124 	
  3125   if ( !function_exists('gzdeflate') )
  3125 	if ( !function_exists('gzdeflate') )
  3126     return false;
  3126 		return false;
  3127  
  3127  
  3128   return (pack("C1C1C1C1VC1C1", 0x1f, 0x8b, 8, $flags, $mtime, 2, 0xFF) .
  3128 	return (pack("C1C1C1C1VC1C1", 0x1f, 0x8b, 8, $flags, $mtime, 2, 0xFF) .
  3129           (empty($filename) ? "" : $filename . "\0") .
  3129 					(empty($filename) ? "" : $filename . "\0") .
  3130           (empty($comment) ? "" : $comment . "\0") .
  3130 					(empty($comment) ? "" : $comment . "\0") .
  3131           gzdeflate($data, $level) .
  3131 					gzdeflate($data, $level) .
  3132           pack("VV", crc32($data), strlen($data)));
  3132 					pack("VV", crc32($data), strlen($data)));
  3133 }
  3133 }
  3134 
  3134 
  3135 $php_errors = array();
  3135 $php_errors = array();
  3136 
  3136 
  3137 /**
  3137 /**
  3140  * @access private
  3140  * @access private
  3141  */
  3141  */
  3142 
  3142 
  3143 function enano_handle_error($errno, $errstr, $errfile, $errline)
  3143 function enano_handle_error($errno, $errstr, $errfile, $errline)
  3144 {
  3144 {
  3145   global $db, $session, $paths, $template, $plugins; // Common objects
  3145 	global $db, $session, $paths, $template, $plugins; // Common objects
  3146   
  3146 	
  3147   $er = error_reporting();
  3147 	$er = error_reporting();
  3148   if ( ! $er & $errno || $er == 0 )
  3148 	if ( ! $er & $errno || $er == 0 )
  3149   {
  3149 	{
  3150     return true;
  3150 		return true;
  3151   }
  3151 	}
  3152   global $do_gzip, $php_errors;
  3152 	global $do_gzip, $php_errors;
  3153   
  3153 	
  3154   if ( defined('ENANO_DEBUG') )
  3154 	if ( defined('ENANO_DEBUG') )
  3155   {
  3155 	{
  3156     // turn off gzip and echo out error immediately for debug installs
  3156 		// turn off gzip and echo out error immediately for debug installs
  3157     $do_gzip = false;
  3157 		$do_gzip = false;
  3158   }
  3158 	}
  3159   
  3159 	
  3160   $error_type = 'error';
  3160 	$error_type = 'error';
  3161   if ( in_array($errno, array(E_WARNING, E_USER_WARNING)) )
  3161 	if ( in_array($errno, array(E_WARNING, E_USER_WARNING)) )
  3162     $error_type = 'warning';
  3162 		$error_type = 'warning';
  3163   else if ( in_array($errno, array(E_NOTICE, E_USER_NOTICE)) )
  3163 	else if ( in_array($errno, array(E_NOTICE, E_USER_NOTICE)) )
  3164     $error_type = 'notice';
  3164 		$error_type = 'notice';
  3165   
  3165 	
  3166   if ( @is_object(@$plugins) )
  3166 	if ( @is_object(@$plugins) )
  3167   {
  3167 	{
  3168     $code = $plugins->setHook('php_error');
  3168 		$code = $plugins->setHook('php_error');
  3169     foreach ( $code as $cmd )
  3169 		foreach ( $code as $cmd )
  3170     {
  3170 		{
  3171       eval($cmd);
  3171 			eval($cmd);
  3172     }
  3172 		}
  3173   }
  3173 	}
  3174   
  3174 	
  3175   // bypass errors in date() and mktime() (Enano has its own code for this anyway)
  3175 	// bypass errors in date() and mktime() (Enano has its own code for this anyway)
  3176   if ( strstr($errstr, "It is not safe to rely on the system's timezone settings. Please use the date.timezone setting, the TZ environment variable or the date_default_timezone_set() function. In case you used any of those methods and you are still getting this warning, you most likely misspelled the timezone identifier.") )
  3176 	if ( strstr($errstr, "It is not safe to rely on the system's timezone settings. Please use the date.timezone setting, the TZ environment variable or the date_default_timezone_set() function. In case you used any of those methods and you are still getting this warning, you most likely misspelled the timezone identifier.") )
  3177   {
  3177 	{
  3178     return true;
  3178 		return true;
  3179   }
  3179 	}
  3180   
  3180 	
  3181   if ( $do_gzip )
  3181 	if ( $do_gzip )
  3182   {
  3182 	{
  3183     $php_errors[] = array(
  3183 		$php_errors[] = array(
  3184         'num' => $errno,
  3184 				'num' => $errno,
  3185         'type' => $error_type,
  3185 				'type' => $error_type,
  3186         'error' => $errstr,
  3186 				'error' => $errstr,
  3187         'file' => $errfile,
  3187 				'file' => $errfile,
  3188         'line' => $errline
  3188 				'line' => $errline
  3189       );
  3189 			);
  3190   }
  3190 	}
  3191   else
  3191 	else
  3192   {
  3192 	{
  3193     echo "[ <b>PHP $error_type:</b> $errstr in <b>$errfile</b>:<b>$errline</b> ]<br />";
  3193 		echo "[ <b>PHP $error_type:</b> $errstr in <b>$errfile</b>:<b>$errline</b> ]<br />";
  3194   }
  3194 	}
  3195 }
  3195 }
  3196 
  3196 
  3197 set_error_handler('enano_handle_error');
  3197 set_error_handler('enano_handle_error');
  3198 
  3198 
  3199 /**
  3199 /**
  3200  * Gzips the output buffer.
  3200  * Gzips the output buffer.
  3201  */
  3201  */
  3202 
  3202 
  3203 function gzip_output()
  3203 function gzip_output()
  3204 {
  3204 {
  3205   global $do_gzip;
  3205 	global $do_gzip;
  3206   
  3206 	
  3207   $gzip_supported = false;
  3207 	$gzip_supported = false;
  3208   if ( isset($_SERVER['HTTP_ACCEPT_ENCODING']) )
  3208 	if ( isset($_SERVER['HTTP_ACCEPT_ENCODING']) )
  3209   {
  3209 	{
  3210     $encodings = explode(',', $_SERVER['HTTP_ACCEPT_ENCODING']);
  3210 		$encodings = explode(',', $_SERVER['HTTP_ACCEPT_ENCODING']);
  3211     $gzip_supported = in_array('gzip', $encodings) || in_array('deflate', $encodings);
  3211 		$gzip_supported = in_array('gzip', $encodings) || in_array('deflate', $encodings);
  3212   }
  3212 	}
  3213   
  3213 	
  3214   //
  3214 	//
  3215   // Compress buffered output if required and send to browser
  3215 	// Compress buffered output if required and send to browser
  3216   // Sorry, doesn't work in IE. What else is new?
  3216 	// Sorry, doesn't work in IE. What else is new?
  3217   //
  3217 	//
  3218   if ( $do_gzip && getConfig('gzip_output', false) == 1 && function_exists('gzdeflate') && !strstr($_SERVER['HTTP_USER_AGENT'], 'MSIE') && !headers_sent() && $gzip_supported )
  3218 	if ( $do_gzip && getConfig('gzip_output', false) == 1 && function_exists('gzdeflate') && !strstr($_SERVER['HTTP_USER_AGENT'], 'MSIE') && !headers_sent() && $gzip_supported )
  3219   {
  3219 	{
  3220     $gzip_contents = ob_get_contents();
  3220 		$gzip_contents = ob_get_contents();
  3221     ob_end_clean();
  3221 		ob_end_clean();
  3222     
  3222 		
  3223     global $php_errors;
  3223 		global $php_errors;
  3224     if ( !empty($php_errors) )
  3224 		if ( !empty($php_errors) )
  3225     {
  3225 		{
  3226       $errors = '';
  3226 			$errors = '';
  3227       foreach ( $php_errors as $error )
  3227 			foreach ( $php_errors as $error )
  3228       {
  3228 			{
  3229         $errors .= "[ <b>PHP {$error['type']}:</b> {$error['error']} in <b>{$error['file']}</b>:<b>{$error['line']}</b> ]<br />";
  3229 				$errors .= "[ <b>PHP {$error['type']}:</b> {$error['error']} in <b>{$error['file']}</b>:<b>{$error['line']}</b> ]<br />";
  3230       }
  3230 			}
  3231       $gzip_contents = str_replace("</body>", "$errors</body>", $gzip_contents);
  3231 			$gzip_contents = str_replace("</body>", "$errors</body>", $gzip_contents);
  3232     }
  3232 		}
  3233     
  3233 		
  3234     $return = @enano_gzencode($gzip_contents);
  3234 		$return = @enano_gzencode($gzip_contents);
  3235     if ( $return )
  3235 		if ( $return )
  3236     {
  3236 		{
  3237       header('Content-encoding: gzip');
  3237 			header('Content-encoding: gzip');
  3238       echo $return;
  3238 			echo $return;
  3239     }
  3239 		}
  3240     else
  3240 		else
  3241     {
  3241 		{
  3242       echo $gzip_contents;
  3242 			echo $gzip_contents;
  3243     }
  3243 		}
  3244   }
  3244 	}
  3245 }
  3245 }
  3246 
  3246 
  3247 /**
  3247 /**
  3248  * Aggressively and hopefully non-destructively optimizes a blob of HTML.
  3248  * Aggressively and hopefully non-destructively optimizes a blob of HTML.
  3249  * @param string HTML to process
  3249  * @param string HTML to process
  3250  * @return string much smaller HTML
  3250  * @return string much smaller HTML
  3251  */
  3251  */
  3252 
  3252 
  3253 function aggressive_optimize_html($html)
  3253 function aggressive_optimize_html($html)
  3254 {
  3254 {
  3255   $size_before = strlen($html);
  3255 	$size_before = strlen($html);
  3256   
  3256 	
  3257   // kill carriage returns
  3257 	// kill carriage returns
  3258   $html = str_replace("\r", "", $html);
  3258 	$html = str_replace("\r", "", $html);
  3259   
  3259 	
  3260   // Which tags to strip for JAVASCRIPT PROCESSING ONLY - you can change this if needed
  3260 	// Which tags to strip for JAVASCRIPT PROCESSING ONLY - you can change this if needed
  3261   $strip_tags = Array('enano:no-opt');
  3261 	$strip_tags = Array('enano:no-opt');
  3262   $strip_tags = implode('|', $strip_tags);
  3262 	$strip_tags = implode('|', $strip_tags);
  3263   
  3263 	
  3264   // Strip out the tags and replace with placeholders
  3264 	// Strip out the tags and replace with placeholders
  3265   preg_match_all("#<($strip_tags)([ ]+.*?)?>(.*?)</($strip_tags)>#is", $html, $matches);
  3265 	preg_match_all("#<($strip_tags)([ ]+.*?)?>(.*?)</($strip_tags)>#is", $html, $matches);
  3266   $seed = md5(microtime() . mt_rand()); // Random value used for placeholders
  3266 	$seed = md5(microtime() . mt_rand()); // Random value used for placeholders
  3267   for ($i = 0;$i < sizeof($matches[1]); $i++)
  3267 	for ($i = 0;$i < sizeof($matches[1]); $i++)
  3268   {
  3268 	{
  3269     $html = str_replace($matches[0][$i], "{DONT_STRIP_ME_NAKED:$seed:$i}", $html);
  3269 		$html = str_replace($matches[0][$i], "{DONT_STRIP_ME_NAKED:$seed:$i}", $html);
  3270   }
  3270 	}
  3271   
  3271 	
  3272   // Optimize (but don't obfuscate) Javascript
  3272 	// Optimize (but don't obfuscate) Javascript
  3273   preg_match_all('/<script([ ]+.*?)?>(.*?)(\]\]>)?<\/script>/is', $html, $jscript);
  3273 	preg_match_all('/<script([ ]+.*?)?>(.*?)(\]\]>)?<\/script>/is', $html, $jscript);
  3274   require_once(ENANO_ROOT . '/includes/js-compressor.php');
  3274 	require_once(ENANO_ROOT . '/includes/js-compressor.php');
  3275   $jsc = new JavascriptCompressor();
  3275 	$jsc = new JavascriptCompressor();
  3276   
  3276 	
  3277   // list of Javascript reserved words - from about.com
  3277 	// list of Javascript reserved words - from about.com
  3278   $reserved_words = array('abstract', 'as', 'boolean', 'break', 'byte', 'case', 'catch', 'char', 'class', 'continue', 'const', 'debugger', 'default', 'delete', 'do',
  3278 	$reserved_words = array('abstract', 'as', 'boolean', 'break', 'byte', 'case', 'catch', 'char', 'class', 'continue', 'const', 'debugger', 'default', 'delete', 'do',
  3279                           'double', 'else', 'enum', 'export', 'extends', 'false', 'final', 'finally', 'float', 'for', 'function', 'goto', 'if', 'implements', 'import',
  3279 													'double', 'else', 'enum', 'export', 'extends', 'false', 'final', 'finally', 'float', 'for', 'function', 'goto', 'if', 'implements', 'import',
  3280                           'in', 'instanceof', 'int', 'interface', 'is', 'long', 'namespace', 'native', 'new', 'null', 'package', 'private', 'protected', 'public',
  3280 													'in', 'instanceof', 'int', 'interface', 'is', 'long', 'namespace', 'native', 'new', 'null', 'package', 'private', 'protected', 'public',
  3281                           'return', 'short', 'static', 'super', 'switch', 'synchronized', 'this', 'throw', 'throws', 'transient', 'true', 'try', 'typeof', 'use', 'var',
  3281 													'return', 'short', 'static', 'super', 'switch', 'synchronized', 'this', 'throw', 'throws', 'transient', 'true', 'try', 'typeof', 'use', 'var',
  3282                           'void', 'volatile', 'while', 'with');
  3282 													'void', 'volatile', 'while', 'with');
  3283   
  3283 	
  3284   $reserved_words = '(' . implode('|', $reserved_words) . ')';
  3284 	$reserved_words = '(' . implode('|', $reserved_words) . ')';
  3285   
  3285 	
  3286   for ( $i = 0; $i < count($jscript[0]); $i++ )
  3286 	for ( $i = 0; $i < count($jscript[0]); $i++ )
  3287   {
  3287 	{
  3288     $js =& $jscript[2][$i];
  3288 		$js =& $jscript[2][$i];
  3289     if ( empty($js) )
  3289 		if ( empty($js) )
  3290       continue;
  3290 			continue;
  3291     
  3291 		
  3292     $js = $jsc->getClean($js);
  3292 		$js = $jsc->getClean($js);
  3293     
  3293 		
  3294     $replacement = "<script{$jscript[1][$i]}>/* <![CDATA[ */ $js /* ]]> */</script>";
  3294 		$replacement = "<script{$jscript[1][$i]}>/* <![CDATA[ */ $js /* ]]> */</script>";
  3295     // apply changes
  3295 		// apply changes
  3296     $html = str_replace($jscript[0][$i], $replacement, $html);
  3296 		$html = str_replace($jscript[0][$i], $replacement, $html);
  3297      
  3297  		
  3298   }
  3298 	}
  3299   
  3299 	
  3300   // Re-insert untouchable tags
  3300 	// Re-insert untouchable tags
  3301   for ($i = 0;$i < sizeof($matches[1]); $i++)
  3301 	for ($i = 0;$i < sizeof($matches[1]); $i++)
  3302   {
  3302 	{
  3303     $html = str_replace("{DONT_STRIP_ME_NAKED:$seed:$i}", "<{$matches[1][$i]}{$matches[2][$i]}>{$matches[3][$i]}</{$matches[4][$i]}>", $html);
  3303 		$html = str_replace("{DONT_STRIP_ME_NAKED:$seed:$i}", "<{$matches[1][$i]}{$matches[2][$i]}>{$matches[3][$i]}</{$matches[4][$i]}>", $html);
  3304   }
  3304 	}
  3305   
  3305 	
  3306   // Which tags to strip - you can change this if needed
  3306 	// Which tags to strip - you can change this if needed
  3307   $strip_tags = Array('pre', 'script', 'style', 'enano:no-opt', 'textarea');
  3307 	$strip_tags = Array('pre', 'script', 'style', 'enano:no-opt', 'textarea');
  3308   $strip_tags = implode('|', $strip_tags);
  3308 	$strip_tags = implode('|', $strip_tags);
  3309   
  3309 	
  3310   // Strip out the tags and replace with placeholders
  3310 	// Strip out the tags and replace with placeholders
  3311   preg_match_all("#<($strip_tags)(.*?)>(.*?)</($strip_tags)>#is", $html, $matches);
  3311 	preg_match_all("#<($strip_tags)(.*?)>(.*?)</($strip_tags)>#is", $html, $matches);
  3312   $seed = md5(microtime() . mt_rand()); // Random value used for placeholders
  3312 	$seed = md5(microtime() . mt_rand()); // Random value used for placeholders
  3313   for ($i = 0;$i < sizeof($matches[1]); $i++)
  3313 	for ($i = 0;$i < sizeof($matches[1]); $i++)
  3314   {
  3314 	{
  3315     $html = str_replace($matches[0][$i], "{DONT_STRIP_ME_NAKED:$seed:$i}", $html);
  3315 		$html = str_replace($matches[0][$i], "{DONT_STRIP_ME_NAKED:$seed:$i}", $html);
  3316   }
  3316 	}
  3317   
  3317 	
  3318   // Finally, process the HTML
  3318 	// Finally, process the HTML
  3319   $html = preg_replace("#\n([ ]*)#", " ", $html);
  3319 	$html = preg_replace("#\n([ ]*)#", " ", $html);
  3320   
  3320 	
  3321   // Remove annoying spaces between tags
  3321 	// Remove annoying spaces between tags
  3322   $html = preg_replace("#>([ ][ ]+)<#", "> <", $html);
  3322 	$html = preg_replace("#>([ ][ ]+)<#", "> <", $html);
  3323   
  3323 	
  3324   // Re-insert untouchable tags
  3324 	// Re-insert untouchable tags
  3325   for ($i = 0;$i < sizeof($matches[1]); $i++)
  3325 	for ($i = 0;$i < sizeof($matches[1]); $i++)
  3326   {
  3326 	{
  3327     $html = str_replace("{DONT_STRIP_ME_NAKED:$seed:$i}", "<{$matches[1][$i]}{$matches[2][$i]}>{$matches[3][$i]}</{$matches[4][$i]}>", $html);
  3327 		$html = str_replace("{DONT_STRIP_ME_NAKED:$seed:$i}", "<{$matches[1][$i]}{$matches[2][$i]}>{$matches[3][$i]}</{$matches[4][$i]}>", $html);
  3328   }
  3328 	}
  3329   
  3329 	
  3330   // Remove <enano:no-opt> blocks (can be used by themes that don't want their HTML optimized)
  3330 	// Remove <enano:no-opt> blocks (can be used by themes that don't want their HTML optimized)
  3331   $html = preg_replace('#<(\/|)enano:no-opt(.*?)>#', '', $html);
  3331 	$html = preg_replace('#<(\/|)enano:no-opt(.*?)>#', '', $html);
  3332   
  3332 	
  3333   $size_after = strlen($html);
  3333 	$size_after = strlen($html);
  3334   
  3334 	
  3335   // Tell snoopish users what's going on
  3335 	// Tell snoopish users what's going on
  3336   $html = str_replace('<html', "\n".'<!-- NOTE: Enano has performed an HTML optimization routine on the HTML you see here. This is to enhance page loading speeds.
  3336 	$html = str_replace('<html', "\n".'<!-- NOTE: Enano has performed an HTML optimization routine on the HTML you see here. This is to enhance page loading speeds.
  3337      To view the uncompressed source of this page, add the "nocompress" parameter to the URI of this page: index.php?title=Main_Page&nocompress or Main_Page?nocompress'."
  3337  		To view the uncompressed source of this page, add the "nocompress" parameter to the URI of this page: index.php?title=Main_Page&nocompress or Main_Page?nocompress'."
  3338      Size before compression: $size_before bytes
  3338  		Size before compression: $size_before bytes
  3339      Size after compression:  $size_after bytes
  3339  		Size after compression:  $size_after bytes
  3340      -->\n<html", $html);
  3340  		-->\n<html", $html);
  3341   return $html;
  3341 	return $html;
  3342 }
  3342 }
  3343 
  3343 
  3344 /**
  3344 /**
  3345  * For an input range of numbers (like 25-256) returns an array filled with all numbers in the range, inclusive.
  3345  * For an input range of numbers (like 25-256) returns an array filled with all numbers in the range, inclusive.
  3346  * @param string
  3346  * @param string
  3347  * @return array
  3347  * @return array
  3348  */
  3348  */
  3349 
  3349 
  3350 function int_range($range)
  3350 function int_range($range)
  3351 {
  3351 {
  3352   if ( strval(intval($range)) == $range )
  3352 	if ( strval(intval($range)) == $range )
  3353     return $range;
  3353 		return $range;
  3354   if ( !preg_match('/^[0-9]+(-[0-9]+)?$/', $range) )
  3354 	if ( !preg_match('/^[0-9]+(-[0-9]+)?$/', $range) )
  3355     return false;
  3355 		return false;
  3356   $ends = explode('-', $range);
  3356 	$ends = explode('-', $range);
  3357   if ( count($ends) != 2 )
  3357 	if ( count($ends) != 2 )
  3358     return $range;
  3358 		return $range;
  3359   $ret = array();
  3359 	$ret = array();
  3360   if ( $ends[1] < $ends[0] )
  3360 	if ( $ends[1] < $ends[0] )
  3361     $ends = array($ends[1], $ends[0]);
  3361 		$ends = array($ends[1], $ends[0]);
  3362   else if ( $ends[0] == $ends[1] )
  3362 	else if ( $ends[0] == $ends[1] )
  3363     return array($ends[0]);
  3363 		return array($ends[0]);
  3364   for ( $i = $ends[0]; $i <= $ends[1]; $i++ )
  3364 	for ( $i = $ends[0]; $i <= $ends[1]; $i++ )
  3365   {
  3365 	{
  3366     $ret[] = $i;
  3366 		$ret[] = $i;
  3367   }
  3367 	}
  3368   return $ret;
  3368 	return $ret;
  3369 }
  3369 }
  3370 
  3370 
  3371 /**
  3371 /**
  3372  * Parses a range or series of IP addresses, and returns the raw addresses. Only parses ranges in the last two octets to prevent DOSing.
  3372  * Parses a range or series of IP addresses, and returns the raw addresses. Only parses ranges in the last two octets to prevent DOSing.
  3373  * Syntax for ranges: x.x.x.x; x|y.x.x.x; x.x.x-z.x; x.x.x-z|p.q|y
  3373  * Syntax for ranges: x.x.x.x; x|y.x.x.x; x.x.x-z.x; x.x.x-z|p.q|y
  3375  * @return array
  3375  * @return array
  3376  */
  3376  */
  3377 
  3377 
  3378 function parse_ip_range($range)
  3378 function parse_ip_range($range)
  3379 {
  3379 {
  3380   $octets = explode('.', $range);
  3380 	$octets = explode('.', $range);
  3381   if ( count($octets) != 4 )
  3381 	if ( count($octets) != 4 )
  3382     // invalid range
  3382 		// invalid range
  3383     return $range;
  3383 		return $range;
  3384   $i = 0;
  3384 	$i = 0;
  3385   $possibilities = array( 0 => array(), 1 => array(), 2 => array(), 3 => array() );
  3385 	$possibilities = array( 0 => array(), 1 => array(), 2 => array(), 3 => array() );
  3386   foreach ( $octets as $octet )
  3386 	foreach ( $octets as $octet )
  3387   {
  3387 	{
  3388     $existing =& $possibilities[$i];
  3388 		$existing =& $possibilities[$i];
  3389     $inner = explode('|', $octet);
  3389 		$inner = explode('|', $octet);
  3390     foreach ( $inner as $bit )
  3390 		foreach ( $inner as $bit )
  3391     {
  3391 		{
  3392       if ( $i >= 2 )
  3392 			if ( $i >= 2 )
  3393       {
  3393 			{
  3394         $bits = int_range($bit);
  3394 				$bits = int_range($bit);
  3395         if ( $bits === false )
  3395 				if ( $bits === false )
  3396           return false;
  3396 					return false;
  3397         else if ( !is_array($bits) )
  3397 				else if ( !is_array($bits) )
  3398           $existing[] = intval($bits);
  3398 					$existing[] = intval($bits);
  3399         else
  3399 				else
  3400           $existing = array_merge($existing, $bits);
  3400 					$existing = array_merge($existing, $bits);
  3401       }
  3401 			}
  3402       else
  3402 			else
  3403       {
  3403 			{
  3404         $bit = intval($bit);
  3404 				$bit = intval($bit);
  3405         $existing[] = $bit;
  3405 				$existing[] = $bit;
  3406       }
  3406 			}
  3407     }
  3407 		}
  3408     $existing = array_unique($existing);
  3408 		$existing = array_unique($existing);
  3409     $i++;
  3409 		$i++;
  3410   }
  3410 	}
  3411   $ips = array();
  3411 	$ips = array();
  3412   
  3412 	
  3413   // The only way to combine all those possibilities. ;-)
  3413 	// The only way to combine all those possibilities. ;-)
  3414   foreach ( $possibilities[0] as $oc1 )
  3414 	foreach ( $possibilities[0] as $oc1 )
  3415     foreach ( $possibilities[1] as $oc2 )
  3415 		foreach ( $possibilities[1] as $oc2 )
  3416       foreach ( $possibilities[2] as $oc3 )
  3416 			foreach ( $possibilities[2] as $oc3 )
  3417         foreach ( $possibilities[3] as $oc4 )
  3417 				foreach ( $possibilities[3] as $oc4 )
  3418           $ips[] = "$oc1.$oc2.$oc3.$oc4";
  3418 					$ips[] = "$oc1.$oc2.$oc3.$oc4";
  3419         
  3419 				
  3420   return $ips;
  3420 	return $ips;
  3421 }
  3421 }
  3422 
  3422 
  3423 /**
  3423 /**
  3424  * Parses a valid IP address range into a regular expression.
  3424  * Parses a valid IP address range into a regular expression.
  3425  * @param string IP range string
  3425  * @param string IP range string
  3426  * @return string
  3426  * @return string
  3427  */
  3427  */
  3428 
  3428 
  3429 function parse_ip_range_regex($range)
  3429 function parse_ip_range_regex($range)
  3430 {
  3430 {
  3431   if ( strstr($range, ':') )
  3431 	if ( strstr($range, ':') )
  3432   {
  3432 	{
  3433     return parse_ipv6_range_regex($range);
  3433 		return parse_ipv6_range_regex($range);
  3434   }
  3434 	}
  3435   else
  3435 	else
  3436   {
  3436 	{
  3437     return parse_ipv4_range_regex($range);
  3437 		return parse_ipv4_range_regex($range);
  3438   }
  3438 	}
  3439 }
  3439 }
  3440 
  3440 
  3441 /**
  3441 /**
  3442  * Parses a valid IPv4 address range into a regular expression.
  3442  * Parses a valid IPv4 address range into a regular expression.
  3443  * @param string IP range string
  3443  * @param string IP range string
  3444  * @return string
  3444  * @return string
  3445  */
  3445  */
  3446 
  3446 
  3447 function parse_ipv4_range_regex($range)
  3447 function parse_ipv4_range_regex($range)
  3448 {
  3448 {
  3449   // Regular expression to test the range string for validity
  3449 	// Regular expression to test the range string for validity
  3450   $regex = '/^(([0-9]+(-[0-9]+)?)(\|([0-9]+(-[0-9]+)?))*)\.'
  3450 	$regex = '/^(([0-9]+(-[0-9]+)?)(\|([0-9]+(-[0-9]+)?))*)\.'
  3451            . '(([0-9]+(-[0-9]+)?)(\|([0-9]+(-[0-9]+)?))*)\.'
  3451  					. '(([0-9]+(-[0-9]+)?)(\|([0-9]+(-[0-9]+)?))*)\.'
  3452            . '(([0-9]+(-[0-9]+)?)(\|([0-9]+(-[0-9]+)?))*)\.'
  3452  					. '(([0-9]+(-[0-9]+)?)(\|([0-9]+(-[0-9]+)?))*)\.'
  3453            . '(([0-9]+(-[0-9]+)?)(\|([0-9]+(-[0-9]+)?))*)$/';
  3453  					. '(([0-9]+(-[0-9]+)?)(\|([0-9]+(-[0-9]+)?))*)$/';
  3454   if ( !preg_match($regex, $range) )
  3454 	if ( !preg_match($regex, $range) )
  3455   {
  3455 	{
  3456     return false;
  3456 		return false;
  3457   }
  3457 	}
  3458   $octets = array(0 => array(), 1 => array(), 2 => array(), 3 => array());
  3458 	$octets = array(0 => array(), 1 => array(), 2 => array(), 3 => array());
  3459   list($octets[0], $octets[1], $octets[2], $octets[3]) = explode('.', $range);
  3459 	list($octets[0], $octets[1], $octets[2], $octets[3]) = explode('.', $range);
  3460   $return = '^';
  3460 	$return = '^';
  3461   foreach ( $octets as $octet )
  3461 	foreach ( $octets as $octet )
  3462   {
  3462 	{
  3463     // alternatives array
  3463 		// alternatives array
  3464     $alts = array();
  3464 		$alts = array();
  3465     if ( strpos($octet, '|') )
  3465 		if ( strpos($octet, '|') )
  3466     {
  3466 		{
  3467       $particles = explode('|', $octet);
  3467 			$particles = explode('|', $octet);
  3468     }
  3468 		}
  3469     else
  3469 		else
  3470     {
  3470 		{
  3471       $particles = array($octet);
  3471 			$particles = array($octet);
  3472     }
  3472 		}
  3473     foreach ( $particles as $atom )
  3473 		foreach ( $particles as $atom )
  3474     {
  3474 		{
  3475       // each $atom will be either
  3475 			// each $atom will be either
  3476       if ( strval(intval($atom)) == $atom )
  3476 			if ( strval(intval($atom)) == $atom )
  3477       {
  3477 			{
  3478         $alts[] = $atom;
  3478 				$alts[] = $atom;
  3479         continue;
  3479 				continue;
  3480       }
  3480 			}
  3481       else
  3481 			else
  3482       {
  3482 			{
  3483         // it's a range - parse it out
  3483 				// it's a range - parse it out
  3484         $alt2 = int_range($atom);
  3484 				$alt2 = int_range($atom);
  3485         if ( !$alt2 )
  3485 				if ( !$alt2 )
  3486           return false;
  3486 					return false;
  3487         foreach ( $alt2 as $neutrino )
  3487 				foreach ( $alt2 as $neutrino )
  3488           $alts[] = $neutrino;
  3488 					$alts[] = $neutrino;
  3489       }
  3489 			}
  3490     }
  3490 		}
  3491     $alts = array_unique($alts);
  3491 		$alts = array_unique($alts);
  3492     $alts = '|' . implode('|', $alts) . '|';
  3492 		$alts = '|' . implode('|', $alts) . '|';
  3493     // we can further optimize/compress this by weaseling our way into using some character ranges
  3493 		// we can further optimize/compress this by weaseling our way into using some character ranges
  3494     for ( $i = 1; $i <= 25; $i++ )
  3494 		for ( $i = 1; $i <= 25; $i++ )
  3495     {
  3495 		{
  3496       $alts = str_replace("|{$i}0|{$i}1|{$i}2|{$i}3|{$i}4|{$i}5|{$i}6|{$i}7|{$i}8|{$i}9|", "|{$i}[0-9]|", $alts);
  3496 			$alts = str_replace("|{$i}0|{$i}1|{$i}2|{$i}3|{$i}4|{$i}5|{$i}6|{$i}7|{$i}8|{$i}9|", "|{$i}[0-9]|", $alts);
  3497     }
  3497 		}
  3498     $alts = str_replace("|1|2|3|4|5|6|7|8|9|", "|[1-9]|", $alts);
  3498 		$alts = str_replace("|1|2|3|4|5|6|7|8|9|", "|[1-9]|", $alts);
  3499     $alts = '(' . substr($alts, 1, -1) . ')';
  3499 		$alts = '(' . substr($alts, 1, -1) . ')';
  3500     $return .= $alts . '\.';
  3500 		$return .= $alts . '\.';
  3501   }
  3501 	}
  3502   $return = substr($return, 0, -2);
  3502 	$return = substr($return, 0, -2);
  3503   $return .= '$';
  3503 	$return .= '$';
  3504   return $return;
  3504 	return $return;
  3505 }
  3505 }
  3506 
  3506 
  3507 /**
  3507 /**
  3508  * Parses a valid IPv6 address range into a regular expression.
  3508  * Parses a valid IPv6 address range into a regular expression.
  3509  * @param string IP range string
  3509  * @param string IP range string
  3510  * @return string
  3510  * @return string
  3511  */
  3511  */
  3512 
  3512 
  3513 function parse_ipv6_range_regex($range)
  3513 function parse_ipv6_range_regex($range)
  3514 {
  3514 {
  3515   $range = strtolower(trim($range));
  3515 	$range = strtolower(trim($range));
  3516   $valid = '/^';
  3516 	$valid = '/^';
  3517   $valid .= '(?:[0-9a-f]{0,4}|[0-9a-f]{1,4}-[0-9a-f]{1,4}):';
  3517 	$valid .= '(?:[0-9a-f]{0,4}|[0-9a-f]{1,4}-[0-9a-f]{1,4}):';
  3518   $valid .= '(?:[0-9a-f]{0,4}|[0-9a-f]{1,4}-[0-9a-f]{1,4}):';
  3518 	$valid .= '(?:[0-9a-f]{0,4}|[0-9a-f]{1,4}-[0-9a-f]{1,4}):';
  3519   $valid .= '(?:[0-9a-f]{0,4}|[0-9a-f]{1,4}-[0-9a-f]{1,4}:|:)?';
  3519 	$valid .= '(?:[0-9a-f]{0,4}|[0-9a-f]{1,4}-[0-9a-f]{1,4}:|:)?';
  3520   $valid .= '(?:[0-9a-f]{0,4}|[0-9a-f]{1,4}-[0-9a-f]{1,4}:|:)?';
  3520 	$valid .= '(?:[0-9a-f]{0,4}|[0-9a-f]{1,4}-[0-9a-f]{1,4}:|:)?';
  3521   $valid .= '(?:[0-9a-f]{0,4}|[0-9a-f]{1,4}-[0-9a-f]{1,4}:|:)?';
  3521 	$valid .= '(?:[0-9a-f]{0,4}|[0-9a-f]{1,4}-[0-9a-f]{1,4}:|:)?';
  3522   $valid .= '(?:[0-9a-f]{0,4}|[0-9a-f]{1,4}-[0-9a-f]{1,4}:|:)?';
  3522 	$valid .= '(?:[0-9a-f]{0,4}|[0-9a-f]{1,4}-[0-9a-f]{1,4}:|:)?';
  3523   $valid .= '(?:[0-9a-f]{0,4}|[0-9a-f]{1,4}-[0-9a-f]{1,4}:|:)?';
  3523 	$valid .= '(?:[0-9a-f]{0,4}|[0-9a-f]{1,4}-[0-9a-f]{1,4}:|:)?';
  3524   $valid .= '(?:[0-9a-f]{0,4}|[0-9a-f]{1,4}-[0-9a-f]{1,4})$/';
  3524 	$valid .= '(?:[0-9a-f]{0,4}|[0-9a-f]{1,4}-[0-9a-f]{1,4})$/';
  3525   if ( !preg_match($valid, $range) )
  3525 	if ( !preg_match($valid, $range) )
  3526     return false;
  3526 		return false;
  3527   
  3527 	
  3528   // expand address range.
  3528 	// expand address range.
  3529   // this takes short ranges like:
  3529 	// this takes short ranges like:
  3530   //   2001:470-471:054-b02b::5-bb
  3530 	//   2001:470-471:054-b02b::5-bb
  3531   // up to:
  3531 	// up to:
  3532   //   2001:0470-0471:0054-b02b:0000:0000:0000:0005-00bb
  3532 	//   2001:0470-0471:0054-b02b:0000:0000:0000:0005-00bb
  3533   $range = preg_replace('/^:/', '0000:', $range);
  3533 	$range = preg_replace('/^:/', '0000:', $range);
  3534   $range = explode(':', $range);
  3534 	$range = explode(':', $range);
  3535   $expanded = '';
  3535 	$expanded = '';
  3536   $size = count($range);
  3536 	$size = count($range);
  3537   foreach ( $range as $byteset )
  3537 	foreach ( $range as $byteset )
  3538   {
  3538 	{
  3539     if ( empty($byteset) )
  3539 		if ( empty($byteset) )
  3540     {
  3540 		{
  3541       // ::
  3541 			// ::
  3542       while ( $size < 9 )
  3542 			while ( $size < 9 )
  3543       {
  3543 			{
  3544         $expanded .= '0000:';
  3544 				$expanded .= '0000:';
  3545         $size++;
  3545 				$size++;
  3546       }
  3546 			}
  3547     }
  3547 		}
  3548     else
  3548 		else
  3549     {
  3549 		{
  3550       if ( strstr($byteset, '-') ) 
  3550 			if ( strstr($byteset, '-') ) 
  3551       {
  3551 			{
  3552         // this is a range
  3552 				// this is a range
  3553         $sides = explode('-', $byteset);
  3553 				$sides = explode('-', $byteset);
  3554         foreach ( $sides as &$bytepair )
  3554 				foreach ( $sides as &$bytepair )
  3555         {
  3555 				{
  3556           while ( strlen($bytepair) < 4 )
  3556 					while ( strlen($bytepair) < 4 )
  3557           {
  3557 					{
  3558             $bytepair = "0$bytepair";
  3558 						$bytepair = "0$bytepair";
  3559           }
  3559 					}
  3560         }
  3560 				}
  3561         $byteset = implode('-', $sides);
  3561 				$byteset = implode('-', $sides);
  3562       }
  3562 			}
  3563       else
  3563 			else
  3564       {
  3564 			{
  3565         while ( strlen($byteset) < 4 )
  3565 				while ( strlen($byteset) < 4 )
  3566         {
  3566 				{
  3567           $byteset = "0$byteset";
  3567 					$byteset = "0$byteset";
  3568         }
  3568 				}
  3569       }
  3569 			}
  3570       $expanded .= "$byteset:";
  3570 			$expanded .= "$byteset:";
  3571     }
  3571 		}
  3572   }
  3572 	}
  3573   $expanded = explode(':', rtrim($expanded, ':'));
  3573 	$expanded = explode(':', rtrim($expanded, ':'));
  3574   
  3574 	
  3575   // ready to dive in and start generating range regexes.
  3575 	// ready to dive in and start generating range regexes.
  3576   // this has to be pretty optimized... we want to end up with regexes like:
  3576 	// this has to be pretty optimized... we want to end up with regexes like:
  3577   // range: 54-b12b
  3577 	// range: 54-b12b
  3578   /*
  3578 	/*
  3579   /005[4-9a-f]|
  3579 	/005[4-9a-f]|
  3580   00[6-9a-f][0-9a-f]|
  3580 	00[6-9a-f][0-9a-f]|
  3581   0[1-9a-f][0-9a-f][0-9a-f]|
  3581 	0[1-9a-f][0-9a-f][0-9a-f]|
  3582   [1-9a][0-9a-f][0-9a-f][0-9a-f]|
  3582 	[1-9a][0-9a-f][0-9a-f][0-9a-f]|
  3583   b[0-0][0-1][0-9a-f]|
  3583 	b[0-0][0-1][0-9a-f]|
  3584   b0[0-1][0-9a-f]|
  3584 	b0[0-1][0-9a-f]|
  3585   b02[0-9a-b]/x
  3585 	b02[0-9a-b]/x
  3586   */
  3586 	*/
  3587   foreach ( $expanded as &$word )
  3587 	foreach ( $expanded as &$word )
  3588   {
  3588 	{
  3589     if ( strstr($word, '-') )
  3589 		if ( strstr($word, '-') )
  3590     {
  3590 		{
  3591       // oh... damn.
  3591 			// oh... damn.
  3592       $word = '(?:' . generate_hex_numeral_range($word) . ')';
  3592 			$word = '(?:' . generate_hex_numeral_range($word) . ')';
  3593     }
  3593 		}
  3594   }
  3594 	}
  3595 
  3595 
  3596   // return print_r($expanded, true);  
  3596 	// return print_r($expanded, true);  
  3597   return '^' . implode(':', $expanded) . '$';
  3597 	return '^' . implode(':', $expanded) . '$';
  3598 }
  3598 }
  3599 
  3599 
  3600 /**
  3600 /**
  3601  * Take a hex numeral range and parse it in to a PCRE.
  3601  * Take a hex numeral range and parse it in to a PCRE.
  3602  * @param string
  3602  * @param string
  3604  * @access private
  3604  * @access private
  3605  */
  3605  */
  3606 
  3606 
  3607 function generate_hex_numeral_range($word)
  3607 function generate_hex_numeral_range($word)
  3608 {
  3608 {
  3609   list($low, $high) = explode('-', $word);
  3609 	list($low, $high) = explode('-', $word);
  3610   
  3610 	
  3611   if ( hexdec($low) > hexdec($high) )
  3611 	if ( hexdec($low) > hexdec($high) )
  3612   {
  3612 	{
  3613     $_ = $low;
  3613 		$_ = $low;
  3614     $low = $high;
  3614 		$low = $high;
  3615     $high = $_;
  3615 		$high = $_;
  3616     unset($_);
  3616 		unset($_);
  3617   }
  3617 	}
  3618   
  3618 	
  3619   while ( strlen($low) < strlen($high) )
  3619 	while ( strlen($low) < strlen($high) )
  3620   {
  3620 	{
  3621     $low = "0$low";
  3621 		$low = "0$low";
  3622   }
  3622 	}
  3623   
  3623 	
  3624   // trim off everything that's the same
  3624 	// trim off everything that's the same
  3625   $trimmed = '';
  3625 	$trimmed = '';
  3626   $len = strlen($low);
  3626 	$len = strlen($low);
  3627   for ( $i = 0; $i < $len; $i++ )
  3627 	for ( $i = 0; $i < $len; $i++ )
  3628   {
  3628 	{
  3629     if ( $low{0} === $high{0} )
  3629 		if ( $low{0} === $high{0} )
  3630     {
  3630 		{
  3631       $trimmed .= $low{0};
  3631 			$trimmed .= $low{0};
  3632       $low = substr($low, 1);
  3632 			$low = substr($low, 1);
  3633       $high = substr($high, 1);
  3633 			$high = substr($high, 1);
  3634     }
  3634 		}
  3635     else
  3635 		else
  3636     {
  3636 		{
  3637       break;
  3637 			break;
  3638     }
  3638 		}
  3639   }
  3639 	}
  3640   
  3640 	
  3641   $len = strlen($high);
  3641 	$len = strlen($high);
  3642   if ( $len == 1 )
  3642 	if ( $len == 1 )
  3643   {
  3643 	{
  3644     // this does happen sometimes, so we can save a bit of CPU power here.
  3644 		// this does happen sometimes, so we can save a bit of CPU power here.
  3645     return $trimmed . __hexdigitrange($low, $high);
  3645 		return $trimmed . __hexdigitrange($low, $high);
  3646   }
  3646 	}
  3647     
  3647 		
  3648   $return = '';
  3648 	$return = '';
  3649   // lower half
  3649 	// lower half
  3650   for ( $i = $len - 1; $i > 0; $i-- )
  3650 	for ( $i = $len - 1; $i > 0; $i-- )
  3651   {
  3651 	{
  3652     if ( $low{$i} == 'f' )
  3652 		if ( $low{$i} == 'f' )
  3653       continue;
  3653 			continue;
  3654     $return .= $trimmed;
  3654 		$return .= $trimmed;
  3655     for ( $j = 0; $j < $len; $j++ )
  3655 		for ( $j = 0; $j < $len; $j++ )
  3656     {
  3656 		{
  3657       if ( $j < $i )
  3657 			if ( $j < $i )
  3658       {
  3658 			{
  3659         $return .= $low{$j};
  3659 				$return .= $low{$j};
  3660       }
  3660 			}
  3661       else if ( $j == $i && ( $i == $len - 1 || $low{$j} == 'f' ) )
  3661 			else if ( $j == $i && ( $i == $len - 1 || $low{$j} == 'f' ) )
  3662       {
  3662 			{
  3663         $return .= __hexdigitrange($low{$j}, 'f');
  3663 				$return .= __hexdigitrange($low{$j}, 'f');
  3664       }
  3664 			}
  3665       else if ( $j == $i && $i != $len - 1 )
  3665 			else if ( $j == $i && $i != $len - 1 )
  3666       {
  3666 			{
  3667         $return .= __hexdigitrange(dechex(hexdec($low{$j}) + 1), 'f');
  3667 				$return .= __hexdigitrange(dechex(hexdec($low{$j}) + 1), 'f');
  3668       }
  3668 			}
  3669       else
  3669 			else
  3670       {
  3670 			{
  3671         $return .= __hexdigitrange('0', 'f');
  3671 				$return .= __hexdigitrange('0', 'f');
  3672       }
  3672 			}
  3673     }
  3673 		}
  3674     $return .= '|';
  3674 		$return .= '|';
  3675   }
  3675 	}
  3676   // middle block
  3676 	// middle block
  3677   if ( hexdec($low{0}) + 1 < hexdec($high{0}) )
  3677 	if ( hexdec($low{0}) + 1 < hexdec($high{0}) )
  3678   {
  3678 	{
  3679     if ( hexdec($low{0}) + 1 < hexdec($high{0}) - 1 )
  3679 		if ( hexdec($low{0}) + 1 < hexdec($high{0}) - 1 )
  3680       $return .= $trimmed . __hexdigitrange(dechex(hexdec($low{0}) + 1), dechex(hexdec($high{0}) - 1));
  3680 			$return .= $trimmed . __hexdigitrange(dechex(hexdec($low{0}) + 1), dechex(hexdec($high{0}) - 1));
  3681     else
  3681 		else
  3682       $return .= $trimmed . __hexdigitrange($low{0}, $high{0});
  3682 			$return .= $trimmed . __hexdigitrange($low{0}, $high{0});
  3683     if ( $len - 1 > 0 )
  3683 		if ( $len - 1 > 0 )
  3684       $return .= '[0-9a-f]{' . ( $len - 1 ) . '}|';
  3684 			$return .= '[0-9a-f]{' . ( $len - 1 ) . '}|';
  3685   }
  3685 	}
  3686   // higher half
  3686 	// higher half
  3687   for ( $i = 1; $i < $len; $i++ )
  3687 	for ( $i = 1; $i < $len; $i++ )
  3688   {
  3688 	{
  3689     if ( $high{$i} == '0' )
  3689 		if ( $high{$i} == '0' )
  3690       continue;
  3690 			continue;
  3691     $return .= $trimmed;
  3691 		$return .= $trimmed;
  3692     for ( $j = 0; $j < $len; $j++ )
  3692 		for ( $j = 0; $j < $len; $j++ )
  3693     {
  3693 		{
  3694       if ( $j < $i )
  3694 			if ( $j < $i )
  3695       {
  3695 			{
  3696         $return .= $high{$j};
  3696 				$return .= $high{$j};
  3697       }
  3697 			}
  3698       else if ( $j == $i && ( $i == $len - 1 || $high{$j} == '0' ) )
  3698 			else if ( $j == $i && ( $i == $len - 1 || $high{$j} == '0' ) )
  3699       {
  3699 			{
  3700         $return .= __hexdigitrange('0', $high{$j});
  3700 				$return .= __hexdigitrange('0', $high{$j});
  3701       }
  3701 			}
  3702       else if ( $j == $i && $i != $len - 1 )
  3702 			else if ( $j == $i && $i != $len - 1 )
  3703       {
  3703 			{
  3704         $return .= __hexdigitrange('0', dechex(hexdec($high{$j}) - 1));
  3704 				$return .= __hexdigitrange('0', dechex(hexdec($high{$j}) - 1));
  3705       }
  3705 			}
  3706       else if ( $j > $i )
  3706 			else if ( $j > $i )
  3707       {
  3707 			{
  3708         $return .= __hexdigitrange('0', 'f');
  3708 				$return .= __hexdigitrange('0', 'f');
  3709       }
  3709 			}
  3710       else
  3710 			else
  3711       {
  3711 			{
  3712         die("I don't know what to do! i $i j $j");
  3712 				die("I don't know what to do! i $i j $j");
  3713       }
  3713 			}
  3714     }
  3714 		}
  3715     $return .= '|';
  3715 		$return .= '|';
  3716   }
  3716 	}
  3717   
  3717 	
  3718   return rtrim($return, '|');
  3718 	return rtrim($return, '|');
  3719 }
  3719 }
  3720 
  3720 
  3721 function __hexdigitrange($low, $high)
  3721 function __hexdigitrange($low, $high)
  3722 {
  3722 {
  3723   if ( $low == $high )
  3723 	if ( $low == $high )
  3724     return $low;
  3724 		return $low;
  3725   if ( empty($low) )
  3725 	if ( empty($low) )
  3726     $low = '0';
  3726 		$low = '0';
  3727   
  3727 	
  3728   $low_type = ( preg_match('/[0-9]/', $low) ) ? 'num' : 'alph';
  3728 	$low_type = ( preg_match('/[0-9]/', $low) ) ? 'num' : 'alph';
  3729   $high_type = ( preg_match('/[0-9]/', $high) ) ? 'num' : 'alph';
  3729 	$high_type = ( preg_match('/[0-9]/', $high) ) ? 'num' : 'alph';
  3730   if ( ( $low_type == 'num' && $high_type == 'num') || ( $low_type == 'alph' && $high_type == 'alph' ) )
  3730 	if ( ( $low_type == 'num' && $high_type == 'num') || ( $low_type == 'alph' && $high_type == 'alph' ) )
  3731   {
  3731 	{
  3732     return "[$low-$high]";
  3732 		return "[$low-$high]";
  3733   }
  3733 	}
  3734   else if ( $low_type == 'num' && $high_type == 'alph' )
  3734 	else if ( $low_type == 'num' && $high_type == 'alph' )
  3735   {
  3735 	{
  3736     $ret = '[';
  3736 		$ret = '[';
  3737     
  3737 		
  3738     if ( $low == '9' )
  3738 		if ( $low == '9' )
  3739       $ret .= '9';
  3739 			$ret .= '9';
  3740     else
  3740 		else
  3741       $ret .= "$low-9";
  3741 			$ret .= "$low-9";
  3742     if ( $high == 'a' )
  3742 		if ( $high == 'a' )
  3743       $ret .= 'a';
  3743 			$ret .= 'a';
  3744     else
  3744 		else
  3745       $ret .= "a-$high";
  3745 			$ret .= "a-$high";
  3746       
  3746 			
  3747     $ret .= "]";
  3747 		$ret .= "]";
  3748     return $ret;
  3748 		return $ret;
  3749   }
  3749 	}
  3750   else if ( $low_type == 'alph' && $high_type == 'num' )
  3750 	else if ( $low_type == 'alph' && $high_type == 'num' )
  3751   {
  3751 	{
  3752     // ???? this should never happen
  3752 		// ???? this should never happen
  3753     return __hexdigitrange($high, $low); 
  3753 		return __hexdigitrange($high, $low); 
  3754   }
  3754 	}
  3755 }
  3755 }
  3756 
  3756 
  3757 /**
  3757 /**
  3758  * Expand an IPv6 address to full form
  3758  * Expand an IPv6 address to full form
  3759  * @param string ::1, 2001:470:e054::2
  3759  * @param string ::1, 2001:470:e054::2
  3760  * @return string 0000:0000:0000:0000:0000:0000:0000:0001, 2001:0470:e054:0000:0000:0000:0000:0002
  3760  * @return string 0000:0000:0000:0000:0000:0000:0000:0001, 2001:0470:e054:0000:0000:0000:0000:0002
  3761  */
  3761  */
  3762 
  3762 
  3763 function expand_ipv6_address($addr)
  3763 function expand_ipv6_address($addr)
  3764 {
  3764 {
  3765   $expanded = array();
  3765 	$expanded = array();
  3766   $addr = explode(':', $addr);
  3766 	$addr = explode(':', $addr);
  3767   foreach ( $addr as $i => $bytepair )
  3767 	foreach ( $addr as $i => $bytepair )
  3768   {
  3768 	{
  3769     if ( empty($bytepair) )
  3769 		if ( empty($bytepair) )
  3770     {
  3770 		{
  3771       // ::
  3771 			// ::
  3772       while ( count($expanded) < (8 - count($addr) + $i + 1) )
  3772 			while ( count($expanded) < (8 - count($addr) + $i + 1) )
  3773       {
  3773 			{
  3774         $expanded[] = '0000';
  3774 				$expanded[] = '0000';
  3775       }
  3775 			}
  3776     }
  3776 		}
  3777     else
  3777 		else
  3778     {
  3778 		{
  3779       while ( strlen($bytepair) < 4 )
  3779 			while ( strlen($bytepair) < 4 )
  3780         $bytepair = "0$bytepair";
  3780 				$bytepair = "0$bytepair";
  3781       $expanded[] = $bytepair;
  3781 			$expanded[] = $bytepair;
  3782     }
  3782 		}
  3783   }
  3783 	}
  3784   return implode(':', $expanded);
  3784 	return implode(':', $expanded);
  3785 }
  3785 }
  3786 
  3786 
  3787 /**
  3787 /**
  3788  * Validates an e-mail address. Uses a compacted version of the regular expression generated by the scripts at <http://examples.oreilly.com/regex/>.
  3788  * Validates an e-mail address. Uses a compacted version of the regular expression generated by the scripts at <http://examples.oreilly.com/regex/>.
  3789  * @param string E-mail address
  3789  * @param string E-mail address
  3790  * @return bool
  3790  * @return bool
  3791  */
  3791  */
  3792 
  3792 
  3793 function check_email_address($email)
  3793 function check_email_address($email)
  3794 {
  3794 {
  3795   static $regexp = '(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|"[^\\\x80-\xff\n\015"]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015"]*)*")[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:\.[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|"[^\\\x80-\xff\n\015"]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015"]*)*")[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*)*@[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|\[(?:[^\\\x80-\xff\n\015\[\]]|\\[^\x80-\xff])*\])[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:\.[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|\[(?:[^\\\x80-\xff\n\015\[\]]|\\[^\x80-\xff])*\])[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*)*|(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|"[^\\\x80-\xff\n\015"]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015"]*)*")[^()<>@,;:".\\\[\]\x80-\xff\000-\010\012-\037]*(?:(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)|"[^\\\x80-\xff\n\015"]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015"]*)*")[^()<>@,;:".\\\[\]\x80-\xff\000-\010\012-\037]*)*<[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:@[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|\[(?:[^\\\x80-\xff\n\015\[\]]|\\[^\x80-\xff])*\])[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:\.[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|\[(?:[^\\\x80-\xff\n\015\[\]]|\\[^\x80-\xff])*\])[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*)*(?:,[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*@[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|\[(?:[^\\\x80-\xff\n\015\[\]]|\\[^\x80-\xff])*\])[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:\.[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|\[(?:[^\\\x80-\xff\n\015\[\]]|\\[^\x80-\xff])*\])[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*)*)*:[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*)?(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|"[^\\\x80-\xff\n\015"]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015"]*)*")[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:\.[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|"[^\\\x80-\xff\n\015"]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015"]*)*")[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*)*@[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|\[(?:[^\\\x80-\xff\n\015\[\]]|\\[^\x80-\xff])*\])[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:\.[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|\[(?:[^\\\x80-\xff\n\015\[\]]|\\[^\x80-\xff])*\])[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*)*>)';
  3795 	static $regexp = '(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|"[^\\\x80-\xff\n\015"]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015"]*)*")[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:\.[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|"[^\\\x80-\xff\n\015"]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015"]*)*")[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*)*@[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|\[(?:[^\\\x80-\xff\n\015\[\]]|\\[^\x80-\xff])*\])[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:\.[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|\[(?:[^\\\x80-\xff\n\015\[\]]|\\[^\x80-\xff])*\])[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*)*|(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|"[^\\\x80-\xff\n\015"]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015"]*)*")[^()<>@,;:".\\\[\]\x80-\xff\000-\010\012-\037]*(?:(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)|"[^\\\x80-\xff\n\015"]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015"]*)*")[^()<>@,;:".\\\[\]\x80-\xff\000-\010\012-\037]*)*<[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:@[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|\[(?:[^\\\x80-\xff\n\015\[\]]|\\[^\x80-\xff])*\])[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:\.[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|\[(?:[^\\\x80-\xff\n\015\[\]]|\\[^\x80-\xff])*\])[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*)*(?:,[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*@[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|\[(?:[^\\\x80-\xff\n\015\[\]]|\\[^\x80-\xff])*\])[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:\.[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|\[(?:[^\\\x80-\xff\n\015\[\]]|\\[^\x80-\xff])*\])[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*)*)*:[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*)?(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|"[^\\\x80-\xff\n\015"]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015"]*)*")[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:\.[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|"[^\\\x80-\xff\n\015"]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015"]*)*")[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*)*@[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|\[(?:[^\\\x80-\xff\n\015\[\]]|\\[^\x80-\xff])*\])[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:\.[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|\[(?:[^\\\x80-\xff\n\015\[\]]|\\[^\x80-\xff])*\])[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*)*>)';
  3796   return ( preg_match("/^$regexp$/", $email) ) ? true : false;
  3796 	return ( preg_match("/^$regexp$/", $email) ) ? true : false;
  3797 }
  3797 }
  3798 
  3798 
  3799 function password_score_len($password)
  3799 function password_score_len($password)
  3800 {
  3800 {
  3801   if ( !is_string($password) )
  3801 	if ( !is_string($password) )
  3802   {
  3802 	{
  3803     return -10;
  3803 		return -10;
  3804   }
  3804 	}
  3805   $len = strlen($password);
  3805 	$len = strlen($password);
  3806   $score = $len - 7;
  3806 	$score = $len - 7;
  3807   return $score;
  3807 	return $score;
  3808 }
  3808 }
  3809 
  3809 
  3810 /**
  3810 /**
  3811  * Give a numerical score for how strong a password is. This is an open-ended scale based on a score added to or subtracted
  3811  * Give a numerical score for how strong a password is. This is an open-ended scale based on a score added to or subtracted
  3812  * from based on certain complexity rules. Anything less than about 1 or 0 is weak, 3-4 is strong, and 10 is not to be easily cracked.
  3812  * from based on certain complexity rules. Anything less than about 1 or 0 is weak, 3-4 is strong, and 10 is not to be easily cracked.
  3816  * @return int
  3816  * @return int
  3817  */
  3817  */
  3818 
  3818 
  3819 function password_score($password, &$debug = false)
  3819 function password_score($password, &$debug = false)
  3820 {
  3820 {
  3821   if ( !is_string($password) )
  3821 	if ( !is_string($password) )
  3822   {
  3822 	{
  3823     return -10;
  3823 		return -10;
  3824   }
  3824 	}
  3825   $score = 0;
  3825 	$score = 0;
  3826   $debug = array();
  3826 	$debug = array();
  3827   // length check
  3827 	// length check
  3828   $lenscore = password_score_len($password);
  3828 	$lenscore = password_score_len($password);
  3829   
  3829 	
  3830   $debug[] = "<b>How this score was calculated</b>\nYour score was tallied up based on an extensive algorithm which outputted\nthe following scores based on traits of your password. Above you can see the\ncomposite score; your individual scores based on certain tests are below.\n\nThe scale is open-ended, with a minimum score of -10. 10 is very strong, 4\nis strong, 1 is good and -3 is fair. Below -3 scores \"Weak.\"\n";
  3830 	$debug[] = "<b>How this score was calculated</b>\nYour score was tallied up based on an extensive algorithm which outputted\nthe following scores based on traits of your password. Above you can see the\ncomposite score; your individual scores based on certain tests are below.\n\nThe scale is open-ended, with a minimum score of -10. 10 is very strong, 4\nis strong, 1 is good and -3 is fair. Below -3 scores \"Weak.\"\n";
  3831   
  3831 	
  3832   $debug[] = 'Adding '.$lenscore.' points for length';
  3832 	$debug[] = 'Adding '.$lenscore.' points for length';
  3833   
  3833 	
  3834   $score += $lenscore;
  3834 	$score += $lenscore;
  3835     
  3835 		
  3836   $has_upper_lower = false;
  3836 	$has_upper_lower = false;
  3837   $has_symbols     = false;
  3837 	$has_symbols     = false;
  3838   $has_numbers     = false;
  3838 	$has_numbers     = false;
  3839   
  3839 	
  3840   // contains uppercase and lowercase
  3840 	// contains uppercase and lowercase
  3841   if ( preg_match('/[A-z]+/', $password) && strtolower($password) != $password )
  3841 	if ( preg_match('/[A-z]+/', $password) && strtolower($password) != $password )
  3842   {
  3842 	{
  3843     $score += 1;
  3843 		$score += 1;
  3844     $has_upper_lower = true;
  3844 		$has_upper_lower = true;
  3845     $debug[] = 'Adding 1 point for having uppercase and lowercase';
  3845 		$debug[] = 'Adding 1 point for having uppercase and lowercase';
  3846   }
  3846 	}
  3847   
  3847 	
  3848   // contains symbols
  3848 	// contains symbols
  3849   if ( preg_match('/[^A-z0-9]+/', $password) )
  3849 	if ( preg_match('/[^A-z0-9]+/', $password) )
  3850   {
  3850 	{
  3851     $score += 1;
  3851 		$score += 1;
  3852     $has_symbols = true;
  3852 		$has_symbols = true;
  3853     $debug[] = 'Adding 1 point for having nonalphanumeric characters (matching /[^A-z0-9]+/)';
  3853 		$debug[] = 'Adding 1 point for having nonalphanumeric characters (matching /[^A-z0-9]+/)';
  3854   }
  3854 	}
  3855   
  3855 	
  3856   // contains numbers
  3856 	// contains numbers
  3857   if ( preg_match('/[0-9]+/', $password) )
  3857 	if ( preg_match('/[0-9]+/', $password) )
  3858   {
  3858 	{
  3859     $score += 1;
  3859 		$score += 1;
  3860     $has_numbers = true;
  3860 		$has_numbers = true;
  3861     $debug[] = 'Adding 1 point for having numbers';
  3861 		$debug[] = 'Adding 1 point for having numbers';
  3862   }
  3862 	}
  3863   
  3863 	
  3864   if ( $has_upper_lower && $has_symbols && $has_numbers && strlen($password) >= 9 )
  3864 	if ( $has_upper_lower && $has_symbols && $has_numbers && strlen($password) >= 9 )
  3865   {
  3865 	{
  3866     // if it has uppercase and lowercase letters, symbols, and numbers, and is of considerable length, add some serious points
  3866 		// if it has uppercase and lowercase letters, symbols, and numbers, and is of considerable length, add some serious points
  3867     $score += 4;
  3867 		$score += 4;
  3868     $debug[] = 'Adding 4 points for having uppercase and lowercase, numbers, and nonalphanumeric and being more than 8 characters';
  3868 		$debug[] = 'Adding 4 points for having uppercase and lowercase, numbers, and nonalphanumeric and being more than 8 characters';
  3869   }
  3869 	}
  3870   else if ( $has_upper_lower && $has_symbols && $has_numbers )
  3870 	else if ( $has_upper_lower && $has_symbols && $has_numbers )
  3871   {
  3871 	{
  3872     // still give some points for passing complexity check
  3872 		// still give some points for passing complexity check
  3873     $score += 2;
  3873 		$score += 2;
  3874     $debug[] = 'Adding 2 points for having uppercase and lowercase, numbers, and nonalphanumeric';
  3874 		$debug[] = 'Adding 2 points for having uppercase and lowercase, numbers, and nonalphanumeric';
  3875   }
  3875 	}
  3876   else if ( ( $has_upper_lower && $has_symbols ) ||
  3876 	else if ( ( $has_upper_lower && $has_symbols ) ||
  3877             ( $has_upper_lower && $has_numbers ) ||
  3877 						( $has_upper_lower && $has_numbers ) ||
  3878             ( $has_symbols && $has_numbers ) )
  3878 						( $has_symbols && $has_numbers ) )
  3879   {
  3879 	{
  3880     // if 2 of the three main complexity checks passed, add a point
  3880 		// if 2 of the three main complexity checks passed, add a point
  3881     $score += 1;
  3881 		$score += 1;
  3882     $debug[] = 'Adding 1 point for having 2 of 3 complexity checks';
  3882 		$debug[] = 'Adding 1 point for having 2 of 3 complexity checks';
  3883   }
  3883 	}
  3884   else if ( preg_match('/^[0-9]*?([a-z]+)[0-9]?$/', $password) )
  3884 	else if ( preg_match('/^[0-9]*?([a-z]+)[0-9]?$/', $password) )
  3885   {
  3885 	{
  3886     // password is something like magnum1 which will be cracked in seconds
  3886 		// password is something like magnum1 which will be cracked in seconds
  3887     $score += -4;
  3887 		$score += -4;
  3888     $debug[] = 'Adding -4 points for being of the form [number][word][number]';
  3888 		$debug[] = 'Adding -4 points for being of the form [number][word][number]';
  3889   }
  3889 	}
  3890   else if ( ( !$has_upper_lower && !$has_numbers && $has_symbols ) ||
  3890 	else if ( ( !$has_upper_lower && !$has_numbers && $has_symbols ) ||
  3891             ( !$has_upper_lower && !$has_symbols && $has_numbers ) ||
  3891 						( !$has_upper_lower && !$has_symbols && $has_numbers ) ||
  3892             ( !$has_numbers && !$has_symbols && $has_upper_lower ) )
  3892 						( !$has_numbers && !$has_symbols && $has_upper_lower ) )
  3893   {
  3893 	{
  3894     $score += -2;
  3894 		$score += -2;
  3895     $debug[] = 'Adding -2 points for only meeting 1 complexity check';
  3895 		$debug[] = 'Adding -2 points for only meeting 1 complexity check';
  3896   }
  3896 	}
  3897   else if ( !$has_upper_lower && !$has_numbers && !$has_symbols )
  3897 	else if ( !$has_upper_lower && !$has_numbers && !$has_symbols )
  3898   {
  3898 	{
  3899     $debug[] = 'Adding -3 points for not meeting any complexity checks';
  3899 		$debug[] = 'Adding -3 points for not meeting any complexity checks';
  3900     $score += -3;
  3900 		$score += -3;
  3901   }
  3901 	}
  3902   
  3902 	
  3903   //
  3903 	//
  3904   // Repetition
  3904 	// Repetition
  3905   // Example: foobar12345 should be deducted points, where f1o2o3b4a5r should be given points
  3905 	// Example: foobar12345 should be deducted points, where f1o2o3b4a5r should be given points
  3906   //
  3906 	//
  3907   
  3907 	
  3908   if ( preg_match('/([A-Z][A-Z][A-Z][A-Z]|[a-z][a-z][a-z][a-z])/', $password) )
  3908 	if ( preg_match('/([A-Z][A-Z][A-Z][A-Z]|[a-z][a-z][a-z][a-z])/', $password) )
  3909   {
  3909 	{
  3910     $debug[] = 'Adding -2 points for having more than 4 letters of the same case in a row';
  3910 		$debug[] = 'Adding -2 points for having more than 4 letters of the same case in a row';
  3911     $score += -2;
  3911 		$score += -2;
  3912   }
  3912 	}
  3913   else if ( preg_match('/([A-Z][A-Z][A-Z]|[a-z][a-z][a-z])/', $password) )
  3913 	else if ( preg_match('/([A-Z][A-Z][A-Z]|[a-z][a-z][a-z])/', $password) )
  3914   {
  3914 	{
  3915     $debug[] = 'Adding -1 points for having more than 3 letters of the same case in a row';
  3915 		$debug[] = 'Adding -1 points for having more than 3 letters of the same case in a row';
  3916     $score += -1;
  3916 		$score += -1;
  3917   }
  3917 	}
  3918   else if ( preg_match('/[A-z]/', $password) && !preg_match('/([A-Z][A-Z][A-Z]|[a-z][a-z][a-z])/', $password) )
  3918 	else if ( preg_match('/[A-z]/', $password) && !preg_match('/([A-Z][A-Z][A-Z]|[a-z][a-z][a-z])/', $password) )
  3919   {
  3919 	{
  3920     $debug[] = 'Adding 1 point for never having more than 2 letters of the same case in a row';
  3920 		$debug[] = 'Adding 1 point for never having more than 2 letters of the same case in a row';
  3921     $score += 1;
  3921 		$score += 1;
  3922   }
  3922 	}
  3923   
  3923 	
  3924   if ( preg_match('/[0-9][0-9][0-9][0-9]/', $password) )
  3924 	if ( preg_match('/[0-9][0-9][0-9][0-9]/', $password) )
  3925   {
  3925 	{
  3926     $debug[] = 'Adding -2 points for having 4 or more numbers in a row';
  3926 		$debug[] = 'Adding -2 points for having 4 or more numbers in a row';
  3927     $score += -2;
  3927 		$score += -2;
  3928   }
  3928 	}
  3929   else if ( preg_match('/[0-9][0-9][0-9]/', $password) )
  3929 	else if ( preg_match('/[0-9][0-9][0-9]/', $password) )
  3930   {
  3930 	{
  3931     $debug[] = 'Adding -1 points for having 3 or more numbers in a row';
  3931 		$debug[] = 'Adding -1 points for having 3 or more numbers in a row';
  3932     $score += -1;
  3932 		$score += -1;
  3933   }
  3933 	}
  3934   else if ( $has_numbers && !preg_match('/[0-9][0-9][0-9]/', $password) )
  3934 	else if ( $has_numbers && !preg_match('/[0-9][0-9][0-9]/', $password) )
  3935   {
  3935 	{
  3936     $debug[] = 'Adding 1 point for never more than 2 numbers in a row';
  3936 		$debug[] = 'Adding 1 point for never more than 2 numbers in a row';
  3937     $score += -1;
  3937 		$score += -1;
  3938   }
  3938 	}
  3939   
  3939 	
  3940   // make passwords like fooooooooooooooooooooooooooooooooooooo totally die by subtracting a point for each character repeated at least 3 times in a row
  3940 	// make passwords like fooooooooooooooooooooooooooooooooooooo totally die by subtracting a point for each character repeated at least 3 times in a row
  3941   $prev_char = '';
  3941 	$prev_char = '';
  3942   $warn = false;
  3942 	$warn = false;
  3943   $loss = 0;
  3943 	$loss = 0;
  3944   for ( $i = 0; $i < strlen($password); $i++ )
  3944 	for ( $i = 0; $i < strlen($password); $i++ )
  3945   {
  3945 	{
  3946     $chr = $password{$i};
  3946 		$chr = $password{$i};
  3947     if ( $chr == $prev_char && $warn )
  3947 		if ( $chr == $prev_char && $warn )
  3948     {
  3948 		{
  3949       $loss += -1;
  3949 			$loss += -1;
  3950     }
  3950 		}
  3951     else if ( $chr == $prev_char && !$warn )
  3951 		else if ( $chr == $prev_char && !$warn )
  3952     {
  3952 		{
  3953       $warn = true;
  3953 			$warn = true;
  3954     }
  3954 		}
  3955     else if ( $chr != $prev_char && $warn )
  3955 		else if ( $chr != $prev_char && $warn )
  3956     {
  3956 		{
  3957       $warn = false;
  3957 			$warn = false;
  3958     }
  3958 		}
  3959     $prev_char = $chr;
  3959 		$prev_char = $chr;
  3960   }
  3960 	}
  3961   if ( $loss < 0 )
  3961 	if ( $loss < 0 )
  3962   {
  3962 	{
  3963     $debug[] = 'Adding '.$loss.' points for immediate character repetition';
  3963 		$debug[] = 'Adding '.$loss.' points for immediate character repetition';
  3964     $score += $loss;
  3964 		$score += $loss;
  3965     // this can bring the score below -10 sometimes
  3965 		// this can bring the score below -10 sometimes
  3966     if ( $score < -10 )
  3966 		if ( $score < -10 )
  3967     {
  3967 		{
  3968       $debug[] = 'Setting score to -10 because it went below ('.$score.')';
  3968 			$debug[] = 'Setting score to -10 because it went below ('.$score.')';
  3969       $score = -10;
  3969 			$score = -10;
  3970     }
  3970 		}
  3971   }
  3971 	}
  3972   
  3972 	
  3973   return $score;
  3973 	return $score;
  3974 }
  3974 }
  3975 
  3975 
  3976 /**
  3976 /**
  3977  * Registers a task that will be run every X hours. Scheduled tasks should always be scheduled at runtime - they are not stored in the DB.
  3977  * Registers a task that will be run every X hours. Scheduled tasks should always be scheduled at runtime - they are not stored in the DB.
  3978  * @param string Function name to call, or array(object, string method)
  3978  * @param string Function name to call, or array(object, string method)
  3979  * @param int Interval between runs, in hours. Defaults to 24.
  3979  * @param int Interval between runs, in hours. Defaults to 24.
  3980  */
  3980  */
  3981 
  3981 
  3982 function register_cron_task($func, $hour_interval = 24)
  3982 function register_cron_task($func, $hour_interval = 24)
  3983 {
  3983 {
  3984   global $cron_tasks;
  3984 	global $cron_tasks;
  3985   $hour_interval = strval($hour_interval);
  3985 	$hour_interval = strval($hour_interval);
  3986   if ( !isset($cron_tasks[$hour_interval]) )
  3986 	if ( !isset($cron_tasks[$hour_interval]) )
  3987     $cron_tasks[$hour_interval] = array();
  3987 		$cron_tasks[$hour_interval] = array();
  3988   $cron_tasks[$hour_interval][] = $func;
  3988 	$cron_tasks[$hour_interval][] = $func;
  3989 }
  3989 }
  3990 
  3990 
  3991 /**
  3991 /**
  3992  * Gets the timestamp for the next estimated cron run.
  3992  * Gets the timestamp for the next estimated cron run.
  3993  * @return int
  3993  * @return int
  3994  */
  3994  */
  3995 
  3995 
  3996 function get_cron_next_run()
  3996 function get_cron_next_run()
  3997 {
  3997 {
  3998   global $cron_tasks;
  3998 	global $cron_tasks;
  3999   $lowest_ivl = min(array_keys($cron_tasks));
  3999 	$lowest_ivl = min(array_keys($cron_tasks));
  4000   $last_run = intval(getConfig("cron_lastrun_ivl_$lowest_ivl"));
  4000 	$last_run = intval(getConfig("cron_lastrun_ivl_$lowest_ivl"));
  4001   return intval($last_run + ( 3600 * $lowest_ivl )) - 30;
  4001 	return intval($last_run + ( 3600 * $lowest_ivl )) - 30;
  4002 }
  4002 }
  4003 
  4003 
  4004 /**
  4004 /**
  4005  * Installs a language.
  4005  * Installs a language.
  4006  * @param string The ISO-639-3 identifier for the language. Maximum of 6 characters, usually 3.
  4006  * @param string The ISO-639-3 identifier for the language. Maximum of 6 characters, usually 3.
  4009  * @param string The path to the file containing the language's strings. Optional.
  4009  * @param string The path to the file containing the language's strings. Optional.
  4010  */
  4010  */
  4011 
  4011 
  4012 function install_language($lang_code, $lang_name_neutral, $lang_name_local, $lang_file = false)
  4012 function install_language($lang_code, $lang_name_neutral, $lang_name_local, $lang_file = false)
  4013 {
  4013 {
  4014   global $db, $session, $paths, $template, $plugins; // Common objects
  4014 	global $db, $session, $paths, $template, $plugins; // Common objects
  4015   
  4015 	
  4016   $q = $db->sql_query('SELECT 1 FROM '.table_prefix.'language WHERE lang_code = \'' . $db->escape($lang_code) . '\';');
  4016 	$q = $db->sql_query('SELECT 1 FROM '.table_prefix.'language WHERE lang_code = \'' . $db->escape($lang_code) . '\';');
  4017   if ( !$q )
  4017 	if ( !$q )
  4018     $db->_die('functions.php - checking for language existence');
  4018 		$db->_die('functions.php - checking for language existence');
  4019   
  4019 	
  4020   if ( $db->numrows() > 0 )
  4020 	if ( $db->numrows() > 0 )
  4021     // Language already exists
  4021 		// Language already exists
  4022     return false;
  4022 		return false;
  4023   
  4023 	
  4024   $q = $db->sql_query('INSERT INTO ' . table_prefix . 'language(lang_code, lang_name_default, lang_name_native) 
  4024 	$q = $db->sql_query('INSERT INTO ' . table_prefix . 'language(lang_code, lang_name_default, lang_name_native) 
  4025                          VALUES(
  4025  												VALUES(
  4026                            \'' . $db->escape($lang_code) . '\',
  4026  													\'' . $db->escape($lang_code) . '\',
  4027                            \'' . $db->escape($lang_name_neutral) . '\',
  4027  													\'' . $db->escape($lang_name_neutral) . '\',
  4028                            \'' . $db->escape($lang_name_local) . '\'
  4028  													\'' . $db->escape($lang_name_local) . '\'
  4029                          );');
  4029  												);');
  4030   if ( !$q )
  4030 	if ( !$q )
  4031     $db->_die('functions.php - installing language');
  4031 		$db->_die('functions.php - installing language');
  4032   
  4032 	
  4033   if ( ENANO_DBLAYER == 'PGSQL' )
  4033 	if ( ENANO_DBLAYER == 'PGSQL' )
  4034   {
  4034 	{
  4035     // exception for Postgres, which doesn't support insert IDs
  4035 		// exception for Postgres, which doesn't support insert IDs
  4036     // This will cause the Language class to just load by lang code
  4036 		// This will cause the Language class to just load by lang code
  4037     // instead of by numeric ID
  4037 		// instead of by numeric ID
  4038     $lang_id = $lang_code;
  4038 		$lang_id = $lang_code;
  4039   }
  4039 	}
  4040   else
  4040 	else
  4041   {
  4041 	{
  4042     $lang_id = $db->insert_id();
  4042 		$lang_id = $db->insert_id();
  4043     if ( empty($lang_id) || $lang_id == 0 )
  4043 		if ( empty($lang_id) || $lang_id == 0 )
  4044     {
  4044 		{
  4045       $db->_die('functions.php - invalid returned lang_id');
  4045 			$db->_die('functions.php - invalid returned lang_id');
  4046     }
  4046 		}
  4047   }
  4047 	}
  4048   
  4048 	
  4049   // Do we also need to install a language file?
  4049 	// Do we also need to install a language file?
  4050   if ( is_string($lang_file) && file_exists($lang_file) )
  4050 	if ( is_string($lang_file) && file_exists($lang_file) )
  4051   {
  4051 	{
  4052     $lang = new Language($lang_id);
  4052 		$lang = new Language($lang_id);
  4053     $lang->import($lang_file);
  4053 		$lang->import($lang_file);
  4054   }
  4054 	}
  4055   else if ( is_string($lang_file) && !file_exists($lang_file) )
  4055 	else if ( is_string($lang_file) && !file_exists($lang_file) )
  4056   {
  4056 	{
  4057     echo '<b>Notice:</b> Can\'t load language file, so the specified language wasn\'t fully installed.<br />';
  4057 		echo '<b>Notice:</b> Can\'t load language file, so the specified language wasn\'t fully installed.<br />';
  4058     return false;
  4058 		return false;
  4059   }
  4059 	}
  4060   return true;
  4060 	return true;
  4061 }
  4061 }
  4062 
  4062 
  4063 /**
  4063 /**
  4064  * Lists available languages.
  4064  * Lists available languages.
  4065  * @return array Multi-depth. Associative, with children associative containing keys name, name_eng, and dir.
  4065  * @return array Multi-depth. Associative, with children associative containing keys name, name_eng, and dir.
  4066  */
  4066  */
  4067 
  4067 
  4068 function list_available_languages()
  4068 function list_available_languages()
  4069 {
  4069 {
  4070   // Pulled from install/includes/common.php
  4070 	// Pulled from install/includes/common.php
  4071   
  4071 	
  4072   // Build a list of available languages
  4072 	// Build a list of available languages
  4073   $dir = @opendir( ENANO_ROOT . '/language' );
  4073 	$dir = @opendir( ENANO_ROOT . '/language' );
  4074   if ( !$dir )
  4074 	if ( !$dir )
  4075     die('CRITICAL: could not open language directory');
  4075 		die('CRITICAL: could not open language directory');
  4076   
  4076 	
  4077   $languages = array();
  4077 	$languages = array();
  4078   
  4078 	
  4079   while ( $dh = @readdir($dir) )
  4079 	while ( $dh = @readdir($dir) )
  4080   {
  4080 	{
  4081     if ( $dh == '.' || $dh == '..' )
  4081 		if ( $dh == '.' || $dh == '..' )
  4082       continue;
  4082 			continue;
  4083     if ( file_exists( ENANO_ROOT . "/language/$dh/meta.json" ) )
  4083 		if ( file_exists( ENANO_ROOT . "/language/$dh/meta.json" ) )
  4084     {
  4084 		{
  4085       // Found a language directory, determine metadata
  4085 			// Found a language directory, determine metadata
  4086       $meta = @file_get_contents( ENANO_ROOT . "/language/$dh/meta.json" );
  4086 			$meta = @file_get_contents( ENANO_ROOT . "/language/$dh/meta.json" );
  4087       if ( empty($meta) )
  4087 			if ( empty($meta) )
  4088         // Could not read metadata file, continue silently
  4088 				// Could not read metadata file, continue silently
  4089         continue;
  4089 				continue;
  4090         
  4090 				
  4091       // Do some syntax correction on the metadata
  4091 			// Do some syntax correction on the metadata
  4092       $meta = enano_clean_json($meta);
  4092 			$meta = enano_clean_json($meta);
  4093         
  4093 				
  4094       $meta = enano_json_decode($meta);
  4094 			$meta = enano_json_decode($meta);
  4095       if ( isset($meta['lang_name_english']) && isset($meta['lang_name_native']) && isset($meta['lang_code']) )
  4095 			if ( isset($meta['lang_name_english']) && isset($meta['lang_name_native']) && isset($meta['lang_code']) )
  4096       {
  4096 			{
  4097         $languages[$meta['lang_code']] = array(
  4097 				$languages[$meta['lang_code']] = array(
  4098             'name' => $meta['lang_name_native'],
  4098 						'name' => $meta['lang_name_native'],
  4099             'name_eng' => $meta['lang_name_english'],
  4099 						'name_eng' => $meta['lang_name_english'],
  4100             'dir' => $dh
  4100 						'dir' => $dh
  4101           );
  4101 					);
  4102       }
  4102 			}
  4103     }
  4103 		}
  4104   }
  4104 	}
  4105   
  4105 	
  4106   return $languages;
  4106 	return $languages;
  4107 }
  4107 }
  4108 
  4108 
  4109 /**
  4109 /**
  4110  * Scales an image to the specified width and height, and writes the output to the specified
  4110  * Scales an image to the specified width and height, and writes the output to the specified
  4111  * file. Will use ImageMagick if present, but if not will attempt to scale with GD. This will
  4111  * file. Will use ImageMagick if present, but if not will attempt to scale with GD. This will
  4118  * @return bool True on success, false on failure
  4118  * @return bool True on success, false on failure
  4119  */
  4119  */
  4120 
  4120 
  4121 function scale_image($in_file, $out_file, $width = 225, $height = 225, $unlink = false)
  4121 function scale_image($in_file, $out_file, $width = 225, $height = 225, $unlink = false)
  4122 {
  4122 {
  4123   global $db, $session, $paths, $template, $plugins; // Common objects
  4123 	global $db, $session, $paths, $template, $plugins; // Common objects
  4124   
  4124 	
  4125   if ( !is_int($width) || !is_int($height) )
  4125 	if ( !is_int($width) || !is_int($height) )
  4126     throw new Exception('Invalid height or width.');
  4126 		throw new Exception('Invalid height or width.');
  4127   
  4127 	
  4128   if ( !file_exists($in_file) )
  4128 	if ( !file_exists($in_file) )
  4129     throw new Exception('Input file does not exist');
  4129 		throw new Exception('Input file does not exist');
  4130   
  4130 	
  4131   $in_file_sh = escapeshellarg($in_file);
  4131 	$in_file_sh = escapeshellarg($in_file);
  4132   $out_file_sh = escapeshellarg($out_file);
  4132 	$out_file_sh = escapeshellarg($out_file);
  4133   
  4133 	
  4134   if ( file_exists($out_file) && !$unlink )
  4134 	if ( file_exists($out_file) && !$unlink )
  4135     throw new Exception('Refusing to write output file as it already exists and $unlink was not specified.');
  4135 		throw new Exception('Refusing to write output file as it already exists and $unlink was not specified.');
  4136   else if ( file_exists($out_file) && $unlink )
  4136 	else if ( file_exists($out_file) && $unlink )
  4137     @unlink($out_file);
  4137 		@unlink($out_file);
  4138   if ( file_exists($out_file) )
  4138 	if ( file_exists($out_file) )
  4139     // couldn't unlink (delete) the output file
  4139 		// couldn't unlink (delete) the output file
  4140     throw new Exception('Failed to delete existing output file.');
  4140 		throw new Exception('Failed to delete existing output file.');
  4141     
  4141 		
  4142   $file_ext = strtolower(substr($in_file, ( strrpos($in_file, '.') + 1)));
  4142 	$file_ext = strtolower(substr($in_file, ( strrpos($in_file, '.') + 1)));
  4143   switch($file_ext)
  4143 	switch($file_ext)
  4144   {
  4144 	{
  4145     case 'png':
  4145 		case 'png':
  4146       $func = 'imagecreatefrompng';
  4146 			$func = 'imagecreatefrompng';
  4147       break;
  4147 			break;
  4148     case 'jpg':
  4148 		case 'jpg':
  4149     case 'jpeg':
  4149 		case 'jpeg':
  4150       $func = 'imagecreatefromjpeg';
  4150 			$func = 'imagecreatefromjpeg';
  4151       break;
  4151 			break;
  4152     case 'gif':
  4152 		case 'gif':
  4153       $func = 'imagecreatefromgif';
  4153 			$func = 'imagecreatefromgif';
  4154       break;
  4154 			break;
  4155     case 'xpm':
  4155 		case 'xpm':
  4156       $func = 'imagecreatefromxpm';
  4156 			$func = 'imagecreatefromxpm';
  4157       break;
  4157 			break;
  4158     default:
  4158 		default:
  4159       throw new Exception('Invalid extension of input file.');
  4159 			throw new Exception('Invalid extension of input file.');
  4160   }
  4160 	}
  4161     
  4161 		
  4162   $magick_path = getConfig('imagemagick_path');
  4162 	$magick_path = getConfig('imagemagick_path');
  4163   $can_use_magick = (
  4163 	$can_use_magick = (
  4164       getConfig('enable_imagemagick') == '1' &&
  4164 			getConfig('enable_imagemagick') == '1' &&
  4165       file_exists($magick_path)              &&
  4165 			file_exists($magick_path)              &&
  4166       is_executable($magick_path)
  4166 			is_executable($magick_path)
  4167     );
  4167 		);
  4168   $can_use_gd = (
  4168 	$can_use_gd = (
  4169       function_exists('getimagesize')         &&
  4169 			function_exists('getimagesize')         &&
  4170       function_exists('imagecreatetruecolor') &&
  4170 			function_exists('imagecreatetruecolor') &&
  4171       function_exists('imagecopyresampled')   &&
  4171 			function_exists('imagecopyresampled')   &&
  4172       function_exists($func)
  4172 			function_exists($func)
  4173     );
  4173 		);
  4174   if ( $can_use_magick )
  4174 	if ( $can_use_magick )
  4175   {
  4175 	{
  4176     if ( !preg_match('/^([\/A-z0-9:\. _-]+)$/', $magick_path) )
  4176 		if ( !preg_match('/^([\/A-z0-9:\. _-]+)$/', $magick_path) )
  4177     {
  4177 		{
  4178       die('SECURITY: ImageMagick path is screwy');
  4178 			die('SECURITY: ImageMagick path is screwy');
  4179     }
  4179 		}
  4180     $cmdline = "$magick_path $in_file_sh -resize \"{$width}x{$height}>\" $out_file_sh";
  4180 		$cmdline = "$magick_path $in_file_sh -resize \"{$width}x{$height}>\" $out_file_sh";
  4181     system($cmdline, $return);
  4181 		system($cmdline, $return);
  4182     if ( !file_exists($out_file) )
  4182 		if ( !file_exists($out_file) )
  4183       throw new Exception('ImageMagick: did not produce output image file.');
  4183 			throw new Exception('ImageMagick: did not produce output image file.');
  4184     return true;
  4184 		return true;
  4185   }
  4185 	}
  4186   else if ( $can_use_gd )
  4186 	else if ( $can_use_gd )
  4187   {
  4187 	{
  4188     @list($width_orig, $height_orig) = @getimagesize($in_file);
  4188 		@list($width_orig, $height_orig) = @getimagesize($in_file);
  4189     if ( !$width_orig || !$height_orig )
  4189 		if ( !$width_orig || !$height_orig )
  4190       throw new Exception('GD: Could not get height and width of input file.');
  4190 			throw new Exception('GD: Could not get height and width of input file.');
  4191     // calculate new width and height
  4191 		// calculate new width and height
  4192     
  4192 		
  4193     $ratio = $width_orig / $height_orig;
  4193 		$ratio = $width_orig / $height_orig;
  4194     if ( $ratio > 1 )
  4194 		if ( $ratio > 1 )
  4195     {
  4195 		{
  4196       // orig. width is greater that height
  4196 			// orig. width is greater that height
  4197       $new_width = $width;
  4197 			$new_width = $width;
  4198       $new_height = round( $width / $ratio );
  4198 			$new_height = round( $width / $ratio );
  4199     }
  4199 		}
  4200     else if ( $ratio < 1 )
  4200 		else if ( $ratio < 1 )
  4201     {
  4201 		{
  4202       // orig. height is greater than width
  4202 			// orig. height is greater than width
  4203       $new_width = round( $height / $ratio );
  4203 			$new_width = round( $height / $ratio );
  4204       $new_height = $height;
  4204 			$new_height = $height;
  4205     }
  4205 		}
  4206     else if ( $ratio == 1 )
  4206 		else if ( $ratio == 1 )
  4207     {
  4207 		{
  4208       $new_width = $width;
  4208 			$new_width = $width;
  4209       $new_height = $width;
  4209 			$new_height = $width;
  4210     }
  4210 		}
  4211     if ( $new_width > $width_orig || $new_height > $height_orig )
  4211 		if ( $new_width > $width_orig || $new_height > $height_orig )
  4212     {
  4212 		{
  4213       // Too big for our britches here; set it to only convert the file
  4213 			// Too big for our britches here; set it to only convert the file
  4214       $new_width = $width_orig;
  4214 			$new_width = $width_orig;
  4215       $new_height = $height_orig;
  4215 			$new_height = $height_orig;
  4216     }
  4216 		}
  4217     
  4217 		
  4218     $newimage = @imagecreatetruecolor($new_width, $new_height);
  4218 		$newimage = @imagecreatetruecolor($new_width, $new_height);
  4219     if ( !$newimage )
  4219 		if ( !$newimage )
  4220       throw new Exception('GD: Request to create new truecolor image refused.');
  4220 			throw new Exception('GD: Request to create new truecolor image refused.');
  4221     $oldimage = @$func($in_file);
  4221 		$oldimage = @$func($in_file);
  4222     if ( !$oldimage )
  4222 		if ( !$oldimage )
  4223       throw new Exception('GD: Request to load input image file failed.');
  4223 			throw new Exception('GD: Request to load input image file failed.');
  4224     
  4224 		
  4225     // Perform scaling
  4225 		// Perform scaling
  4226     imagecopyresampled($newimage, $oldimage, 0, 0, 0, 0, $new_width, $new_height, $width_orig, $height_orig);
  4226 		imagecopyresampled($newimage, $oldimage, 0, 0, 0, 0, $new_width, $new_height, $width_orig, $height_orig);
  4227     
  4227 		
  4228     // Get output format
  4228 		// Get output format
  4229     $out_ext = strtolower(substr($out_file, ( strrpos($out_file, '.') + 1)));
  4229 		$out_ext = strtolower(substr($out_file, ( strrpos($out_file, '.') + 1)));
  4230     switch($out_ext)
  4230 		switch($out_ext)
  4231     {
  4231 		{
  4232       case 'png':
  4232 			case 'png':
  4233         $outfunc = 'imagepng';
  4233 				$outfunc = 'imagepng';
  4234         break;
  4234 				break;
  4235       case 'jpg':
  4235 			case 'jpg':
  4236       case 'jpeg':
  4236 			case 'jpeg':
  4237         $outfunc = 'imagejpeg';
  4237 				$outfunc = 'imagejpeg';
  4238         break;
  4238 				break;
  4239       case 'gif':
  4239 			case 'gif':
  4240         $outfunc = 'imagegif';
  4240 				$outfunc = 'imagegif';
  4241         break;
  4241 				break;
  4242       case 'xpm':
  4242 			case 'xpm':
  4243         $outfunc = 'imagexpm';
  4243 				$outfunc = 'imagexpm';
  4244         break;
  4244 				break;
  4245       default:
  4245 			default:
  4246         imagedestroy($newimage);
  4246 				imagedestroy($newimage);
  4247         imagedestroy($oldimage);
  4247 				imagedestroy($oldimage);
  4248         throw new Exception('GD: Invalid extension of output file.');
  4248 				throw new Exception('GD: Invalid extension of output file.');
  4249     }
  4249 		}
  4250     
  4250 		
  4251     // Write output
  4251 		// Write output
  4252     $outfunc($newimage, $out_file);
  4252 		$outfunc($newimage, $out_file);
  4253     
  4253 		
  4254     // clean up
  4254 		// clean up
  4255     imagedestroy($newimage);
  4255 		imagedestroy($newimage);
  4256     imagedestroy($oldimage);
  4256 		imagedestroy($oldimage);
  4257     
  4257 		
  4258     // done!
  4258 		// done!
  4259     return true;
  4259 		return true;
  4260   }
  4260 	}
  4261   // Neither scaling method worked; we'll let plugins try to scale it, and then if the file still doesn't exist, die
  4261 	// Neither scaling method worked; we'll let plugins try to scale it, and then if the file still doesn't exist, die
  4262   $code = $plugins->setHook('scale_image_failure');
  4262 	$code = $plugins->setHook('scale_image_failure');
  4263   foreach ( $code as $cmd )
  4263 	foreach ( $code as $cmd )
  4264   {
  4264 	{
  4265     eval($cmd);
  4265 		eval($cmd);
  4266   }
  4266 	}
  4267   if ( file_exists($out_file) )
  4267 	if ( file_exists($out_file) )
  4268     return true;
  4268 		return true;
  4269   
  4269 	
  4270   throw new Exception('Failed to find an appropriate method for scaling.');
  4270 	throw new Exception('Failed to find an appropriate method for scaling.');
  4271 }
  4271 }
  4272 
  4272 
  4273 /**
  4273 /**
  4274  * Determines whether a GIF file is animated or not. Credit goes to ZeBadger from <http://www.php.net/imagecreatefromgif>.
  4274  * Determines whether a GIF file is animated or not. Credit goes to ZeBadger from <http://www.php.net/imagecreatefromgif>.
  4275  * Modified to conform to Enano coding standards.
  4275  * Modified to conform to Enano coding standards.
  4278  */
  4278  */
  4279 
  4279 
  4280  
  4280  
  4281 function is_gif_animated($filename)
  4281 function is_gif_animated($filename)
  4282 {
  4282 {
  4283   $filecontents = @file_get_contents($filename);
  4283 	$filecontents = @file_get_contents($filename);
  4284   if ( empty($filecontents) )
  4284 	if ( empty($filecontents) )
  4285     return false;
  4285 		return false;
  4286 
  4286 
  4287   $str_loc = 0;
  4287 	$str_loc = 0;
  4288   $count = 0;
  4288 	$count = 0;
  4289   while ( $count < 2 ) // There is no point in continuing after we find a 2nd frame
  4289 	while ( $count < 2 ) // There is no point in continuing after we find a 2nd frame
  4290   {
  4290 	{
  4291     $where1 = strpos($filecontents,"\x00\x21\xF9\x04", $str_loc);
  4291 		$where1 = strpos($filecontents,"\x00\x21\xF9\x04", $str_loc);
  4292     if ( $where1 === false )
  4292 		if ( $where1 === false )
  4293     {
  4293 		{
  4294       break;
  4294 			break;
  4295     }
  4295 		}
  4296     else
  4296 		else
  4297     {
  4297 		{
  4298       $str_loc = $where1 + 1;
  4298 			$str_loc = $where1 + 1;
  4299       $where2 = strpos($filecontents,"\x00\x2C", $str_loc);
  4299 			$where2 = strpos($filecontents,"\x00\x2C", $str_loc);
  4300       if ( $where2 === false )
  4300 			if ( $where2 === false )
  4301       {
  4301 			{
  4302         break;
  4302 				break;
  4303       }
  4303 			}
  4304       else
  4304 			else
  4305       {
  4305 			{
  4306         if ( $where1 + 8 == $where2 )
  4306 				if ( $where1 + 8 == $where2 )
  4307         {
  4307 				{
  4308           $count++;
  4308 					$count++;
  4309         }
  4309 				}
  4310         $str_loc = $where2 + 1;
  4310 				$str_loc = $where2 + 1;
  4311       }
  4311 			}
  4312     }
  4312 		}
  4313   }
  4313 	}
  4314   
  4314 	
  4315   return ( $count > 1 ) ? true : false;
  4315 	return ( $count > 1 ) ? true : false;
  4316 }
  4316 }
  4317 
  4317 
  4318 /**
  4318 /**
  4319  * Retrieves the dimensions of a GIF image.
  4319  * Retrieves the dimensions of a GIF image.
  4320  * @param string The path to the GIF file.
  4320  * @param string The path to the GIF file.
  4321  * @return array Key 0 is width, key 1 is height
  4321  * @return array Key 0 is width, key 1 is height
  4322  */
  4322  */
  4323 
  4323 
  4324 function gif_get_dimensions($filename)
  4324 function gif_get_dimensions($filename)
  4325 {
  4325 {
  4326   $filecontents = @file_get_contents($filename);
  4326 	$filecontents = @file_get_contents($filename);
  4327   if ( empty($filecontents) )
  4327 	if ( empty($filecontents) )
  4328     return false;
  4328 		return false;
  4329   if ( strlen($filecontents) < 10 )
  4329 	if ( strlen($filecontents) < 10 )
  4330     return false;
  4330 		return false;
  4331   
  4331 	
  4332   $width = substr($filecontents, 6, 2);
  4332 	$width = substr($filecontents, 6, 2);
  4333   $height = substr($filecontents, 8, 2);
  4333 	$height = substr($filecontents, 8, 2);
  4334   $width = unpack('v', $width);
  4334 	$width = unpack('v', $width);
  4335   $height = unpack('v', $height);
  4335 	$height = unpack('v', $height);
  4336   return array($width[1], $height[1]);
  4336 	return array($width[1], $height[1]);
  4337 }
  4337 }
  4338 
  4338 
  4339 /**
  4339 /**
  4340  * Determines whether a PNG image is animated or not. Based on some specification information from <http://wiki.mozilla.org/APNG_Specification>.
  4340  * Determines whether a PNG image is animated or not. Based on some specification information from <http://wiki.mozilla.org/APNG_Specification>.
  4341  * @param string Path to PNG file.
  4341  * @param string Path to PNG file.
  4342  * @return bool If animated, returns true
  4342  * @return bool If animated, returns true
  4343  */
  4343  */
  4344 
  4344 
  4345 function is_png_animated($filename)
  4345 function is_png_animated($filename)
  4346 {
  4346 {
  4347   $filecontents = @file_get_contents($filename);
  4347 	$filecontents = @file_get_contents($filename);
  4348   if ( empty($filecontents) )
  4348 	if ( empty($filecontents) )
  4349     return false;
  4349 		return false;
  4350   
  4350 	
  4351   $parsed = parse_png($filecontents);
  4351 	$parsed = parse_png($filecontents);
  4352   if ( !$parsed )
  4352 	if ( !$parsed )
  4353     return false;
  4353 		return false;
  4354   
  4354 	
  4355   if ( !isset($parsed['fdAT']) )
  4355 	if ( !isset($parsed['fdAT']) )
  4356     return false;
  4356 		return false;
  4357   
  4357 	
  4358   if ( count($parsed['fdAT']) > 1 )
  4358 	if ( count($parsed['fdAT']) > 1 )
  4359     return true;
  4359 		return true;
  4360 }
  4360 }
  4361 
  4361 
  4362 /**
  4362 /**
  4363  * Gets the dimensions of a PNG image.
  4363  * Gets the dimensions of a PNG image.
  4364  * @param string Path to PNG file
  4364  * @param string Path to PNG file
  4365  * @return array Key 0 is width, key 1 is length.
  4365  * @return array Key 0 is width, key 1 is length.
  4366  */
  4366  */
  4367 
  4367 
  4368 function png_get_dimensions($filename)
  4368 function png_get_dimensions($filename)
  4369 {
  4369 {
  4370   $filecontents = @file_get_contents($filename);
  4370 	$filecontents = @file_get_contents($filename);
  4371   if ( empty($filecontents) )
  4371 	if ( empty($filecontents) )
  4372     return false;
  4372 		return false;
  4373   
  4373 	
  4374   $parsed = parse_png($filecontents);
  4374 	$parsed = parse_png($filecontents);
  4375   if ( !$parsed )
  4375 	if ( !$parsed )
  4376     return false;
  4376 		return false;
  4377   
  4377 	
  4378   $ihdr_stream = $parsed['IHDR'][0];
  4378 	$ihdr_stream = $parsed['IHDR'][0];
  4379   $width = substr($ihdr_stream, 0, 4);
  4379 	$width = substr($ihdr_stream, 0, 4);
  4380   $height = substr($ihdr_stream, 4, 4);
  4380 	$height = substr($ihdr_stream, 4, 4);
  4381   $width = unpack('N', $width);
  4381 	$width = unpack('N', $width);
  4382   $height = unpack('N', $height);
  4382 	$height = unpack('N', $height);
  4383   $x = $width[1];
  4383 	$x = $width[1];
  4384   $y = $height[1];
  4384 	$y = $height[1];
  4385   return array($x, $y);
  4385 	return array($x, $y);
  4386 }
  4386 }
  4387 
  4387 
  4388 /**
  4388 /**
  4389  * Internal function to parse out the streams of a PNG file. Based on the W3 PNG spec: http://www.w3.org/TR/PNG/
  4389  * Internal function to parse out the streams of a PNG file. Based on the W3 PNG spec: http://www.w3.org/TR/PNG/
  4390  * @param string The contents of the PNG
  4390  * @param string The contents of the PNG
  4391  * @return array Associative array containing the streams
  4391  * @return array Associative array containing the streams
  4392  */
  4392  */
  4393 
  4393 
  4394 function parse_png($data)
  4394 function parse_png($data)
  4395 {
  4395 {
  4396   // Trim off first 8 bytes to check for PNG header
  4396 	// Trim off first 8 bytes to check for PNG header
  4397   $header = substr($data, 0, 8);
  4397 	$header = substr($data, 0, 8);
  4398   if ( $header != "\x89\x50\x4e\x47\x0d\x0a\x1a\x0a" )
  4398 	if ( $header != "\x89\x50\x4e\x47\x0d\x0a\x1a\x0a" )
  4399   {
  4399 	{
  4400     return false;
  4400 		return false;
  4401   }
  4401 	}
  4402   $return = array();
  4402 	$return = array();
  4403   $data = substr($data, 8);
  4403 	$data = substr($data, 8);
  4404   while ( strlen($data) > 0 )
  4404 	while ( strlen($data) > 0 )
  4405   {
  4405 	{
  4406     $chunklen_bin = substr($data, 0, 4);
  4406 		$chunklen_bin = substr($data, 0, 4);
  4407     $chunk_type = substr($data, 4, 4);
  4407 		$chunk_type = substr($data, 4, 4);
  4408     $chunklen = unpack('N', $chunklen_bin);
  4408 		$chunklen = unpack('N', $chunklen_bin);
  4409     $chunklen = $chunklen[1];
  4409 		$chunklen = $chunklen[1];
  4410     $chunk_data = substr($data, 8, $chunklen);
  4410 		$chunk_data = substr($data, 8, $chunklen);
  4411     
  4411 		
  4412     // If the chunk type is not valid, this may be a malicious PNG with bad offsets. Break out of the loop.
  4412 		// If the chunk type is not valid, this may be a malicious PNG with bad offsets. Break out of the loop.
  4413     if ( !preg_match('/^[A-z]{4}$/', $chunk_type) )
  4413 		if ( !preg_match('/^[A-z]{4}$/', $chunk_type) )
  4414       break;
  4414 			break;
  4415     
  4415 		
  4416     if ( !isset($return[$chunk_type]) )
  4416 		if ( !isset($return[$chunk_type]) )
  4417       $return[$chunk_type] = array();
  4417 			$return[$chunk_type] = array();
  4418     $return[$chunk_type][] = $chunk_data;
  4418 		$return[$chunk_type][] = $chunk_data;
  4419     
  4419 		
  4420     $offset_next = 4 // Length
  4420 		$offset_next = 4 // Length
  4421                  + 4 // Type
  4421  								+ 4 // Type
  4422                  + $chunklen // Data
  4422  								+ $chunklen // Data
  4423                  + 4; // CRC
  4423  								+ 4; // CRC
  4424     $data = substr($data, $offset_next);
  4424 		$data = substr($data, $offset_next);
  4425   }
  4425 	}
  4426   return $return;
  4426 	return $return;
  4427 }
  4427 }
  4428 
  4428 
  4429 /**
  4429 /**
  4430  * Retreives information about the intrinsic characteristics of the jpeg image, such as Bits per Component, Height and Width. This function is from the PHP JPEG Metadata Toolkit. Licensed under the GPLv2 or later.
  4430  * Retreives information about the intrinsic characteristics of the jpeg image, such as Bits per Component, Height and Width. This function is from the PHP JPEG Metadata Toolkit. Licensed under the GPLv2 or later.
  4431  * @param array The JPEG header data, as retrieved from the get_jpeg_header_data function
  4431  * @param array The JPEG header data, as retrieved from the get_jpeg_header_data function
  4434  * @copyright Copyright Evan Hunter 2004 
  4434  * @copyright Copyright Evan Hunter 2004 
  4435  */
  4435  */
  4436 
  4436 
  4437 function get_jpeg_intrinsic_values( $jpeg_header_data )
  4437 function get_jpeg_intrinsic_values( $jpeg_header_data )
  4438 {
  4438 {
  4439   // Create a blank array for the output
  4439 	// Create a blank array for the output
  4440   $Outputarray = array( );
  4440 	$Outputarray = array( );
  4441 
  4441 
  4442   //Cycle through the header segments until Start Of Frame (SOF) is found or we run out of segments
  4442 	//Cycle through the header segments until Start Of Frame (SOF) is found or we run out of segments
  4443   $i = 0;
  4443 	$i = 0;
  4444   while ( ( $i < count( $jpeg_header_data) )  && ( substr( $jpeg_header_data[$i]['SegName'], 0, 3 ) != "SOF" ) )
  4444 	while ( ( $i < count( $jpeg_header_data) )  && ( substr( $jpeg_header_data[$i]['SegName'], 0, 3 ) != "SOF" ) )
  4445   {
  4445 	{
  4446     $i++;
  4446 		$i++;
  4447   }
  4447 	}
  4448 
  4448 
  4449   // Check if a SOF segment has been found
  4449 	// Check if a SOF segment has been found
  4450   if ( substr( $jpeg_header_data[$i]['SegName'], 0, 3 ) == "SOF" )
  4450 	if ( substr( $jpeg_header_data[$i]['SegName'], 0, 3 ) == "SOF" )
  4451   {
  4451 	{
  4452     // SOF segment was found, extract the information
  4452 		// SOF segment was found, extract the information
  4453 
  4453 
  4454     $data = $jpeg_header_data[$i]['SegData'];
  4454 		$data = $jpeg_header_data[$i]['SegData'];
  4455 
  4455 
  4456     // First byte is Bits per component
  4456 		// First byte is Bits per component
  4457     $Outputarray['Bits per Component'] = ord( $data{0} );
  4457 		$Outputarray['Bits per Component'] = ord( $data{0} );
  4458 
  4458 
  4459     // Second and third bytes are Image Height
  4459 		// Second and third bytes are Image Height
  4460     $Outputarray['Image Height'] = ord( $data{ 1 } ) * 256 + ord( $data{ 2 } );
  4460 		$Outputarray['Image Height'] = ord( $data{ 1 } ) * 256 + ord( $data{ 2 } );
  4461 
  4461 
  4462     // Forth and fifth bytes are Image Width
  4462 		// Forth and fifth bytes are Image Width
  4463     $Outputarray['Image Width'] = ord( $data{ 3 } ) * 256 + ord( $data{ 4 } );
  4463 		$Outputarray['Image Width'] = ord( $data{ 3 } ) * 256 + ord( $data{ 4 } );
  4464 
  4464 
  4465     // Sixth byte is number of components
  4465 		// Sixth byte is number of components
  4466     $numcomponents = ord( $data{ 5 } );
  4466 		$numcomponents = ord( $data{ 5 } );
  4467 
  4467 
  4468     // Following this is a table containing information about the components
  4468 		// Following this is a table containing information about the components
  4469     for( $i = 0; $i < $numcomponents; $i++ )
  4469 		for( $i = 0; $i < $numcomponents; $i++ )
  4470     {
  4470 		{
  4471       $Outputarray['Components'][] = array (  'Component Identifier' => ord( $data{ 6 + $i * 3 } ),
  4471 			$Outputarray['Components'][] = array (  'Component Identifier' => ord( $data{ 6 + $i * 3 } ),
  4472                 'Horizontal Sampling Factor' => ( ord( $data{ 7 + $i * 3 } ) & 0xF0 ) / 16,
  4472 								'Horizontal Sampling Factor' => ( ord( $data{ 7 + $i * 3 } ) & 0xF0 ) / 16,
  4473                 'Vertical Sampling Factor' => ( ord( $data{ 7 + $i * 3 } ) & 0x0F ),
  4473 								'Vertical Sampling Factor' => ( ord( $data{ 7 + $i * 3 } ) & 0x0F ),
  4474                 'Quantization table destination selector' => ord( $data{ 8 + $i * 3 } ) );
  4474 								'Quantization table destination selector' => ord( $data{ 8 + $i * 3 } ) );
  4475     }
  4475 		}
  4476   }
  4476 	}
  4477   else
  4477 	else
  4478   {
  4478 	{
  4479     // Couldn't find Start Of Frame segment, hence can't retrieve info
  4479 		// Couldn't find Start Of Frame segment, hence can't retrieve info
  4480     return FALSE;
  4480 		return FALSE;
  4481   }
  4481 	}
  4482 
  4482 
  4483   return $Outputarray;
  4483 	return $Outputarray;
  4484 }
  4484 }
  4485 
  4485 
  4486 /**
  4486 /**
  4487  * Reads all the JPEG header segments from an JPEG image file into an array. This function is from the PHP JPEG Metadata Toolkit. Licensed under the GPLv2 or later. Modified slightly for Enano coding standards and to remove unneeded capability.
  4487  * Reads all the JPEG header segments from an JPEG image file into an array. This function is from the PHP JPEG Metadata Toolkit. Licensed under the GPLv2 or later. Modified slightly for Enano coding standards and to remove unneeded capability.
  4488  * @param string the filename of the file to JPEG file to read
  4488  * @param string the filename of the file to JPEG file to read
  4491  * @copyright Copyright Evan Hunter 2004
  4491  * @copyright Copyright Evan Hunter 2004
  4492  */
  4492  */
  4493 
  4493 
  4494 function get_jpeg_header_data( $filename )
  4494 function get_jpeg_header_data( $filename )
  4495 {
  4495 {
  4496   // Attempt to open the jpeg file - the at symbol supresses the error message about
  4496 	// Attempt to open the jpeg file - the at symbol supresses the error message about
  4497   // not being able to open files. The file_exists would have been used, but it
  4497 	// not being able to open files. The file_exists would have been used, but it
  4498   // does not work with files fetched over http or ftp.
  4498 	// does not work with files fetched over http or ftp.
  4499   $filehnd = @fopen($filename, 'rb');
  4499 	$filehnd = @fopen($filename, 'rb');
  4500 
  4500 
  4501   // Check if the file opened successfully
  4501 	// Check if the file opened successfully
  4502   if ( ! $filehnd  )
  4502 	if ( ! $filehnd  )
  4503   {
  4503 	{
  4504     // Could't open the file - exit
  4504 		// Could't open the file - exit
  4505     return FALSE;
  4505 		return FALSE;
  4506   }
  4506 	}
  4507 
  4507 
  4508 
  4508 
  4509   // Read the first two characters
  4509 	// Read the first two characters
  4510   $data = fread( $filehnd, 2 );
  4510 	$data = fread( $filehnd, 2 );
  4511 
  4511 
  4512   // Check that the first two characters are 0xFF 0xDA  (SOI - Start of image)
  4512 	// Check that the first two characters are 0xFF 0xDA  (SOI - Start of image)
  4513   if ( $data != "\xFF\xD8" )
  4513 	if ( $data != "\xFF\xD8" )
  4514   {
  4514 	{
  4515     // No SOI (FF D8) at start of file - This probably isn't a JPEG file - close file and return;
  4515 		// No SOI (FF D8) at start of file - This probably isn't a JPEG file - close file and return;
  4516     fclose($filehnd);
  4516 		fclose($filehnd);
  4517     return FALSE;
  4517 		return FALSE;
  4518   }
  4518 	}
  4519 
  4519 
  4520 
  4520 
  4521   // Read the third character
  4521 	// Read the third character
  4522   $data = fread( $filehnd, 2 );
  4522 	$data = fread( $filehnd, 2 );
  4523 
  4523 
  4524   // Check that the third character is 0xFF (Start of first segment header)
  4524 	// Check that the third character is 0xFF (Start of first segment header)
  4525   if ( $data{0} != "\xFF" )
  4525 	if ( $data{0} != "\xFF" )
  4526   {
  4526 	{
  4527     // NO FF found - close file and return - JPEG is probably corrupted
  4527 		// NO FF found - close file and return - JPEG is probably corrupted
  4528     fclose($filehnd);
  4528 		fclose($filehnd);
  4529     return FALSE;
  4529 		return FALSE;
  4530   }
  4530 	}
  4531 
  4531 
  4532   // Flag that we havent yet hit the compressed image data
  4532 	// Flag that we havent yet hit the compressed image data
  4533   $hit_compressed_image_data = FALSE;
  4533 	$hit_compressed_image_data = FALSE;
  4534 
  4534 
  4535 
  4535 
  4536   // Cycle through the file until, one of: 1) an EOI (End of image) marker is hit,
  4536 	// Cycle through the file until, one of: 1) an EOI (End of image) marker is hit,
  4537   //               2) we have hit the compressed image data (no more headers are allowed after data)
  4537 	//               2) we have hit the compressed image data (no more headers are allowed after data)
  4538   //               3) or end of file is hit
  4538 	//               3) or end of file is hit
  4539 
  4539 
  4540   while ( ( $data{1} != "\xD9" ) && (! $hit_compressed_image_data) && ( ! feof( $filehnd ) ))
  4540 	while ( ( $data{1} != "\xD9" ) && (! $hit_compressed_image_data) && ( ! feof( $filehnd ) ))
  4541   {
  4541 	{
  4542     // Found a segment to look at.
  4542 		// Found a segment to look at.
  4543     // Check that the segment marker is not a Restart marker - restart markers don't have size or data after them
  4543 		// Check that the segment marker is not a Restart marker - restart markers don't have size or data after them
  4544     if (  ( ord($data{1}) < 0xD0 ) || ( ord($data{1}) > 0xD7 ) )
  4544 		if (  ( ord($data{1}) < 0xD0 ) || ( ord($data{1}) > 0xD7 ) )
  4545     {
  4545 		{
  4546       // Segment isn't a Restart marker
  4546 			// Segment isn't a Restart marker
  4547       // Read the next two bytes (size)
  4547 			// Read the next two bytes (size)
  4548       $sizestr = fread( $filehnd, 2 );
  4548 			$sizestr = fread( $filehnd, 2 );
  4549 
  4549 
  4550       // convert the size bytes to an integer
  4550 			// convert the size bytes to an integer
  4551       $decodedsize = unpack ("nsize", $sizestr);
  4551 			$decodedsize = unpack ("nsize", $sizestr);
  4552 
  4552 
  4553       // Save the start position of the data
  4553 			// Save the start position of the data
  4554       $segdatastart = ftell( $filehnd );
  4554 			$segdatastart = ftell( $filehnd );
  4555 
  4555 
  4556       // Read the segment data with length indicated by the previously read size
  4556 			// Read the segment data with length indicated by the previously read size
  4557       $segdata = fread( $filehnd, $decodedsize['size'] - 2 );
  4557 			$segdata = fread( $filehnd, $decodedsize['size'] - 2 );
  4558 
  4558 
  4559 
  4559 
  4560       // Store the segment information in the output array
  4560 			// Store the segment information in the output array
  4561       $headerdata[] = array(  "SegType" => ord($data{1}),
  4561 			$headerdata[] = array(  "SegType" => ord($data{1}),
  4562             "SegName" => $GLOBALS[ "JPEG_Segment_Names" ][ ord($data{1}) ],
  4562 						"SegName" => $GLOBALS[ "JPEG_Segment_Names" ][ ord($data{1}) ],
  4563             "SegDataStart" => $segdatastart,
  4563 						"SegDataStart" => $segdatastart,
  4564             "SegData" => $segdata );
  4564 						"SegData" => $segdata );
  4565     }
  4565 		}
  4566 
  4566 
  4567     // If this is a SOS (Start Of Scan) segment, then there is no more header data - the compressed image data follows
  4567 		// If this is a SOS (Start Of Scan) segment, then there is no more header data - the compressed image data follows
  4568     if ( $data{1} == "\xDA" )
  4568 		if ( $data{1} == "\xDA" )
  4569     {
  4569 		{
  4570       // Flag that we have hit the compressed image data - exit loop as no more headers available.
  4570 			// Flag that we have hit the compressed image data - exit loop as no more headers available.
  4571       $hit_compressed_image_data = TRUE;
  4571 			$hit_compressed_image_data = TRUE;
  4572     }
  4572 		}
  4573     else
  4573 		else
  4574     {
  4574 		{
  4575       // Not an SOS - Read the next two bytes - should be the segment marker for the next segment
  4575 			// Not an SOS - Read the next two bytes - should be the segment marker for the next segment
  4576       $data = fread( $filehnd, 2 );
  4576 			$data = fread( $filehnd, 2 );
  4577 
  4577 
  4578       // Check that the first byte of the two is 0xFF as it should be for a marker
  4578 			// Check that the first byte of the two is 0xFF as it should be for a marker
  4579       if ( $data{0} != "\xFF" )
  4579 			if ( $data{0} != "\xFF" )
  4580       {
  4580 			{
  4581         // NO FF found - close file and return - JPEG is probably corrupted
  4581 				// NO FF found - close file and return - JPEG is probably corrupted
  4582         fclose($filehnd);
  4582 				fclose($filehnd);
  4583         return FALSE;
  4583 				return FALSE;
  4584       }
  4584 			}
  4585     }
  4585 		}
  4586   }
  4586 	}
  4587 
  4587 
  4588   // Close File
  4588 	// Close File
  4589   fclose($filehnd);
  4589 	fclose($filehnd);
  4590 
  4590 
  4591   // Return the header data retrieved
  4591 	// Return the header data retrieved
  4592   return $headerdata;
  4592 	return $headerdata;
  4593 }
  4593 }
  4594 
  4594 
  4595 /**
  4595 /**
  4596  * Returns the dimensions of a JPEG image in the same format as {php,gif}_get_dimensions().
  4596  * Returns the dimensions of a JPEG image in the same format as {php,gif}_get_dimensions().
  4597  * @param string JPEG file to check
  4597  * @param string JPEG file to check
  4598  * @return array Key 0 is width, key 1 is height
  4598  * @return array Key 0 is width, key 1 is height
  4599  */
  4599  */
  4600 
  4600 
  4601 function jpg_get_dimensions($filename)
  4601 function jpg_get_dimensions($filename)
  4602 {
  4602 {
  4603   if ( !file_exists($filename) )
  4603 	if ( !file_exists($filename) )
  4604   {
  4604 	{
  4605     echo "Doesn't exist<br />";
  4605 		echo "Doesn't exist<br />";
  4606     return false;
  4606 		return false;
  4607   }
  4607 	}
  4608   
  4608 	
  4609   $headers = get_jpeg_header_data($filename);
  4609 	$headers = get_jpeg_header_data($filename);
  4610   if ( !$headers )
  4610 	if ( !$headers )
  4611   {
  4611 	{
  4612     echo "Bad headers<br />";
  4612 		echo "Bad headers<br />";
  4613     return false;
  4613 		return false;
  4614   }
  4614 	}
  4615   
  4615 	
  4616   $metadata = get_jpeg_intrinsic_values($headers);
  4616 	$metadata = get_jpeg_intrinsic_values($headers);
  4617   if ( !$metadata )
  4617 	if ( !$metadata )
  4618   {
  4618 	{
  4619     echo "Bad metadata: <pre>" . print_r($metadata, true) . "</pre><br />";
  4619 		echo "Bad metadata: <pre>" . print_r($metadata, true) . "</pre><br />";
  4620     return false;
  4620 		return false;
  4621   }
  4621 	}
  4622   
  4622 	
  4623   if ( !isset($metadata['Image Width']) || !isset($metadata['Image Height']) )
  4623 	if ( !isset($metadata['Image Width']) || !isset($metadata['Image Height']) )
  4624   {
  4624 	{
  4625     echo "No metadata<br />";
  4625 		echo "No metadata<br />";
  4626     return false;
  4626 		return false;
  4627   }
  4627 	}
  4628   
  4628 	
  4629   return array($metadata['Image Width'], $metadata['Image Height']);
  4629 	return array($metadata['Image Width'], $metadata['Image Height']);
  4630 }
  4630 }
  4631 
  4631 
  4632 /**
  4632 /**
  4633  * Generates a URL for the avatar for the given user ID and avatar type.
  4633  * Generates a URL for the avatar for the given user ID and avatar type.
  4634  * @param int User ID
  4634  * @param int User ID
  4637  * @return string
  4637  * @return string
  4638  */
  4638  */
  4639 
  4639 
  4640 function make_avatar_url($user_id, $avi_type, $user_email = false)
  4640 function make_avatar_url($user_id, $avi_type, $user_email = false)
  4641 {
  4641 {
  4642   static $img_types = array(
  4642 	static $img_types = array(
  4643       'png' => IMAGE_TYPE_PNG,
  4643 			'png' => IMAGE_TYPE_PNG,
  4644       'gif' => IMAGE_TYPE_GIF,
  4644 			'gif' => IMAGE_TYPE_GIF,
  4645       'jpg' => IMAGE_TYPE_JPG,
  4645 			'jpg' => IMAGE_TYPE_JPG,
  4646       'grv' => IMAGE_TYPE_GRV
  4646 			'grv' => IMAGE_TYPE_GRV
  4647     );
  4647 		);
  4648   
  4648 	
  4649   if ( !is_int($user_id) )
  4649 	if ( !is_int($user_id) )
  4650     return false;
  4650 		return false;
  4651   if ( !isset($img_types[$avi_type]) )
  4651 	if ( !isset($img_types[$avi_type]) )
  4652     return false;
  4652 		return false;
  4653   
  4653 	
  4654   if ( $avi_type == 'grv' )
  4654 	if ( $avi_type == 'grv' )
  4655   {
  4655 	{
  4656     if ( $user_email )
  4656 		if ( $user_email )
  4657     {
  4657 		{
  4658       return make_gravatar_url($user_email);
  4658 			return make_gravatar_url($user_email);
  4659     }
  4659 		}
  4660   }
  4660 	}
  4661   else
  4661 	else
  4662   {
  4662 	{
  4663     $avi_relative_path = '/' . getConfig('avatar_directory') . '/' . $user_id . '.' . $avi_type;
  4663 		$avi_relative_path = '/' . getConfig('avatar_directory') . '/' . $user_id . '.' . $avi_type;
  4664     if ( !file_exists(ENANO_ROOT . $avi_relative_path) )
  4664 		if ( !file_exists(ENANO_ROOT . $avi_relative_path) )
  4665     {
  4665 		{
  4666       return '';
  4666 			return '';
  4667     }
  4667 		}
  4668   }
  4668 	}
  4669   
  4669 	
  4670   $img_type = $img_types[$avi_type];
  4670 	$img_type = $img_types[$avi_type];
  4671   
  4671 	
  4672   $dateline = @filemtime(ENANO_ROOT . $avi_relative_path);
  4672 	$dateline = @filemtime(ENANO_ROOT . $avi_relative_path);
  4673   $avi_id = pack('VVv', $dateline, $user_id, $img_type);
  4673 	$avi_id = pack('VVv', $dateline, $user_id, $img_type);
  4674   $avi_id = hexencode($avi_id, '', '');
  4674 	$avi_id = hexencode($avi_id, '', '');
  4675     
  4675 		
  4676   // return scriptPath . $avi_relative_path;
  4676 	// return scriptPath . $avi_relative_path;
  4677   return makeUrlNS('Special', "Avatar/$avi_id");
  4677 	return makeUrlNS('Special', "Avatar/$avi_id");
  4678 }
  4678 }
  4679 
  4679 
  4680 /**
  4680 /**
  4681  * Generates a URL to the Gravatar for a user based on his/her e-mail address.
  4681  * Generates a URL to the Gravatar for a user based on his/her e-mail address.
  4682  * @param string E-mail address
  4682  * @param string E-mail address
  4684  * @return string URL
  4684  * @return string URL
  4685  */
  4685  */
  4686 
  4686 
  4687 function make_gravatar_url($email, $size = false)
  4687 function make_gravatar_url($email, $size = false)
  4688 {
  4688 {
  4689   $email = md5($email);
  4689 	$email = md5($email);
  4690   
  4690 	
  4691   // gravatar parameters
  4691 	// gravatar parameters
  4692   if ( $size )
  4692 	if ( $size )
  4693   {
  4693 	{
  4694     $max_size = intval($size);
  4694 		$max_size = intval($size);
  4695   }
  4695 	}
  4696   else
  4696 	else
  4697   {
  4697 	{
  4698     $max_x = intval(getConfig('avatar_max_width', '150'));
  4698 		$max_x = intval(getConfig('avatar_max_width', '150'));
  4699     $max_y = intval(getConfig('avatar_max_height', '150'));
  4699 		$max_y = intval(getConfig('avatar_max_height', '150'));
  4700     // ?s=
  4700 		// ?s=
  4701     $max_size = ( $max_x > $max_y ) ? $max_y : $max_x;
  4701 		$max_size = ( $max_x > $max_y ) ? $max_y : $max_x;
  4702   }
  4702 	}
  4703   
  4703 	
  4704   // ?r=
  4704 	// ?r=
  4705   $rating = getConfig('gravatar_rating', 'g');
  4705 	$rating = getConfig('gravatar_rating', 'g');
  4706   
  4706 	
  4707   // final URL
  4707 	// final URL
  4708   $url = "http://www.gravatar.com/avatar/$email?r=$rating&s=$max_size";
  4708 	$url = "http://www.gravatar.com/avatar/$email?r=$rating&s=$max_size";
  4709   
  4709 	
  4710   return $url;
  4710 	return $url;
  4711 }
  4711 }
  4712 
  4712 
  4713 /**
  4713 /**
  4714  * Determines an image's filetype based on its signature.
  4714  * Determines an image's filetype based on its signature.
  4715  * @param string Path to image file
  4715  * @param string Path to image file
  4716  * @return string One of gif, png, or jpg, or false if none of these.
  4716  * @return string One of gif, png, or jpg, or false if none of these.
  4717  */
  4717  */
  4718 
  4718 
  4719 function get_image_filetype($filename)
  4719 function get_image_filetype($filename)
  4720 {
  4720 {
  4721   $filecontents = @file_get_contents($filename);
  4721 	$filecontents = @file_get_contents($filename);
  4722   if ( empty($filecontents) )
  4722 	if ( empty($filecontents) )
  4723     return false;
  4723 		return false;
  4724   
  4724 	
  4725   if ( substr($filecontents, 0, 8) == "\x89\x50\x4e\x47\x0d\x0a\x1a\x0a" )
  4725 	if ( substr($filecontents, 0, 8) == "\x89\x50\x4e\x47\x0d\x0a\x1a\x0a" )
  4726     return 'png';
  4726 		return 'png';
  4727   
  4727 	
  4728   if ( substr($filecontents, 0, 6) == 'GIF87a' || substr($filecontents, 0, 6) == 'GIF89a' )
  4728 	if ( substr($filecontents, 0, 6) == 'GIF87a' || substr($filecontents, 0, 6) == 'GIF89a' )
  4729     return 'gif';
  4729 		return 'gif';
  4730   
  4730 	
  4731   if ( substr($filecontents, 0, 2) == "\xFF\xD8" )
  4731 	if ( substr($filecontents, 0, 2) == "\xFF\xD8" )
  4732     return 'jpg';
  4732 		return 'jpg';
  4733   
  4733 	
  4734   return false;
  4734 	return false;
  4735 }
  4735 }
  4736 
  4736 
  4737 /**
  4737 /**
  4738  * Generates a JSON encoder/decoder singleton.
  4738  * Generates a JSON encoder/decoder singleton.
  4739  * @return object
  4739  * @return object
  4740  */
  4740  */
  4741 
  4741 
  4742 function enano_json_singleton()
  4742 function enano_json_singleton()
  4743 {
  4743 {
  4744   static $json_obj;
  4744 	static $json_obj;
  4745   if ( !is_object($json_obj) )
  4745 	if ( !is_object($json_obj) )
  4746     $json_obj = new Services_JSON(SERVICES_JSON_LOOSE_TYPE | SERVICES_JSON_SUPPRESS_ERRORS);
  4746 		$json_obj = new Services_JSON(SERVICES_JSON_LOOSE_TYPE | SERVICES_JSON_SUPPRESS_ERRORS);
  4747   
  4747 	
  4748   return $json_obj;
  4748 	return $json_obj;
  4749 }
  4749 }
  4750 
  4750 
  4751 /**
  4751 /**
  4752  * Wrapper for JSON encoding.
  4752  * Wrapper for JSON encoding.
  4753  * @param mixed Variable to encode
  4753  * @param mixed Variable to encode
  4754  * @return string JSON-encoded string
  4754  * @return string JSON-encoded string
  4755  */
  4755  */
  4756 
  4756 
  4757 function enano_json_encode($data)
  4757 function enano_json_encode($data)
  4758 {
  4758 {
  4759   /*
  4759 	/*
  4760   if ( function_exists('json_encode') )
  4760 	if ( function_exists('json_encode') )
  4761   {
  4761 	{
  4762     // using PHP5 with JSON support
  4762 		// using PHP5 with JSON support
  4763     return json_encode($data);
  4763 		return json_encode($data);
  4764   }
  4764 	}
  4765   */
  4765 	*/
  4766   
  4766 	
  4767   return Zend_Json::encode($data, true);
  4767 	return Zend_Json::encode($data, true);
  4768 }
  4768 }
  4769 
  4769 
  4770 /**
  4770 /**
  4771  * Wrapper for JSON decoding.
  4771  * Wrapper for JSON decoding.
  4772  * @param string JSON-encoded string
  4772  * @param string JSON-encoded string
  4773  * @return mixed Decoded value
  4773  * @return mixed Decoded value
  4774  */
  4774  */
  4775 
  4775 
  4776 function enano_json_decode($data)
  4776 function enano_json_decode($data)
  4777 {
  4777 {
  4778   /*
  4778 	/*
  4779   if ( function_exists('json_decode') )
  4779 	if ( function_exists('json_decode') )
  4780   {
  4780 	{
  4781     // using PHP5 with JSON support
  4781 		// using PHP5 with JSON support
  4782     return json_decode($data);
  4782 		return json_decode($data);
  4783   }
  4783 	}
  4784   */
  4784 	*/
  4785   
  4785 	
  4786   return Zend_Json::decode($data, Zend_Json::TYPE_ARRAY);
  4786 	return Zend_Json::decode($data, Zend_Json::TYPE_ARRAY);
  4787 }
  4787 }
  4788 
  4788 
  4789 /**
  4789 /**
  4790  * Cleans a snippet of JSON for closer standards compliance (shuts up the picky Zend parser)
  4790  * Cleans a snippet of JSON for closer standards compliance (shuts up the picky Zend parser)
  4791  * @param string Dirty JSON
  4791  * @param string Dirty JSON
  4792  * @return string Clean JSON
  4792  * @return string Clean JSON
  4793  */
  4793  */
  4794 
  4794 
  4795 function enano_clean_json($json)
  4795 function enano_clean_json($json)
  4796 {
  4796 {
  4797   // eliminate comments
  4797 	// eliminate comments
  4798   $json = preg_replace(array(
  4798 	$json = preg_replace(array(
  4799           // eliminate single line comments in '// ...' form
  4799 					// eliminate single line comments in '// ...' form
  4800           '#^\s*//(.*)$#m',
  4800 					'#^\s*//(.*)$#m',
  4801           // eliminate multi-line comments in '/* ... */' form, at start of string
  4801 					// eliminate multi-line comments in '/* ... */' form, at start of string
  4802           '#^\s*/\*(.+)\*/#Us',
  4802 					'#^\s*/\*(.+)\*/#Us',
  4803           // eliminate multi-line comments in '/* ... */' form, at end of string
  4803 					// eliminate multi-line comments in '/* ... */' form, at end of string
  4804           '#/\*(.+)\*/\s*$#Us'
  4804 					'#/\*(.+)\*/\s*$#Us'
  4805         ), '', $json);
  4805 				), '', $json);
  4806     
  4806 		
  4807   $json = preg_replace('/([,\{\[])(?:[\r\n]+)([\s]*?)([a-z0-9_]+)([\s]*?):/', '\\1\\2"\\3" :', $json);
  4807 	$json = preg_replace('/([,\{\[])(?:[\r\n]+)([\s]*?)([a-z0-9_]+)([\s]*?):/', '\\1\\2"\\3" :', $json);
  4808   
  4808 	
  4809   return $json;
  4809 	return $json;
  4810 }
  4810 }
  4811 
  4811 
  4812 /**
  4812 /**
  4813  * Trims a snippet of text to the first and last curly braces. Useful for JSON.
  4813  * Trims a snippet of text to the first and last curly braces. Useful for JSON.
  4814  * @param string Text to trim
  4814  * @param string Text to trim
  4815  * @return string
  4815  * @return string
  4816  */
  4816  */
  4817 
  4817 
  4818 function enano_trim_json($json)
  4818 function enano_trim_json($json)
  4819 {
  4819 {
  4820   return preg_replace('/^([^{]+)\{/', '{', preg_replace('/\}([^}]+)$/', '}', $json));
  4820 	return preg_replace('/^([^{]+)\{/', '{', preg_replace('/\}([^}]+)$/', '}', $json));
  4821 }
  4821 }
  4822 
  4822 
  4823 /**
  4823 /**
  4824  * Starts the profiler.
  4824  * Starts the profiler.
  4825  */
  4825  */
  4826 
  4826 
  4827 function profiler_start()
  4827 function profiler_start()
  4828 {
  4828 {
  4829   global $_profiler;
  4829 	global $_profiler;
  4830   $_profiler = array();
  4830 	$_profiler = array();
  4831   
  4831 	
  4832   if ( !defined('ENANO_DEBUG') )
  4832 	if ( !defined('ENANO_DEBUG') )
  4833     return false;
  4833 		return false;
  4834   
  4834 	
  4835   $_profiler[] = array(
  4835 	$_profiler[] = array(
  4836       'point' => 'Profiling started',
  4836 			'point' => 'Profiling started',
  4837       'time' => microtime_float(),
  4837 			'time' => microtime_float(),
  4838       'backtrace' => false,
  4838 			'backtrace' => false,
  4839       'mem' => false
  4839 			'mem' => false
  4840     );
  4840 		);
  4841   if ( function_exists('memory_get_usage') )
  4841 	if ( function_exists('memory_get_usage') )
  4842   {
  4842 	{
  4843     $_profiler[ count($_profiler) - 1 ]['mem'] = memory_get_usage();
  4843 		$_profiler[ count($_profiler) - 1 ]['mem'] = memory_get_usage();
  4844   }
  4844 	}
  4845 }
  4845 }
  4846 
  4846 
  4847 /**
  4847 /**
  4848  * Logs something in the profiler.
  4848  * Logs something in the profiler.
  4849  * @param string Point name or message
  4849  * @param string Point name or message
  4852  * @return resource Event ID
  4852  * @return resource Event ID
  4853  */
  4853  */
  4854 
  4854 
  4855 function profiler_log($point, $allow_backtrace = true, $parent_event = false)
  4855 function profiler_log($point, $allow_backtrace = true, $parent_event = false)
  4856 {
  4856 {
  4857   if ( !defined('ENANO_DEBUG') )
  4857 	if ( !defined('ENANO_DEBUG') )
  4858     return false;
  4858 		return false;
  4859   
  4859 	
  4860   global $_profiler;
  4860 	global $_profiler;
  4861   $backtrace = false;
  4861 	$backtrace = false;
  4862   if ( $allow_backtrace && function_exists('debug_print_backtrace') )
  4862 	if ( $allow_backtrace && function_exists('debug_print_backtrace') )
  4863   {
  4863 	{
  4864     list(, $backtrace) = explode("\n", enano_debug_print_backtrace(true));
  4864 		list(, $backtrace) = explode("\n", enano_debug_print_backtrace(true));
  4865   }
  4865 	}
  4866   $_profiler[] = array(
  4866 	$_profiler[] = array(
  4867       'point' => $point,
  4867 			'point' => $point,
  4868       'time' => microtime_float(),
  4868 			'time' => microtime_float(),
  4869       'backtrace' => $backtrace,
  4869 			'backtrace' => $backtrace,
  4870       'mem' => false,
  4870 			'mem' => false,
  4871       'parent_event' => $parent_event
  4871 			'parent_event' => $parent_event
  4872     );
  4872 		);
  4873   if ( function_exists('memory_get_usage') )
  4873 	if ( function_exists('memory_get_usage') )
  4874   {
  4874 	{
  4875     $_profiler[ count($_profiler) - 1 ]['mem'] = memory_get_usage();
  4875 		$_profiler[ count($_profiler) - 1 ]['mem'] = memory_get_usage();
  4876   }
  4876 	}
  4877   return count($_profiler) - 1;
  4877 	return count($_profiler) - 1;
  4878 }
  4878 }
  4879 
  4879 
  4880 /**
  4880 /**
  4881  * Insert a message (an event without any time data) into the profiler.
  4881  * Insert a message (an event without any time data) into the profiler.
  4882  * @param string Message
  4882  * @param string Message
  4883  */
  4883  */
  4884 
  4884 
  4885 function profiler_message($message)
  4885 function profiler_message($message)
  4886 {
  4886 {
  4887   if ( !defined('ENANO_DEBUG') )
  4887 	if ( !defined('ENANO_DEBUG') )
  4888     return false;
  4888 		return false;
  4889   
  4889 	
  4890   global $_profiler;
  4890 	global $_profiler;
  4891   
  4891 	
  4892   $_profiler[] = array(
  4892 	$_profiler[] = array(
  4893       'message' => $message,
  4893 			'message' => $message,
  4894     );
  4894 		);
  4895 }
  4895 }
  4896 
  4896 
  4897 /**
  4897 /**
  4898  * Returns the profiler's data (so far).
  4898  * Returns the profiler's data (so far).
  4899  * @return array
  4899  * @return array
  4900  */
  4900  */
  4901 
  4901 
  4902 function profiler_dump()
  4902 function profiler_dump()
  4903 {
  4903 {
  4904   return $GLOBALS['_profiler'];
  4904 	return $GLOBALS['_profiler'];
  4905 }
  4905 }
  4906 
  4906 
  4907 /**
  4907 /**
  4908  * Generates an HTML version of the performance profile. Not localized because only used as a debugging tool.
  4908  * Generates an HTML version of the performance profile. Not localized because only used as a debugging tool.
  4909  * @return string
  4909  * @return string
  4910  */
  4910  */
  4911 
  4911 
  4912 function profiler_make_html()
  4912 function profiler_make_html()
  4913 {
  4913 {
  4914   if ( !defined('ENANO_DEBUG') )
  4914 	if ( !defined('ENANO_DEBUG') )
  4915     return '';
  4915 		return '';
  4916     
  4916 		
  4917   $profile = profiler_dump();
  4917 	$profile = profiler_dump();
  4918   
  4918 	
  4919   $html = '<div class="tblholder">';
  4919 	$html = '<div class="tblholder">';
  4920   $html .= '<table border="0" cellspacing="1" cellpadding="4">';
  4920 	$html .= '<table border="0" cellspacing="1" cellpadding="4">';
  4921   
  4921 	
  4922   $time_start = $time_last = $profile[0]['time'];
  4922 	$time_start = $time_last = $profile[0]['time'];
  4923   
  4923 	
  4924   foreach ( $profile as $i => $entry )
  4924 	foreach ( $profile as $i => $entry )
  4925   {
  4925 	{
  4926     // $time_since_last = $entry['time'] - $time_last;
  4926 		// $time_since_last = $entry['time'] - $time_last;
  4927     // if ( $time_since_last < 0.01 )
  4927 		// if ( $time_since_last < 0.01 )
  4928     //   continue;
  4928 		//   continue;
  4929     
  4929 		
  4930     if ( isset($entry['message']) )
  4930 		if ( isset($entry['message']) )
  4931     {
  4931 		{
  4932       $html .= "<!-- ########################################################## -->\n<tr>\n  <th colspan=\"2\">Message $i</th>\n</tr>";
  4932 			$html .= "<!-- ########################################################## -->\n<tr>\n  <th colspan=\"2\">Message $i</th>\n</tr>";
  4933       
  4933 			
  4934       $html .= '<tr>' . "\n";
  4934 			$html .= '<tr>' . "\n";
  4935       $html .= '  <td class="row2">Message:</td>' . "\n";
  4935 			$html .= '  <td class="row2">Message:</td>' . "\n";
  4936       $html .= '  <td class="row1">' . htmlspecialchars($entry['message']) . '</td>' . "\n";
  4936 			$html .= '  <td class="row1">' . htmlspecialchars($entry['message']) . '</td>' . "\n";
  4937       $html .= '</tr>' . "\n";
  4937 			$html .= '</tr>' . "\n";
  4938       continue;
  4938 			continue;
  4939     }
  4939 		}
  4940     
  4940 		
  4941     $html .= "<!-- ########################################################## -->\n<tr>\n  <th colspan=\"2\">Event $i</th>\n</tr>";
  4941 		$html .= "<!-- ########################################################## -->\n<tr>\n  <th colspan=\"2\">Event $i</th>\n</tr>";
  4942     
  4942 		
  4943     $html .= '<tr>' . "\n";
  4943 		$html .= '<tr>' . "\n";
  4944     $html .= '  <td class="row2">Event:</td>' . "\n";
  4944 		$html .= '  <td class="row2">Event:</td>' . "\n";
  4945     $html .= '  <td class="row1">' . htmlspecialchars($entry['point']) . '</td>' . "\n";
  4945 		$html .= '  <td class="row1">' . htmlspecialchars($entry['point']) . '</td>' . "\n";
  4946     $html .= '</tr>' . "\n";
  4946 		$html .= '</tr>' . "\n";
  4947     
  4947 		
  4948     $time = $entry['time'] - $time_start;
  4948 		$time = $entry['time'] - $time_start;
  4949     
  4949 		
  4950     $html .= '<tr>' . "\n";
  4950 		$html .= '<tr>' . "\n";
  4951     $html .= '  <td class="row2">Time since start:</td>' . "\n";
  4951 		$html .= '  <td class="row2">Time since start:</td>' . "\n";
  4952     $html .= '  <td class="row1">' . $time . 's</td>' . "\n";
  4952 		$html .= '  <td class="row1">' . $time . 's</td>' . "\n";
  4953     $html .= '</tr>' . "\n";
  4953 		$html .= '</tr>' . "\n";
  4954     
  4954 		
  4955     $time_label = 'Time since last event:';
  4955 		$time_label = 'Time since last event:';
  4956     if ( $entry['parent_event'] && is_int($entry['parent_event']) && isset($profile[$entry['parent_event']]) )
  4956 		if ( $entry['parent_event'] && is_int($entry['parent_event']) && isset($profile[$entry['parent_event']]) )
  4957     {
  4957 		{
  4958       $time_last = $profile[$entry['parent_event']]['time'];
  4958 			$time_last = $profile[$entry['parent_event']]['time'];
  4959       $time_label = "Time since event #{$entry['parent_event']}:";
  4959 			$time_label = "Time since event #{$entry['parent_event']}:";
  4960     }
  4960 		}
  4961     
  4961 		
  4962     $time = $entry['time'] - $time_last;
  4962 		$time = $entry['time'] - $time_last;
  4963     if ( $time < 0.0001 )
  4963 		if ( $time < 0.0001 )
  4964       $time_html = 'Marginal';
  4964 			$time_html = 'Marginal';
  4965     else
  4965 		else
  4966       $time_html = number_format($time, 6) . "s";
  4966 			$time_html = number_format($time, 6) . "s";
  4967     
  4967 		
  4968     if ( $time > 0.02 )
  4968 		if ( $time > 0.02 )
  4969       $time_html = "<span style=\"background-color: #a00; padding: 4px; color: #fff; font-weight: bold;\">$time_html</span>";
  4969 			$time_html = "<span style=\"background-color: #a00; padding: 4px; color: #fff; font-weight: bold;\">$time_html</span>";
  4970       
  4970 			
  4971     $html .= '<tr>' . "\n";
  4971 		$html .= '<tr>' . "\n";
  4972     $html .= '  <td class="row2">' . $time_label . '</td>' . "\n";
  4972 		$html .= '  <td class="row2">' . $time_label . '</td>' . "\n";
  4973     $html .= '  <td class="row1">' . $time_html . '</td>' . "\n";
  4973 		$html .= '  <td class="row1">' . $time_html . '</td>' . "\n";
  4974     $html .= '</tr>' . "\n";
  4974 		$html .= '</tr>' . "\n";
  4975     
  4975 		
  4976     if ( $entry['backtrace'] )
  4976 		if ( $entry['backtrace'] )
  4977     {
  4977 		{
  4978       $html .= '<tr>' . "\n";
  4978 			$html .= '<tr>' . "\n";
  4979       $html .= '  <td class="row2">Called from:</td>' . "\n";
  4979 			$html .= '  <td class="row2">Called from:</td>' . "\n";
  4980       $html .= '  <td class="row1">' . htmlspecialchars($entry['backtrace']) . '</td>' . "\n";
  4980 			$html .= '  <td class="row1">' . htmlspecialchars($entry['backtrace']) . '</td>' . "\n";
  4981       $html .= '</tr>' . "\n";
  4981 			$html .= '</tr>' . "\n";
  4982     }
  4982 		}
  4983     
  4983 		
  4984     if ( $entry['mem'] )
  4984 		if ( $entry['mem'] )
  4985     {
  4985 		{
  4986       $html .= '<tr>' . "\n";
  4986 			$html .= '<tr>' . "\n";
  4987       $html .= '  <td class="row2">Total mem usage:</td>' . "\n";
  4987 			$html .= '  <td class="row2">Total mem usage:</td>' . "\n";
  4988       $html .= '  <td class="row1">' . htmlspecialchars($entry['mem']) . ' (bytes)</td>' . "\n";
  4988 			$html .= '  <td class="row1">' . htmlspecialchars($entry['mem']) . ' (bytes)</td>' . "\n";
  4989       $html .= '</tr>' . "\n";
  4989 			$html .= '</tr>' . "\n";
  4990     }
  4990 		}
  4991     
  4991 		
  4992     $html .= "\n";
  4992 		$html .= "\n";
  4993     
  4993 		
  4994     $time_last = $entry['time'];
  4994 		$time_last = $entry['time'];
  4995   }
  4995 	}
  4996   $html .= '</table></div>';
  4996 	$html .= '</table></div>';
  4997   
  4997 	
  4998   return $html;
  4998 	return $html;
  4999 }
  4999 }
  5000 
  5000 
  5001 // Might as well start the profiler, it has no external dependencies except from this file.
  5001 // Might as well start the profiler, it has no external dependencies except from this file.
  5002 profiler_start();
  5002 profiler_start();
  5003 
  5003 
  5008  * @return int
  5008  * @return int
  5009  */
  5009  */
  5010 
  5010 
  5011 function get_char_count($string, $char)
  5011 function get_char_count($string, $char)
  5012 {
  5012 {
  5013   $char = substr($char, 0, 1);
  5013 	$char = substr($char, 0, 1);
  5014   $count = 0;
  5014 	$count = 0;
  5015   for ( $i = 0; $i < strlen($string); $i++ )
  5015 	for ( $i = 0; $i < strlen($string); $i++ )
  5016   {
  5016 	{
  5017     if ( $string{$i} == $char )
  5017 		if ( $string{$i} == $char )
  5018       $count++;
  5018 			$count++;
  5019   }
  5019 	}
  5020   return $count;
  5020 	return $count;
  5021 }
  5021 }
  5022 
  5022 
  5023 /**
  5023 /**
  5024  * Returns the number of lines in a string.
  5024  * Returns the number of lines in a string.
  5025  * @param string String to check
  5025  * @param string String to check
  5026  * @return int
  5026  * @return int
  5027  */
  5027  */
  5028 
  5028 
  5029 function get_line_count($string)
  5029 function get_line_count($string)
  5030 {
  5030 {
  5031   return ( get_char_count($string, "\n") ) + 1;
  5031 	return ( get_char_count($string, "\n") ) + 1;
  5032 }
  5032 }
  5033 
  5033 
  5034 if ( !function_exists('sys_get_temp_dir') )
  5034 if ( !function_exists('sys_get_temp_dir') )
  5035 {
  5035 {
  5036     // Based on http://www.phpit.net/
  5036 		// Based on http://www.phpit.net/
  5037     // article/creating-zip-tar-archives-dynamically-php/2/
  5037 		// article/creating-zip-tar-archives-dynamically-php/2/
  5038     /**
  5038 		/**
  5039      * Attempt to get the system's temp directory.
  5039  		* Attempt to get the system's temp directory.
  5040      * @return string or bool false on failure
  5040  		* @return string or bool false on failure
  5041      */
  5041  		*/
  5042     
  5042 		
  5043     function sys_get_temp_dir()
  5043 		function sys_get_temp_dir()
  5044     {
  5044 		{
  5045         // Try to get from environment variable
  5045 				// Try to get from environment variable
  5046         if ( !empty($_ENV['TMP']) )
  5046 				if ( !empty($_ENV['TMP']) )
  5047         {
  5047 				{
  5048             return realpath( $_ENV['TMP'] );
  5048 						return realpath( $_ENV['TMP'] );
  5049         }
  5049 				}
  5050         else if ( !empty($_ENV['TMPDIR']) )
  5050 				else if ( !empty($_ENV['TMPDIR']) )
  5051         {
  5051 				{
  5052             return realpath( $_ENV['TMPDIR'] );
  5052 						return realpath( $_ENV['TMPDIR'] );
  5053         }
  5053 				}
  5054         else if ( !empty($_ENV['TEMP']) )
  5054 				else if ( !empty($_ENV['TEMP']) )
  5055         {
  5055 				{
  5056             return realpath( $_ENV['TEMP'] );
  5056 						return realpath( $_ENV['TEMP'] );
  5057         }
  5057 				}
  5058 
  5058 
  5059         // Detect by creating a temporary file
  5059 				// Detect by creating a temporary file
  5060         else
  5060 				else
  5061         {
  5061 				{
  5062             // Try to use system's temporary directory
  5062 						// Try to use system's temporary directory
  5063             // as random name shouldn't exist
  5063 						// as random name shouldn't exist
  5064             $temp_file = tempnam( md5(uniqid(rand(), TRUE)), '' );
  5064 						$temp_file = tempnam( md5(uniqid(rand(), TRUE)), '' );
  5065             if ( $temp_file )
  5065 						if ( $temp_file )
  5066             {
  5066 						{
  5067                 $temp_dir = realpath( dirname($temp_file) );
  5067 								$temp_dir = realpath( dirname($temp_file) );
  5068                 unlink( $temp_file );
  5068 								unlink( $temp_file );
  5069                 return $temp_dir;
  5069 								return $temp_dir;
  5070             }
  5070 						}
  5071             else
  5071 						else
  5072             {
  5072 						{
  5073                 return FALSE;
  5073 								return FALSE;
  5074             }
  5074 						}
  5075         }
  5075 				}
  5076     }
  5076 		}
  5077 }
  5077 }
  5078 
  5078 
  5079 /**
  5079 /**
  5080  * Grabs and processes all rank information directly from the database.
  5080  * Grabs and processes all rank information directly from the database.
  5081  */
  5081  */
  5082 
  5082 
  5083 function fetch_rank_data()
  5083 function fetch_rank_data()
  5084 {
  5084 {
  5085   global $db, $session, $paths, $template, $plugins; // Common objects
  5085 	global $db, $session, $paths, $template, $plugins; // Common objects
  5086   global $lang;
  5086 	global $lang;
  5087   
  5087 	
  5088   $sql = $session->generate_rank_sql();
  5088 	$sql = $session->generate_rank_sql();
  5089   $q = $db->sql_query($sql);
  5089 	$q = $db->sql_query($sql);
  5090   if ( !$q )
  5090 	if ( !$q )
  5091     $db->_die();
  5091 		$db->_die();
  5092   
  5092 	
  5093   $GLOBALS['user_ranks'] = array();
  5093 	$GLOBALS['user_ranks'] = array();
  5094   global $user_ranks;
  5094 	global $user_ranks;
  5095   
  5095 	
  5096   while ( $row = $db->fetchrow($q) )
  5096 	while ( $row = $db->fetchrow($q) )
  5097   {
  5097 	{
  5098     $user_id = $row['user_id'];
  5098 		$user_id = $row['user_id'];
  5099     $username = $row['username'];
  5099 		$username = $row['username'];
  5100     $row = $session->calculate_user_rank($row);
  5100 		$row = $session->calculate_user_rank($row);
  5101     $user_ranks[$username] =  $row;
  5101 		$user_ranks[$username] =  $row;
  5102     $user_ranks[$user_id]  =& $user_ranks[$username];
  5102 		$user_ranks[$user_id]  =& $user_ranks[$username];
  5103   }
  5103 	}
  5104 }
  5104 }
  5105 
  5105 
  5106 /**
  5106 /**
  5107  * Caches the computed user rank information.
  5107  * Caches the computed user rank information.
  5108  */
  5108  */
  5109 
  5109 
  5110 function generate_cache_userranks()
  5110 function generate_cache_userranks()
  5111 {
  5111 {
  5112   global $db, $session, $paths, $template, $plugins; // Common objects
  5112 	global $db, $session, $paths, $template, $plugins; // Common objects
  5113   global $lang;
  5113 	global $lang;
  5114   global $user_ranks;
  5114 	global $user_ranks;
  5115   
  5115 	
  5116   fetch_rank_data();
  5116 	fetch_rank_data();
  5117   
  5117 	
  5118   $user_ranks_stripped = array();
  5118 	$user_ranks_stripped = array();
  5119   foreach ( $user_ranks as $key => $value )
  5119 	foreach ( $user_ranks as $key => $value )
  5120   {
  5120 	{
  5121     if ( is_int($key) )
  5121 		if ( is_int($key) )
  5122       $user_ranks_stripped[$key] = $value;
  5122 			$user_ranks_stripped[$key] = $value;
  5123   }
  5123 	}
  5124   
  5124 	
  5125   $ranks_exported = "<?php\n\n// Automatically generated user rank cache.\nglobal \$user_ranks;\n" . '$user_ranks = ' . $lang->var_export_string($user_ranks_stripped) . ';';
  5125 	$ranks_exported = "<?php\n\n// Automatically generated user rank cache.\nglobal \$user_ranks;\n" . '$user_ranks = ' . $lang->var_export_string($user_ranks_stripped) . ';';
  5126   $uid_map = array();
  5126 	$uid_map = array();
  5127   foreach ( $user_ranks as $id => $row )
  5127 	foreach ( $user_ranks as $id => $row )
  5128   {
  5128 	{
  5129     if ( !is_int($id) )
  5129 		if ( !is_int($id) )
  5130     {
  5130 		{
  5131       $username = $id;
  5131 			$username = $id;
  5132       continue;
  5132 			continue;
  5133     }
  5133 		}
  5134     
  5134 		
  5135     $un_san = addslashes($username);
  5135 		$un_san = addslashes($username);
  5136     $ranks_exported .= "\n\$user_ranks['$un_san'] =& \$user_ranks[{$row['user_id']}];";
  5136 		$ranks_exported .= "\n\$user_ranks['$un_san'] =& \$user_ranks[{$row['user_id']}];";
  5137   }
  5137 	}
  5138   $ranks_exported .= "\n\ndefine('ENANO_RANKS_CACHE_LOADED', 1); \n?>";
  5138 	$ranks_exported .= "\n\ndefine('ENANO_RANKS_CACHE_LOADED', 1); \n?>";
  5139   
  5139 	
  5140   // open ranks cache file
  5140 	// open ranks cache file
  5141   $fh = @fopen( ENANO_ROOT . '/cache/cache_ranks.php', 'w' );
  5141 	$fh = @fopen( ENANO_ROOT . '/cache/cache_ranks.php', 'w' );
  5142   if ( !$fh )
  5142 	if ( !$fh )
  5143     return false;
  5143 		return false;
  5144   fwrite($fh, $ranks_exported);
  5144 	fwrite($fh, $ranks_exported);
  5145   fclose($fh);
  5145 	fclose($fh);
  5146   
  5146 	
  5147   return true;
  5147 	return true;
  5148 }
  5148 }
  5149 
  5149 
  5150 /**
  5150 /**
  5151  * Loads the rank data, first attempting the cache file and then the database.
  5151  * Loads the rank data, first attempting the cache file and then the database.
  5152  */
  5152  */
  5153 
  5153 
  5154 function load_rank_data()
  5154 function load_rank_data()
  5155 {
  5155 {
  5156   if ( file_exists( ENANO_ROOT . '/cache/cache_ranks.php' ) )
  5156 	if ( file_exists( ENANO_ROOT . '/cache/cache_ranks.php' ) )
  5157   {
  5157 	{
  5158     @include(ENANO_ROOT . '/cache/cache_ranks.php');
  5158 		@include(ENANO_ROOT . '/cache/cache_ranks.php');
  5159   }
  5159 	}
  5160   if ( !defined('ENANO_RANKS_CACHE_LOADED') )
  5160 	if ( !defined('ENANO_RANKS_CACHE_LOADED') )
  5161   {
  5161 	{
  5162     fetch_rank_data();
  5162 		fetch_rank_data();
  5163   }
  5163 	}
  5164 }
  5164 }
  5165 
  5165 
  5166 /**
  5166 /**
  5167  * Function to purge all caches used in Enano. Useful for upgrades, maintenance, backing the site up, etc.
  5167  * Function to purge all caches used in Enano. Useful for upgrades, maintenance, backing the site up, etc.
  5168  */
  5168  */
  5169 
  5169 
  5170 function purge_all_caches()
  5170 function purge_all_caches()
  5171 {
  5171 {
  5172   global $cache;
  5172 	global $cache;
  5173   if ( $dh = opendir(ENANO_ROOT . '/cache') )
  5173 	if ( $dh = opendir(ENANO_ROOT . '/cache') )
  5174   {
  5174 	{
  5175     $cache->purge('page_meta');
  5175 		$cache->purge('page_meta');
  5176     $cache->purge('anon_sidebar');
  5176 		$cache->purge('anon_sidebar');
  5177     $cache->purge('plugins');
  5177 		$cache->purge('plugins');
  5178     $cache->purge('wiki_edit_notice');
  5178 		$cache->purge('wiki_edit_notice');
  5179     
  5179 		
  5180     $data_files = array(
  5180 		$data_files = array(
  5181         'aes_decrypt.php',
  5181 				'aes_decrypt.php',
  5182         // ranks cache is stored using a custom engine (not enano's default cache)
  5182 				// ranks cache is stored using a custom engine (not enano's default cache)
  5183         'cache_ranks.php'
  5183 				'cache_ranks.php'
  5184       );
  5184 			);
  5185     while ( $file = @readdir($dh) )
  5185 		while ( $file = @readdir($dh) )
  5186     {
  5186 		{
  5187       $fullpath = ENANO_ROOT . "/cache/$file";
  5187 			$fullpath = ENANO_ROOT . "/cache/$file";
  5188       // we don't want to mess with directories
  5188 			// we don't want to mess with directories
  5189       if ( !is_file($fullpath) )
  5189 			if ( !is_file($fullpath) )
  5190         continue;
  5190 				continue;
  5191       
  5191 			
  5192       // data files
  5192 			// data files
  5193       if ( in_array($file, $data_files) )
  5193 			if ( in_array($file, $data_files) )
  5194         unlink($fullpath);
  5194 				unlink($fullpath);
  5195       // template files
  5195 			// template files
  5196       else if ( preg_match('/\.(?:tpl|css)\.php$/', $file) )
  5196 			else if ( preg_match('/\.(?:tpl|css)\.php$/', $file) )
  5197         unlink($fullpath);
  5197 				unlink($fullpath);
  5198       // compressed javascript
  5198 			// compressed javascript
  5199       else if ( preg_match('/^jsres_(?:[A-z0-9_-]+)\.js\.json$/', $file) )
  5199 			else if ( preg_match('/^jsres_(?:[A-z0-9_-]+)\.js\.json$/', $file) )
  5200         unlink($fullpath);
  5200 				unlink($fullpath);
  5201       // tinymce stuff
  5201 			// tinymce stuff
  5202       else if ( preg_match('/^tiny_mce_(?:[a-f0-9]+)\.gz$/', $file) )
  5202 			else if ( preg_match('/^tiny_mce_(?:[a-f0-9]+)\.gz$/', $file) )
  5203         unlink($fullpath);
  5203 				unlink($fullpath);
  5204       // language files
  5204 			// language files
  5205       else if ( preg_match('/^lang_json_(?:[a-f0-9]+?)\.php$/', $file) || preg_match('/^(?:cache_)?lang_(?:[0-9]+?)\.php$/', $file) )
  5205 			else if ( preg_match('/^lang_json_(?:[a-f0-9]+?)\.php$/', $file) || preg_match('/^(?:cache_)?lang_(?:[0-9]+?)\.php$/', $file) )
  5206         unlink($fullpath);
  5206 				unlink($fullpath);
  5207     }
  5207 		}
  5208     return true;
  5208 		return true;
  5209   }
  5209 	}
  5210   return false;
  5210 	return false;
  5211 }
  5211 }
  5212 
  5212 
  5213 /**
  5213 /**
  5214  * Implementation of the "which" command in native PHP.
  5214  * Implementation of the "which" command in native PHP.
  5215  * @param string command
  5215  * @param string command
  5216  * @return string path to executable, or false on failure
  5216  * @return string path to executable, or false on failure
  5217  */
  5217  */
  5218 
  5218 
  5219 function which($executable)
  5219 function which($executable)
  5220 {
  5220 {
  5221   $path = ( isset($_ENV['PATH']) ) ? $_ENV['PATH'] : ( isset($_SERVER['PATH']) ? $_SERVER['PATH'] : false );
  5221 	$path = ( isset($_ENV['PATH']) ) ? $_ENV['PATH'] : ( isset($_SERVER['PATH']) ? $_SERVER['PATH'] : false );
  5222   if ( !$path )
  5222 	if ( !$path )
  5223     // couldn't get OS's PATH
  5223 		// couldn't get OS's PATH
  5224     return false;
  5224 		return false;
  5225     
  5225 		
  5226   $win32 = ( PHP_OS == 'WINNT' || PHP_OS == 'WIN32' );
  5226 	$win32 = ( PHP_OS == 'WINNT' || PHP_OS == 'WIN32' );
  5227   $extensions = $win32 ? array('.exe', '.com', '.bat') : array('');
  5227 	$extensions = $win32 ? array('.exe', '.com', '.bat') : array('');
  5228   $separator = $win32 ? ';' : ':';
  5228 	$separator = $win32 ? ';' : ':';
  5229   $paths = explode($separator, $path);
  5229 	$paths = explode($separator, $path);
  5230   foreach ( $paths as $dir )
  5230 	foreach ( $paths as $dir )
  5231   {
  5231 	{
  5232     foreach ( $extensions as $ext )
  5232 		foreach ( $extensions as $ext )
  5233     {
  5233 		{
  5234       $fullpath = "$dir/{$executable}{$ext}";
  5234 			$fullpath = "$dir/{$executable}{$ext}";
  5235       if ( @file_exists($fullpath) && @is_executable($fullpath) )
  5235 			if ( @file_exists($fullpath) && @is_executable($fullpath) )
  5236       {
  5236 			{
  5237         return $fullpath;
  5237 				return $fullpath;
  5238       }
  5238 			}
  5239     }
  5239 		}
  5240   }
  5240 	}
  5241   return false;
  5241 	return false;
  5242 }
  5242 }
  5243 
  5243 
  5244 /**
  5244 /**
  5245  * Properly test a file or directory for writability. Used in various places around installer.
  5245  * Properly test a file or directory for writability. Used in various places around installer.
  5246  * @param string File or directory to test
  5246  * @param string File or directory to test
  5247  * @return bool
  5247  * @return bool
  5248  */
  5248  */
  5249 
  5249 
  5250 function write_test($filename)
  5250 function write_test($filename)
  5251 {
  5251 {
  5252   // We need to actually _open_ the file to make sure it can be written, because sometimes this fails even when is_writable() returns
  5252 	// We need to actually _open_ the file to make sure it can be written, because sometimes this fails even when is_writable() returns
  5253   // true on Windows/IIS servers. Don't ask me why.
  5253 	// true on Windows/IIS servers. Don't ask me why.
  5254   
  5254 	
  5255   $file = ENANO_ROOT . '/' . $filename;
  5255 	$file = ENANO_ROOT . '/' . $filename;
  5256   if ( is_dir($file) )
  5256 	if ( is_dir($file) )
  5257   {
  5257 	{
  5258     $file = rtrim($file, '/') . '/' . 'enanoinstalltest.txt';
  5258 		$file = rtrim($file, '/') . '/' . 'enanoinstalltest.txt';
  5259     if ( file_exists($file) )
  5259 		if ( file_exists($file) )
  5260     {
  5260 		{
  5261       $fp = @fopen($file, 'a+');
  5261 			$fp = @fopen($file, 'a+');
  5262       if ( !$fp )
  5262 			if ( !$fp )
  5263         return false;
  5263 				return false;
  5264       fclose($fp);
  5264 			fclose($fp);
  5265       unlink($file);
  5265 			unlink($file);
  5266       return true;
  5266 			return true;
  5267     }
  5267 		}
  5268     else
  5268 		else
  5269     {
  5269 		{
  5270       $fp = @fopen($file, 'w');
  5270 			$fp = @fopen($file, 'w');
  5271       if ( !$fp )
  5271 			if ( !$fp )
  5272         return false;
  5272 				return false;
  5273       fclose($fp);
  5273 			fclose($fp);
  5274       unlink($file);
  5274 			unlink($file);
  5275       return true;
  5275 			return true;
  5276     }
  5276 		}
  5277   }
  5277 	}
  5278   else
  5278 	else
  5279   {
  5279 	{
  5280     if ( file_exists($file) )
  5280 		if ( file_exists($file) )
  5281     {
  5281 		{
  5282       $fp = @fopen($file, 'a+');
  5282 			$fp = @fopen($file, 'a+');
  5283       if ( !$fp )
  5283 			if ( !$fp )
  5284         return false;
  5284 				return false;
  5285       fclose($fp);
  5285 			fclose($fp);
  5286       return true;
  5286 			return true;
  5287     }
  5287 		}
  5288     else
  5288 		else
  5289     {
  5289 		{
  5290       $fp = @fopen($file, 'w');
  5290 			$fp = @fopen($file, 'w');
  5291       if ( !$fp )
  5291 			if ( !$fp )
  5292         return false;
  5292 				return false;
  5293       fclose($fp);
  5293 			fclose($fp);
  5294       return true;
  5294 			return true;
  5295     }
  5295 		}
  5296   }
  5296 	}
  5297 }
  5297 }
  5298 
  5298 
  5299 /**
  5299 /**
  5300  * Get the appropriate crypto backend for this server
  5300  * Get the appropriate crypto backend for this server
  5301  * @return string
  5301  * @return string
  5302  */
  5302  */
  5303 
  5303 
  5304 function install_get_crypto_backend()
  5304 function install_get_crypto_backend()
  5305 {
  5305 {
  5306   $crypto_backend = 'none';
  5306 	$crypto_backend = 'none';
  5307 
  5307 
  5308   // Extension test: BCMath
  5308 	// Extension test: BCMath
  5309   if ( function_exists('bcadd') )
  5309 	if ( function_exists('bcadd') )
  5310     $crypto_backend = 'bcmath';
  5310 		$crypto_backend = 'bcmath';
  5311   
  5311 	
  5312   // Extension test: Big_Int
  5312 	// Extension test: Big_Int
  5313   if ( function_exists('bi_from_str') )
  5313 	if ( function_exists('bi_from_str') )
  5314     $crypto_backend = 'bigint';
  5314 		$crypto_backend = 'bigint';
  5315   
  5315 	
  5316   // Extension test: GMP
  5316 	// Extension test: GMP
  5317   if ( function_exists('gmp_init') )
  5317 	if ( function_exists('gmp_init') )
  5318     $crypto_backend = 'gmp';
  5318 		$crypto_backend = 'gmp';
  5319   
  5319 	
  5320   return $crypto_backend;
  5320 	return $crypto_backend;
  5321 }
  5321 }
  5322 
  5322