includes/sessions.php
changeset 1227 bdac73ed481e
parent 1216 4125e19d3b27
child 1231 4797a4a88533
equal deleted inserted replaced
1226:de56132c008d 1227:bdac73ed481e
    19  * @subpackage Session manager
    19  * @subpackage Session manager
    20  * @category security, user management, logins, etc.
    20  * @category security, user management, logins, etc.
    21  */
    21  */
    22 
    22 
    23 class sessionManager {
    23 class sessionManager {
    24   
    24 	
    25   # Variables
    25 	# Variables
    26   
    26 	
    27   /**
    27 	/**
    28    * Whether we're logged in or not
    28  	* Whether we're logged in or not
    29    * @var bool
    29  	* @var bool
    30    */
    30  	*/
    31    
    31  	
    32   var $user_logged_in = false;
    32 	var $user_logged_in = false;
    33   
    33 	
    34   /**
    34 	/**
    35    * Our current low-privilege session key
    35  	* Our current low-privilege session key
    36    * @var string
    36  	* @var string
    37    */
    37  	*/
    38   
    38 	
    39   var $sid;
    39 	var $sid;
    40   
    40 	
    41   /**
    41 	/**
    42    * Username of currently logged-in user, or IP address if not logged in
    42  	* Username of currently logged-in user, or IP address if not logged in
    43    * @var string
    43  	* @var string
    44    */
    44  	*/
    45   
    45 	
    46   var $username;
    46 	var $username;
    47   
    47 	
    48   /**
    48 	/**
    49    * User ID of currently logged-in user, or 1 if not logged in
    49  	* User ID of currently logged-in user, or 1 if not logged in
    50    * @var int
    50  	* @var int
    51    */
    51  	*/
    52   
    52 	
    53   var $user_id = 1;
    53 	var $user_id = 1;
    54   
    54 	
    55   /**
    55 	/**
    56    * Real name of currently logged-in user, or blank if not logged in
    56  	* Real name of currently logged-in user, or blank if not logged in
    57    * @var string
    57  	* @var string
    58    */
    58  	*/
    59   
    59 	
    60   var $real_name;
    60 	var $real_name;
    61   
    61 	
    62   /**
    62 	/**
    63    * E-mail address of currently logged-in user, or blank if not logged in
    63  	* E-mail address of currently logged-in user, or blank if not logged in
    64    * @var string
    64  	* @var string
    65    */
    65  	*/
    66   
    66 	
    67   var $email;
    67 	var $email;
    68   
    68 	
    69   /**
    69 	/**
    70    * List of "extra" user information fields (IM handles, etc.)
    70  	* List of "extra" user information fields (IM handles, etc.)
    71    * @var array (associative)
    71  	* @var array (associative)
    72    */
    72  	*/
    73   
    73 	
    74   var $user_extra;
    74 	var $user_extra;
    75   
    75 	
    76   /**
    76 	/**
    77    * User level of current user
    77  	* User level of current user
    78    * USER_LEVEL_GUEST: guest
    78  	* USER_LEVEL_GUEST: guest
    79    * USER_LEVEL_MEMBER: regular user
    79  	* USER_LEVEL_MEMBER: regular user
    80    * USER_LEVEL_CHPREF: default - pseudo-level that allows changing password and e-mail address (requires re-authentication)
    80  	* USER_LEVEL_CHPREF: default - pseudo-level that allows changing password and e-mail address (requires re-authentication)
    81    * USER_LEVEL_MOD: moderator
    81  	* USER_LEVEL_MOD: moderator
    82    * USER_LEVEL_ADMIN: administrator
    82  	* USER_LEVEL_ADMIN: administrator
    83    * @var int
    83  	* @var int
    84    */
    84  	*/
    85   
    85 	
    86   var $user_level;
    86 	var $user_level;
    87   
    87 	
    88   /**
    88 	/**
    89    * High-privilege session key
    89  	* High-privilege session key
    90    * @var string or false if not running on high-level authentication
    90  	* @var string or false if not running on high-level authentication
    91    */
    91  	*/
    92   
    92 	
    93   var $sid_super;
    93 	var $sid_super;
    94   
    94 	
    95   /**
    95 	/**
    96    * The user's theme preference, defaults to $template->default_theme
    96  	* The user's theme preference, defaults to $template->default_theme
    97    * @var string
    97  	* @var string
    98    */
    98  	*/
    99   
    99 	
   100   var $theme;
   100 	var $theme;
   101   
   101 	
   102   /**
   102 	/**
   103    * The user's style preference, or style auto-detected based on theme if not logged in
   103  	* The user's style preference, or style auto-detected based on theme if not logged in
   104    * @var string
   104  	* @var string
   105    */
   105  	*/
   106   
   106 	
   107   var $style;
   107 	var $style;
   108   
   108 	
   109   /**
   109 	/**
   110    * Signature of current user - appended to comments, etc.
   110  	* Signature of current user - appended to comments, etc.
   111    * @var string
   111  	* @var string
   112    */
   112  	*/
   113   
   113 	
   114   var $signature;
   114 	var $signature;
   115   
   115 	
   116   /**
   116 	/**
   117    * UNIX timestamp of when we were registered, or 0 if not logged in
   117  	* UNIX timestamp of when we were registered, or 0 if not logged in
   118    * @var int
   118  	* @var int
   119    */
   119  	*/
   120   
   120 	
   121   var $reg_time;
   121 	var $reg_time;
   122   
   122 	
   123   /**
   123 	/**
   124    * The number of unread private messages this user has.
   124  	* The number of unread private messages this user has.
   125    * @var int
   125  	* @var int
   126    */
   126  	*/
   127   
   127 	
   128   var $unread_pms = 0;
   128 	var $unread_pms = 0;
   129   
   129 	
   130   /**
   130 	/**
   131    * AES key used to encrypt passwords and session key info.
   131  	* AES key used to encrypt passwords and session key info.
   132    * @var string
   132  	* @var string
   133    * @access private
   133  	* @access private
   134    */
   134  	*/
   135    
   135  	
   136   protected $private_key;
   136 	protected $private_key;
   137   
   137 	
   138   /**
   138 	/**
   139    * Regex that defines a valid username, minus the ^ and $, these are added later
   139  	* Regex that defines a valid username, minus the ^ and $, these are added later
   140    * @var string
   140  	* @var string
   141    */
   141  	*/
   142    
   142  	
   143   var $valid_username = '([^<>&\?\'"%\n\r\t\a\/]+)';
   143 	var $valid_username = '([^<>&\?\'"%\n\r\t\a\/]+)';
   144   
   144 	
   145   /**
   145 	/**
   146    * The current user's user title. Defaults to NULL.
   146  	* The current user's user title. Defaults to NULL.
   147    * @var string
   147  	* @var string
   148    */
   148  	*/
   149   
   149 	
   150   var $user_title = null;
   150 	var $user_title = null;
   151    
   151  	
   152   /**
   152 	/**
   153    * What we're allowed to do as far as permissions go. This changes based on the value of the "auth" URI param.
   153  	* What we're allowed to do as far as permissions go. This changes based on the value of the "auth" URI param.
   154    * @var string
   154  	* @var string
   155    */
   155  	*/
   156    
   156  	
   157   var $auth_level = 1;
   157 	var $auth_level = 1;
   158   
   158 	
   159   /**
   159 	/**
   160    * Preference for date formatting
   160  	* Preference for date formatting
   161    * @var string
   161  	* @var string
   162    */
   162  	*/
   163   
   163 	
   164   var $date_format = DATE_4;
   164 	var $date_format = DATE_4;
   165   
   165 	
   166   /**
   166 	/**
   167    * Preference for time formatting
   167  	* Preference for time formatting
   168    * @var string
   168  	* @var string
   169    */
   169  	*/
   170   
   170 	
   171   var $time_format = TIME_24_NS;
   171 	var $time_format = TIME_24_NS;
   172   
   172 	
   173   /**
   173 	/**
   174    * State variable to track if a session timed out
   174  	* State variable to track if a session timed out
   175    * @var bool
   175  	* @var bool
   176    */
   176  	*/
   177   
   177 	
   178   var $sw_timed_out = false;
   178 	var $sw_timed_out = false;
   179   
   179 	
   180   /**
   180 	/**
   181    * Token appended to some important forms to prevent CSRF.
   181  	* Token appended to some important forms to prevent CSRF.
   182    * @var string
   182  	* @var string
   183    */
   183  	*/
   184   
   184 	
   185   var $csrf_token = false;
   185 	var $csrf_token = false;
   186   
   186 	
   187   /**
   187 	/**
   188    * Password change disabled, for auth plugins
   188  	* Password change disabled, for auth plugins
   189    * @var bool
   189  	* @var bool
   190    */
   190  	*/
   191   
   191 	
   192   var $password_change_disabled = false;
   192 	var $password_change_disabled = false;
   193   
   193 	
   194   /**
   194 	/**
   195    * Password change page URL + title, for auth plugins
   195  	* Password change page URL + title, for auth plugins
   196    * @var array
   196  	* @var array
   197    */
   197  	*/
   198   
   198 	
   199   var $password_change_dest = array('url' => '', 'title' => '');
   199 	var $password_change_dest = array('url' => '', 'title' => '');
   200   
   200 	
   201   /**
   201 	/**
   202    * Switch to track if we're started or not.
   202  	* Switch to track if we're started or not.
   203    * @access private
   203  	* @access private
   204    * @var bool
   204  	* @var bool
   205    */
   205  	*/
   206    
   206  	
   207   var $started = false;
   207 	var $started = false;
   208   
   208 	
   209   /**
   209 	/**
   210    * Switch to control compatibility mode (for older Enano websites being upgraded)
   210  	* Switch to control compatibility mode (for older Enano websites being upgraded)
   211    * @access private
   211  	* @access private
   212    * @var bool
   212  	* @var bool
   213    */
   213  	*/
   214    
   214  	
   215   var $compat = false;
   215 	var $compat = false;
   216   
   216 	
   217   /**
   217 	/**
   218    * Our list of permission types.
   218  	* Our list of permission types.
   219    * @access private
   219  	* @access private
   220    * @var array
   220  	* @var array
   221    */
   221  	*/
   222    
   222  	
   223   var $acl_types = Array();
   223 	var $acl_types = Array();
   224   
   224 	
   225   /**
   225 	/**
   226    * The list of descriptions for the permission types
   226  	* The list of descriptions for the permission types
   227    * @var array
   227  	* @var array
   228    */
   228  	*/
   229    
   229  	
   230   var $acl_descs = Array();
   230 	var $acl_descs = Array();
   231   
   231 	
   232   /**
   232 	/**
   233    * A list of dependencies for ACL types.
   233  	* A list of dependencies for ACL types.
   234    * @var array
   234  	* @var array
   235    */
   235  	*/
   236    
   236  	
   237   var $acl_deps = Array();
   237 	var $acl_deps = Array();
   238   
   238 	
   239   /**
   239 	/**
   240    * Our tell-all list of permissions. Do not even try to change this.
   240  	* Our tell-all list of permissions. Do not even try to change this.
   241    * @access private
   241  	* @access private
   242    * @var array
   242  	* @var array
   243    */
   243  	*/
   244    
   244  	
   245   var $perms = Array();
   245 	var $perms = Array();
   246   
   246 	
   247   /**
   247 	/**
   248    * A cache variable - saved after sitewide permissions are checked but before page-specific permissions.
   248  	* A cache variable - saved after sitewide permissions are checked but before page-specific permissions.
   249    * @var array
   249  	* @var array
   250    * @access private
   250  	* @access private
   251    */
   251  	*/
   252   
   252 	
   253   var $acl_base_cache = Array();
   253 	var $acl_base_cache = Array();
   254   
   254 	
   255   /**
   255 	/**
   256    * Stores the scope information for ACL types.
   256  	* Stores the scope information for ACL types.
   257    * @var array
   257  	* @var array
   258    * @access private
   258  	* @access private
   259    */
   259  	*/
   260    
   260  	
   261   var $acl_scope = Array();
   261 	var $acl_scope = Array();
   262   
   262 	
   263   /**
   263 	/**
   264    * Array to track which default permissions are being used
   264  	* Array to track which default permissions are being used
   265    * @var array
   265  	* @var array
   266    * @access private
   266  	* @access private
   267    */
   267  	*/
   268    
   268  	
   269   var $acl_defaults_used = Array();
   269 	var $acl_defaults_used = Array();
   270   
   270 	
   271   /**
   271 	/**
   272    * Array to track group membership.
   272  	* Array to track group membership.
   273    * @var array
   273  	* @var array
   274    */
   274  	*/
   275    
   275  	
   276   var $groups = Array();
   276 	var $groups = Array();
   277   
   277 	
   278   /**
   278 	/**
   279    * Associative array to track group modship.
   279  	* Associative array to track group modship.
   280    * @var array
   280  	* @var array
   281    */
   281  	*/
   282    
   282  	
   283   var $group_mod = Array();
   283 	var $group_mod = Array();
   284   
   284 	
   285   /**
   285 	/**
   286    * A constant array of user-level-to-rank default associations.
   286  	* A constant array of user-level-to-rank default associations.
   287    * @var array
   287  	* @var array
   288    */
   288  	*/
   289   
   289 	
   290   var $level_rank_table = array(
   290 	var $level_rank_table = array(
   291       USER_LEVEL_ADMIN  => RANK_ID_ADMIN,
   291 			USER_LEVEL_ADMIN  => RANK_ID_ADMIN,
   292       USER_LEVEL_MOD    => RANK_ID_MOD,
   292 			USER_LEVEL_MOD    => RANK_ID_MOD,
   293       USER_LEVEL_MEMBER => RANK_ID_MEMBER,
   293 			USER_LEVEL_MEMBER => RANK_ID_MEMBER,
   294       USER_LEVEL_CHPREF => RANK_ID_MEMBER,
   294 			USER_LEVEL_CHPREF => RANK_ID_MEMBER,
   295       USER_LEVEL_GUEST  => RANK_ID_GUEST
   295 			USER_LEVEL_GUEST  => RANK_ID_GUEST
   296     );
   296 		);
   297   
   297 	
   298   /**
   298 	/**
   299    * A constant array that maps precedence constants to language strings
   299  	* A constant array that maps precedence constants to language strings
   300    * @var array
   300  	* @var array
   301    */
   301  	*/
   302   
   302 	
   303   var $acl_inherit_lang_table = array(
   303 	var $acl_inherit_lang_table = array(
   304       ACL_INHERIT_ENANO_DEFAULT   => 'acl_inherit_enano_default',
   304 			ACL_INHERIT_ENANO_DEFAULT   => 'acl_inherit_enano_default',
   305       ACL_INHERIT_GLOBAL_EVERYONE => 'acl_inherit_global_everyone',
   305 			ACL_INHERIT_GLOBAL_EVERYONE => 'acl_inherit_global_everyone',
   306       ACL_INHERIT_GLOBAL_GROUP    => 'acl_inherit_global_group',
   306 			ACL_INHERIT_GLOBAL_GROUP    => 'acl_inherit_global_group',
   307       ACL_INHERIT_GLOBAL_USER     => 'acl_inherit_global_user',
   307 			ACL_INHERIT_GLOBAL_USER     => 'acl_inherit_global_user',
   308       ACL_INHERIT_PG_EVERYONE     => 'acl_inherit_pg_everyone',
   308 			ACL_INHERIT_PG_EVERYONE     => 'acl_inherit_pg_everyone',
   309       ACL_INHERIT_PG_GROUP        => 'acl_inherit_pg_group',
   309 			ACL_INHERIT_PG_GROUP        => 'acl_inherit_pg_group',
   310       ACL_INHERIT_PG_USER         => 'acl_inherit_pg_user',
   310 			ACL_INHERIT_PG_USER         => 'acl_inherit_pg_user',
   311       ACL_INHERIT_LOCAL_EVERYONE  => 'acl_inherit_local_everyone',
   311 			ACL_INHERIT_LOCAL_EVERYONE  => 'acl_inherit_local_everyone',
   312       ACL_INHERIT_LOCAL_GROUP     => 'acl_inherit_local_group',
   312 			ACL_INHERIT_LOCAL_GROUP     => 'acl_inherit_local_group',
   313       ACL_INHERIT_LOCAL_USER      => 'acl_inherit_local_user'
   313 			ACL_INHERIT_LOCAL_USER      => 'acl_inherit_local_user'
   314     );
   314 		);
   315   
   315 	
   316   # Basic functions
   316 	# Basic functions
   317    
   317  	
   318   /**
   318 	/**
   319    * Constructor.
   319  	* Constructor.
   320    */
   320  	*/
   321    
   321  	
   322   function __construct()
   322 	function __construct()
   323   {
   323 	{
   324     global $db, $session, $paths, $template, $plugins; // Common objects
   324 		global $db, $session, $paths, $template, $plugins; // Common objects
   325     
   325 		
   326     if ( defined('IN_ENANO_INSTALL') && !defined('IN_ENANO_UPGRADE') )
   326 		if ( defined('IN_ENANO_INSTALL') && !defined('IN_ENANO_UPGRADE') )
   327     {
   327 		{
   328       @include(ENANO_ROOT.'/config.new.php');
   328 			@include(ENANO_ROOT.'/config.new.php');
   329     }
   329 		}
   330     else
   330 		else
   331     {
   331 		{
   332       @include(ENANO_ROOT.'/config.php');
   332 			@include(ENANO_ROOT.'/config.php');
   333     }
   333 		}
   334     
   334 		
   335     unset($dbhost, $dbname, $dbuser, $dbpasswd);
   335 		unset($dbhost, $dbname, $dbuser, $dbpasswd);
   336     if(isset($crypto_key))
   336 		if(isset($crypto_key))
   337     {
   337 		{
   338       $this->private_key = $crypto_key;
   338 			$this->private_key = $crypto_key;
   339       $this->private_key = hexdecode($this->private_key);
   339 			$this->private_key = hexdecode($this->private_key);
   340     }
   340 		}
   341     else
   341 		else
   342     {
   342 		{
   343       if(is_writable(ENANO_ROOT.'/config.php'))
   343 			if(is_writable(ENANO_ROOT.'/config.php'))
   344       {
   344 			{
   345         // Generate and stash a private key
   345 				// Generate and stash a private key
   346         // This should only happen during an automated silent gradual migration to the new encryption platform.
   346 				// This should only happen during an automated silent gradual migration to the new encryption platform.
   347         $aes = AESCrypt::singleton(AES_BITS, AES_BLOCKSIZE);
   347 				$aes = AESCrypt::singleton(AES_BITS, AES_BLOCKSIZE);
   348         $this->private_key = $aes->gen_readymade_key();
   348 				$this->private_key = $aes->gen_readymade_key();
   349         
   349 				
   350         $config = file_get_contents(ENANO_ROOT.'/config.php');
   350 				$config = file_get_contents(ENANO_ROOT.'/config.php');
   351         if(!$config)
   351 				if(!$config)
   352         {
   352 				{
   353           die('$session->__construct(): can\'t get the contents of config.php');
   353 					die('$session->__construct(): can\'t get the contents of config.php');
   354         }
   354 				}
   355         
   355 				
   356         $config = str_replace("?>", "\$crypto_key = '{$this->private_key}';\n?>", $config);
   356 				$config = str_replace("?>", "\$crypto_key = '{$this->private_key}';\n?>", $config);
   357         // And while we're at it...
   357 				// And while we're at it...
   358         $config = str_replace('MIDGET_INSTALLED', 'ENANO_INSTALLED', $config);
   358 				$config = str_replace('MIDGET_INSTALLED', 'ENANO_INSTALLED', $config);
   359         $fh = @fopen(ENANO_ROOT.'/config.php', 'w');
   359 				$fh = @fopen(ENANO_ROOT.'/config.php', 'w');
   360         if ( !$fh ) 
   360 				if ( !$fh ) 
   361         {
   361 				{
   362           die('$session->__construct(): Couldn\'t open config file for writing to store the private key, I tried to avoid something like this...');
   362 					die('$session->__construct(): Couldn\'t open config file for writing to store the private key, I tried to avoid something like this...');
   363         }
   363 				}
   364         
   364 				
   365         fwrite($fh, $config);
   365 				fwrite($fh, $config);
   366         fclose($fh);
   366 				fclose($fh);
   367       }
   367 			}
   368       else
   368 			else
   369       {
   369 			{
   370         die_semicritical('Crypto error', '<p>No private key was found in the config file, and we can\'t generate one because we don\'t have write access to the config file. Please CHMOD config.php to 666 or 777 and reload this page.</p>');
   370 				die_semicritical('Crypto error', '<p>No private key was found in the config file, and we can\'t generate one because we don\'t have write access to the config file. Please CHMOD config.php to 666 or 777 and reload this page.</p>');
   371       }
   371 			}
   372     }
   372 		}
   373     // Check for compatibility mode
   373 		// Check for compatibility mode
   374     if(defined('IN_ENANO_INSTALL'))
   374 		if(defined('IN_ENANO_INSTALL'))
   375     {
   375 		{
   376       $q = $db->sql_query('SELECT old_encryption FROM '.table_prefix.'users LIMIT 1;');
   376 			$q = $db->sql_query('SELECT old_encryption FROM '.table_prefix.'users LIMIT 1;');
   377       if(!$q)
   377 			if(!$q)
   378       {
   378 			{
   379         $error = mysql_error();
   379 				$error = mysql_error();
   380         if(strstr($error, "Unknown column 'old_encryption'"))
   380 				if(strstr($error, "Unknown column 'old_encryption'"))
   381           $this->compat = true;
   381 					$this->compat = true;
   382         else
   382 				else
   383           $db->_die('This should never happen and is a bug - the only error that was supposed to happen here didn\'t happen. (sessions.php in constructor, during compat mode check)');
   383 					$db->_die('This should never happen and is a bug - the only error that was supposed to happen here didn\'t happen. (sessions.php in constructor, during compat mode check)');
   384       }
   384 			}
   385       $db->free_result();
   385 			$db->free_result();
   386     }
   386 		}
   387   }
   387 	}
   388   
   388 	
   389   /**
   389 	/**
   390    * PHP 4 compatible constructor. Deprecated in 1.1.x.
   390  	* PHP 4 compatible constructor. Deprecated in 1.1.x.
   391    */
   391  	*/
   392    
   392  	
   393   /*
   393 	/*
   394   function sessionManager()
   394 	function sessionManager()
   395   {
   395 	{
   396     $this->__construct();
   396 		$this->__construct();
   397   }
   397 	}
   398   */
   398 	*/
   399   
   399 	
   400   /**
   400 	/**
   401    * Wrapper function to sanitize strings for MySQL and HTML
   401  	* Wrapper function to sanitize strings for MySQL and HTML
   402    * @param string $text The text to sanitize
   402  	* @param string $text The text to sanitize
   403    * @return string
   403  	* @return string
   404    */
   404  	*/
   405   
   405 	
   406   function prepare_text($text)
   406 	function prepare_text($text)
   407   {
   407 	{
   408     global $db;
   408 		global $db;
   409     return $db->escape(htmlspecialchars($text));
   409 		return $db->escape(htmlspecialchars($text));
   410   }
   410 	}
   411   
   411 	
   412   /**
   412 	/**
   413    * Makes a SQL query and handles error checking
   413  	* Makes a SQL query and handles error checking
   414    * @param string $query The SQL query to make
   414  	* @param string $query The SQL query to make
   415    * @return resource
   415  	* @return resource
   416    */
   416  	*/
   417   
   417 	
   418   function sql($query)
   418 	function sql($query)
   419   {
   419 	{
   420     global $db, $session, $paths, $template, $plugins; // Common objects
   420 		global $db, $session, $paths, $template, $plugins; // Common objects
   421     $result = $db->sql_query($query);
   421 		$result = $db->sql_query($query);
   422     if(!$result)
   422 		if(!$result)
   423     {
   423 		{
   424       $db->_die('The error seems to have occurred somewhere in the session management code.');
   424 			$db->_die('The error seems to have occurred somewhere in the session management code.');
   425     }
   425 		}
   426     return $result;
   426 		return $result;
   427   }
   427 	}
   428   
   428 	
   429   /**
   429 	/**
   430    * Returns true if we're currently on a page that shouldn't be blocked even if we have an inactive or banned account
   430  	* Returns true if we're currently on a page that shouldn't be blocked even if we have an inactive or banned account
   431    * @param bool strict - if true, whitelist of pages is even stricter (Login, Logout and CSS only). if false (default), admin access is allowed, assuming other factors allow it
   431  	* @param bool strict - if true, whitelist of pages is even stricter (Login, Logout and CSS only). if false (default), admin access is allowed, assuming other factors allow it
   432    * @return bool
   432  	* @return bool
   433    */
   433  	*/
   434   
   434 	
   435   function on_critical_page($strict = false)
   435 	function on_critical_page($strict = false)
   436   {
   436 	{
   437     global $urlname;
   437 		global $urlname;
   438     list($page_id, $namespace) = RenderMan::strToPageID($urlname);
   438 		list($page_id, $namespace) = RenderMan::strToPageID($urlname);
   439     list($page_id) = explode('/', $page_id);
   439 		list($page_id) = explode('/', $page_id);
   440     
   440 		
   441     if ( $strict )
   441 		if ( $strict )
   442     {
   442 		{
   443       return $namespace == 'Special' && in_array($page_id, array('CSS', 'Login', 'Logout', 'LangExportJSON', 'ActivateAccount'));
   443 			return $namespace == 'Special' && in_array($page_id, array('CSS', 'Login', 'Logout', 'LangExportJSON', 'ActivateAccount'));
   444     }
   444 		}
   445     else
   445 		else
   446     {
   446 		{
   447       return $namespace == 'Admin' || ($namespace == 'Special' && in_array($page_id, array('CSS', 'Login', 'Logout', 'Administration', 'LangExportJSON', 'ActivateAccount')));
   447 			return $namespace == 'Admin' || ($namespace == 'Special' && in_array($page_id, array('CSS', 'Login', 'Logout', 'Administration', 'LangExportJSON', 'ActivateAccount')));
   448     }
   448 		}
   449   }
   449 	}
   450   
   450 	
   451   # Session restoration and permissions
   451 	# Session restoration and permissions
   452   
   452 	
   453   /**
   453 	/**
   454    * Initializes the basic state of things, including most user prefs, login data, cookie stuff
   454  	* Initializes the basic state of things, including most user prefs, login data, cookie stuff
   455    */
   455  	*/
   456   
   456 	
   457   function start()
   457 	function start()
   458   {
   458 	{
   459     global $db, $session, $paths, $template, $plugins; // Common objects
   459 		global $db, $session, $paths, $template, $plugins; // Common objects
   460     global $lang;
   460 		global $lang;
   461     global $timezone;
   461 		global $timezone;
   462     if($this->started) return;
   462 		if($this->started) return;
   463     $this->started = true;
   463 		$this->started = true;
   464     $user = false;
   464 		$user = false;
   465     if ( isset($_COOKIE['sid']) )
   465 		if ( isset($_COOKIE['sid']) )
   466     {
   466 		{
   467       if ( $this->compat )
   467 			if ( $this->compat )
   468       {
   468 			{
   469         $userdata = $this->compat_validate_session($_COOKIE['sid']);
   469 				$userdata = $this->compat_validate_session($_COOKIE['sid']);
   470       }
   470 			}
   471       else
   471 			else
   472       {
   472 			{
   473         $userdata = $this->validate_session($_COOKIE['sid']);
   473 				$userdata = $this->validate_session($_COOKIE['sid']);
   474       }
   474 			}
   475       if ( is_array($userdata) )
   475 			if ( is_array($userdata) )
   476       {
   476 			{
   477         $this->sid = $_COOKIE['sid'];
   477 				$this->sid = $_COOKIE['sid'];
   478         $this->user_logged_in = true;
   478 				$this->user_logged_in = true;
   479         $this->user_id =       intval($userdata['user_id']);
   479 				$this->user_id =       intval($userdata['user_id']);
   480         $this->username =      $userdata['username'];
   480 				$this->username =      $userdata['username'];
   481         $this->user_level =    intval($userdata['user_level']);
   481 				$this->user_level =    intval($userdata['user_level']);
   482         $this->real_name =     $userdata['real_name'];
   482 				$this->real_name =     $userdata['real_name'];
   483         $this->email =         $userdata['email'];
   483 				$this->email =         $userdata['email'];
   484         $this->unread_pms =    $userdata['num_pms'];
   484 				$this->unread_pms =    $userdata['num_pms'];
   485         $this->user_title =    ( isset($userdata['user_title']) ) ? $userdata['user_title'] : null;
   485 				$this->user_title =    ( isset($userdata['user_title']) ) ? $userdata['user_title'] : null;
   486         if(!$this->compat)
   486 				if(!$this->compat)
   487         {
   487 				{
   488           $this->theme =         $userdata['theme'];
   488 					$this->theme =         $userdata['theme'];
   489           $this->style =         $userdata['style'];
   489 					$this->style =         $userdata['style'];
   490           $this->signature =     $userdata['signature'];
   490 					$this->signature =     $userdata['signature'];
   491           $this->reg_time =      $userdata['reg_time'];
   491 					$this->reg_time =      $userdata['reg_time'];
   492         }
   492 				}
   493         $this->auth_level =    USER_LEVEL_MEMBER;
   493 				$this->auth_level =    USER_LEVEL_MEMBER;
   494         // generate an anti-CSRF token
   494 				// generate an anti-CSRF token
   495         $this->csrf_token =    sha1($this->username . $this->sid . $this->user_id);
   495 				$this->csrf_token =    sha1($this->username . $this->sid . $this->user_id);
   496         if(!isset($template->named_theme_list[$this->theme]))
   496 				if(!isset($template->named_theme_list[$this->theme]))
   497         {
   497 				{
   498           if($this->compat || !is_object($template))
   498 					if($this->compat || !is_object($template))
   499           {
   499 					{
   500             $this->theme = 'oxygen';
   500 						$this->theme = 'oxygen';
   501             $this->style = 'bleu';
   501 						$this->style = 'bleu';
   502           }
   502 					}
   503           else
   503 					else
   504           {
   504 					{
   505             $this->theme = $template->default_theme;
   505 						$this->theme = $template->default_theme;
   506             $this->style = $template->default_style;
   506 						$this->style = $template->default_style;
   507           }
   507 					}
   508         }
   508 				}
   509         $user = true;
   509 				$user = true;
   510         
   510 				
   511         // set timezone params
   511 				// set timezone params
   512         $GLOBALS['timezone'] = $userdata['user_timezone'];
   512 				$GLOBALS['timezone'] = $userdata['user_timezone'];
   513         $GLOBALS['dst_params'] = explode(';', $userdata['user_dst']);
   513 				$GLOBALS['dst_params'] = explode(';', $userdata['user_dst']);
   514         foreach ( $GLOBALS['dst_params'] as &$parm )
   514 				foreach ( $GLOBALS['dst_params'] as &$parm )
   515         {
   515 				{
   516           if ( substr($parm, -1) != 'd' )
   516 					if ( substr($parm, -1) != 'd' )
   517             $parm = intval($parm);
   517 						$parm = intval($parm);
   518         }
   518 				}
   519         
   519 				
   520         // Set language
   520 				// Set language
   521         if ( !defined('ENANO_ALLOW_LOAD_NOLANG') )
   521 				if ( !defined('ENANO_ALLOW_LOAD_NOLANG') )
   522         {
   522 				{
   523           $lang_id = intval($userdata['user_lang']);
   523 					$lang_id = intval($userdata['user_lang']);
   524           $lang = new Language($lang_id);
   524 					$lang = new Language($lang_id);
   525           @setlocale(LC_ALL, $lang->lang_code);
   525 					@setlocale(LC_ALL, $lang->lang_code);
   526         }
   526 				}
   527         
   527 				
   528         if(isset($_REQUEST['auth']) && !$this->sid_super)
   528 				if(isset($_REQUEST['auth']) && !$this->sid_super)
   529         {
   529 				{
   530           // Now he thinks he's a moderator. Or maybe even an administrator. Let's find out if he's telling the truth.
   530 					// Now he thinks he's a moderator. Or maybe even an administrator. Let's find out if he's telling the truth.
   531           if($this->compat)
   531 					if($this->compat)
   532           {
   532 					{
   533             $key = $_REQUEST['auth'];
   533 						$key = $_REQUEST['auth'];
   534             $super = $this->compat_validate_session($key);
   534 						$super = $this->compat_validate_session($key);
   535           }
   535 					}
   536           else
   536 					else
   537           {
   537 					{
   538             $key = $_REQUEST['auth'];
   538 						$key = $_REQUEST['auth'];
   539             if ( !empty($key) && ( strlen($key) / 2 ) % 4 == 0 )
   539 						if ( !empty($key) && ( strlen($key) / 2 ) % 4 == 0 )
   540             {
   540 						{
   541               $super = $this->validate_session($key);
   541 							$super = $this->validate_session($key);
   542             }
   542 						}
   543           }
   543 					}
   544           if(is_array(@$super))
   544 					if(is_array(@$super))
   545           {
   545 					{
   546             $this->auth_level = intval($super['auth_level']);
   546 						$this->auth_level = intval($super['auth_level']);
   547             $this->sid_super = $_REQUEST['auth'];
   547 						$this->sid_super = $_REQUEST['auth'];
   548           }
   548 					}
   549         }
   549 				}
   550       }
   550 			}
   551     }
   551 		}
   552     if(!$user)
   552 		if(!$user)
   553     {
   553 		{
   554       //exit;
   554 			//exit;
   555       $this->register_guest_session();
   555 			$this->register_guest_session();
   556     }
   556 		}
   557     if(!$this->compat)
   557 		if(!$this->compat)
   558     {
   558 		{
   559       // init groups
   559 			// init groups
   560       $q = $this->sql('SELECT g.group_name,g.group_id,m.is_mod FROM '.table_prefix.'groups AS g' . "\n"
   560 			$q = $this->sql('SELECT g.group_name,g.group_id,m.is_mod FROM '.table_prefix.'groups AS g' . "\n"
   561         . '  LEFT JOIN '.table_prefix.'group_members AS m' . "\n"
   561 				. '  LEFT JOIN '.table_prefix.'group_members AS m' . "\n"
   562         . '    ON g.group_id=m.group_id' . "\n"
   562 				. '    ON g.group_id=m.group_id' . "\n"
   563         . '  WHERE ( m.user_id='.$this->user_id.'' . "\n" 
   563 				. '  WHERE ( m.user_id='.$this->user_id.'' . "\n" 
   564         . '    OR g.group_name=\'Everyone\')' . "\n"
   564 				. '    OR g.group_name=\'Everyone\')' . "\n"
   565         . '    ' . ( /* quick hack for upgrade compatibility reasons */ enano_version() == '1.0RC1' ? '' : 'AND ( m.pending != 1 OR m.pending IS NULL )' ) . '' . "\n"
   565 				. '    ' . ( /* quick hack for upgrade compatibility reasons */ enano_version() == '1.0RC1' ? '' : 'AND ( m.pending != 1 OR m.pending IS NULL )' ) . '' . "\n"
   566         . '  ORDER BY group_id ASC;'); // The ORDER BY is to make sure "Everyone" comes first so the permissions can be overridden
   566 				. '  ORDER BY group_id ASC;'); // The ORDER BY is to make sure "Everyone" comes first so the permissions can be overridden
   567       if($row = $db->fetchrow())
   567 			if($row = $db->fetchrow())
   568       {
   568 			{
   569         do {
   569 				do {
   570           $this->groups[$row['group_id']] = $row['group_name'];
   570 					$this->groups[$row['group_id']] = $row['group_name'];
   571           $this->group_mod[$row['group_id']] = ( intval($row['is_mod']) == 1 );
   571 					$this->group_mod[$row['group_id']] = ( intval($row['is_mod']) == 1 );
   572         } while($row = $db->fetchrow());
   572 				} while($row = $db->fetchrow());
   573       }
   573 			}
   574       else
   574 			else
   575       {
   575 			{
   576         die('No group info');
   576 				die('No group info');
   577       }
   577 			}
   578       profiler_log('Fetched group memberships');
   578 			profiler_log('Fetched group memberships');
   579     }
   579 		}
   580     
   580 		
   581     // make sure we aren't banned
   581 		// make sure we aren't banned
   582     $this->check_banlist();
   582 		$this->check_banlist();
   583     
   583 		
   584     // make sure the account is active
   584 		// make sure the account is active
   585     if ( !$this->compat && $this->user_logged_in && $userdata['account_active'] < 1 && !$this->on_critical_page() )
   585 		if ( !$this->compat && $this->user_logged_in && $userdata['account_active'] < 1 && !$this->on_critical_page() )
   586     {
   586 		{
   587       $this->show_inactive_error($userdata);
   587 			$this->show_inactive_error($userdata);
   588     }
   588 		}
   589     
   589 		
   590     // Printable page view? Probably the wrong place to control
   590 		// Printable page view? Probably the wrong place to control
   591     // it but $template is pretty dumb, it will just about always
   591 		// it but $template is pretty dumb, it will just about always
   592     // do what you ask it to do, which isn't always what we want
   592 		// do what you ask it to do, which isn't always what we want
   593     if ( isset ( $_GET['printable'] ) )
   593 		if ( isset ( $_GET['printable'] ) )
   594     {
   594 		{
   595       $this->theme = 'printable';
   595 			$this->theme = 'printable';
   596       $this->style = 'default';
   596 			$this->style = 'default';
   597     }
   597 		}
   598     
   598 		
   599     // setup theme ACLs
   599 		// setup theme ACLs
   600     $template->process_theme_acls();
   600 		$template->process_theme_acls();
   601     
   601 		
   602     profiler_log('Sessions started. Banlist and theme ACLs initialized');
   602 		profiler_log('Sessions started. Banlist and theme ACLs initialized');
   603   }
   603 	}
   604   
   604 	
   605   # Logins
   605 	# Logins
   606   
   606 	
   607   /**
   607 	/**
   608    * Attempts to perform a login using crypto functions
   608  	* Attempts to perform a login using crypto functions
   609    * @param string $username The username
   609  	* @param string $username The username
   610    * @param string $aes_data The encrypted password, hex-encoded
   610  	* @param string $aes_data The encrypted password, hex-encoded
   611    * @param string $aes_key The MD5 hash of the encryption key, hex-encoded
   611  	* @param string $aes_key The MD5 hash of the encryption key, hex-encoded
   612    * @param string $challenge The 256-bit MD5 challenge string - first 128 bits should be the hash, the last 128 should be the challenge salt
   612  	* @param string $challenge The 256-bit MD5 challenge string - first 128 bits should be the hash, the last 128 should be the challenge salt
   613    * @param int $level The privilege level we're authenticating for, defaults to 0
   613  	* @param int $level The privilege level we're authenticating for, defaults to 0
   614    * @param string $captcha_hash Optional. If we're locked out and the lockout policy is captcha, this should be the identifier for the code.
   614  	* @param string $captcha_hash Optional. If we're locked out and the lockout policy is captcha, this should be the identifier for the code.
   615    * @param string $captcha_code Optional. If we're locked out and the lockout policy is captcha, this should be the code the user entered.
   615  	* @param string $captcha_code Optional. If we're locked out and the lockout policy is captcha, this should be the code the user entered.
   616    * @param bool $remember Optional. If true, remembers the session for X days. Otherwise, assigns a short session. Defaults to false.
   616  	* @param bool $remember Optional. If true, remembers the session for X days. Otherwise, assigns a short session. Defaults to false.
   617    * @param bool $lookup_key Optional. If true (default) this queries the database for the "real" encryption key. Else, uses what is given.
   617  	* @param bool $lookup_key Optional. If true (default) this queries the database for the "real" encryption key. Else, uses what is given.
   618    * @return string 'success' on success, or error string on failure
   618  	* @return string 'success' on success, or error string on failure
   619    */
   619  	*/
   620    
   620  	
   621   function login_with_crypto($username, $aes_data, $aes_key_id, $challenge, $level = USER_LEVEL_MEMBER, $captcha_hash = false, $captcha_code = false, $remember = false, $lookup_key = true)
   621 	function login_with_crypto($username, $aes_data, $aes_key_id, $challenge, $level = USER_LEVEL_MEMBER, $captcha_hash = false, $captcha_code = false, $remember = false, $lookup_key = true)
   622   {
   622 	{
   623     global $db, $session, $paths, $template, $plugins; // Common objects
   623 		global $db, $session, $paths, $template, $plugins; // Common objects
   624     
   624 		
   625     // Instanciate the Rijndael encryption object
   625 		// Instanciate the Rijndael encryption object
   626     $aes = AESCrypt::singleton(AES_BITS, AES_BLOCKSIZE);
   626 		$aes = AESCrypt::singleton(AES_BITS, AES_BLOCKSIZE);
   627     
   627 		
   628     // Fetch our decryption key
   628 		// Fetch our decryption key
   629     
   629 		
   630     if ( $lookup_key )
   630 		if ( $lookup_key )
   631     {
   631 		{
   632       $aes_key = $this->fetch_public_key($aes_key_id);
   632 			$aes_key = $this->fetch_public_key($aes_key_id);
   633       if ( !$aes_key )
   633 			if ( !$aes_key )
   634       {
   634 			{
   635         // It could be that our key cache is full. If it seems larger than 65KB, clear it
   635 				// It could be that our key cache is full. If it seems larger than 65KB, clear it
   636         if ( strlen(getConfig('login_key_cache')) > 65000 )
   636 				if ( strlen(getConfig('login_key_cache')) > 65000 )
   637         {
   637 				{
   638           setConfig('login_key_cache', '');
   638 					setConfig('login_key_cache', '');
   639           return array(
   639 					return array(
   640             'success' => false,
   640 						'success' => false,
   641             'error' => 'key_not_found_cleared',
   641 						'error' => 'key_not_found_cleared',
   642             );
   642 						);
   643         }
   643 				}
   644         return array(
   644 				return array(
   645           'success' => false,
   645 					'success' => false,
   646           'error' => 'key_not_found'
   646 					'error' => 'key_not_found'
   647           );
   647 					);
   648       }
   648 			}
   649     }
   649 		}
   650     else
   650 		else
   651     {
   651 		{
   652       $aes_key =& $aes_key_id;
   652 			$aes_key =& $aes_key_id;
   653     }
   653 		}
   654     
   654 		
   655     // Convert the key to a binary string
   655 		// Convert the key to a binary string
   656     $bin_key = hexdecode($aes_key);
   656 		$bin_key = hexdecode($aes_key);
   657     
   657 		
   658     if(strlen($bin_key) != AES_BITS / 8)
   658 		if(strlen($bin_key) != AES_BITS / 8)
   659       return array(
   659 			return array(
   660         'success' => false,
   660 				'success' => false,
   661         'error' => 'key_wrong_length'
   661 				'error' => 'key_wrong_length'
   662         );
   662 				);
   663     
   663 		
   664     // Decrypt our password
   664 		// Decrypt our password
   665     $password = $aes->decrypt($aes_data, $bin_key, ENC_HEX);
   665 		$password = $aes->decrypt($aes_data, $bin_key, ENC_HEX);
   666     
   666 		
   667     // Let the LoginAPI do the rest.
   667 		// Let the LoginAPI do the rest.
   668     return $this->login_without_crypto($username, $password, false, $level, $captcha_hash, $captcha_code, $remember);
   668 		return $this->login_without_crypto($username, $password, false, $level, $captcha_hash, $captcha_code, $remember);
   669   }
   669 	}
   670   
   670 	
   671   /**
   671 	/**
   672    * Attempts to login without using crypto stuff, mainly for use when the other side doesn't like Javascript
   672  	* Attempts to login without using crypto stuff, mainly for use when the other side doesn't like Javascript
   673    * This method of authentication is inherently insecure, there's really nothing we can do about it except hope and pray that everyone moves to Firefox
   673  	* This method of authentication is inherently insecure, there's really nothing we can do about it except hope and pray that everyone moves to Firefox
   674    * Technically it still uses crypto, but it only decrypts the password already stored, which is (obviously) required for authentication
   674  	* Technically it still uses crypto, but it only decrypts the password already stored, which is (obviously) required for authentication
   675    * @param string $username The username
   675  	* @param string $username The username
   676    * @param string $password The password -OR- the MD5 hash of the password if $already_md5ed is true
   676  	* @param string $password The password -OR- the MD5 hash of the password if $already_md5ed is true
   677    * @param bool $already_md5ed This should be set to true if $password is an MD5 hash, and should be false if it's plaintext. Defaults to false.
   677  	* @param bool $already_md5ed This should be set to true if $password is an MD5 hash, and should be false if it's plaintext. Defaults to false.
   678    * @param int $level The privilege level we're authenticating for, defaults to 0
   678  	* @param int $level The privilege level we're authenticating for, defaults to 0
   679    * @param bool $remember Optional. If true, remembers the session for X days. Otherwise, assigns a short session. Defaults to false.
   679  	* @param bool $remember Optional. If true, remembers the session for X days. Otherwise, assigns a short session. Defaults to false.
   680    */
   680  	*/
   681   
   681 	
   682   function login_without_crypto($username, $password, $already_md5ed = false, $level = USER_LEVEL_MEMBER, $remember = false)
   682 	function login_without_crypto($username, $password, $already_md5ed = false, $level = USER_LEVEL_MEMBER, $remember = false)
   683   {
   683 	{
   684     global $db, $session, $paths, $template, $plugins; // Common objects
   684 		global $db, $session, $paths, $template, $plugins; // Common objects
   685     
   685 		
   686     if ( $already_md5ed )
   686 		if ( $already_md5ed )
   687     {
   687 		{
   688       // No longer supported
   688 			// No longer supported
   689       return array(
   689 			return array(
   690           'mode' => 'error',
   690 					'mode' => 'error',
   691           'error' => '$already_md5ed is no longer supported (set this parameter to false and make sure the password you send to $session->login_without_crypto() is not hashed)'
   691 					'error' => '$already_md5ed is no longer supported (set this parameter to false and make sure the password you send to $session->login_without_crypto() is not hashed)'
   692         );
   692 				);
   693     }
   693 		}
   694     
   694 		
   695     // Replace underscores with spaces in username
   695 		// Replace underscores with spaces in username
   696     // (Added in 1.0.2)
   696 		// (Added in 1.0.2)
   697     $username = str_replace('_', ' ', $username);
   697 		$username = str_replace('_', ' ', $username);
   698     
   698 		
   699     // Perhaps we're upgrading Enano?
   699 		// Perhaps we're upgrading Enano?
   700     if($this->compat)
   700 		if($this->compat)
   701     {
   701 		{
   702       return $this->login_compat($username, md5($password), $level);
   702 			return $this->login_compat($username, md5($password), $level);
   703     }
   703 		}
   704     
   704 		
   705     // Instanciate the Rijndael encryption object
   705 		// Instanciate the Rijndael encryption object
   706     $aes = AESCrypt::singleton(AES_BITS, AES_BLOCKSIZE);
   706 		$aes = AESCrypt::singleton(AES_BITS, AES_BLOCKSIZE);
   707     
   707 		
   708     // Initialize our success switch
   708 		// Initialize our success switch
   709     $success = false;
   709 		$success = false;
   710     
   710 		
   711     // Retrieve the real password from the database
   711 		// Retrieve the real password from the database
   712     $username_db = $db->escape(strtolower($username));
   712 		$username_db = $db->escape(strtolower($username));
   713     $username_db_upper = $db->escape($username);
   713 		$username_db_upper = $db->escape($username);
   714     if ( !$db->sql_query('SELECT password,password_salt,old_encryption,user_id,user_level,temp_password,temp_password_time FROM '.table_prefix."users\n"
   714 		if ( !$db->sql_query('SELECT password,password_salt,old_encryption,user_id,user_level,temp_password,temp_password_time FROM '.table_prefix."users\n"
   715                        . "  WHERE ( " . ENANO_SQLFUNC_LOWERCASE . "(username) = '$username_db' OR username = '$username_db_upper' );") )
   715  											. "  WHERE ( " . ENANO_SQLFUNC_LOWERCASE . "(username) = '$username_db' OR username = '$username_db_upper' );") )
   716     {
   716 		{
   717       $this->sql('SELECT password,\'\' AS password_salt,old_encryption,user_id,user_level,temp_password,temp_password_time FROM '.table_prefix."users\n"
   717 			$this->sql('SELECT password,\'\' AS password_salt,old_encryption,user_id,user_level,temp_password,temp_password_time FROM '.table_prefix."users\n"
   718                . "  WHERE ( " . ENANO_SQLFUNC_LOWERCASE . "(username) = '$username_db' OR username = '$username_db_upper' );");
   718  							. "  WHERE ( " . ENANO_SQLFUNC_LOWERCASE . "(username) = '$username_db' OR username = '$username_db_upper' );");
   719     }
   719 		}
   720     if ( $db->numrows() < 1 )
   720 		if ( $db->numrows() < 1 )
   721     {
   721 		{
   722       // This wasn't logged in <1.0.2, dunno how it slipped through
   722 			// This wasn't logged in <1.0.2, dunno how it slipped through
   723       if ( $level > USER_LEVEL_MEMBER )
   723 			if ( $level > USER_LEVEL_MEMBER )
   724         $this->sql('INSERT INTO ' . table_prefix . "logs(log_type,action,time_id,date_string,author,edit_summary,page_text) VALUES\n"
   724 				$this->sql('INSERT INTO ' . table_prefix . "logs(log_type,action,time_id,date_string,author,edit_summary,page_text) VALUES\n"
   725                    . '  (\'security\', \'admin_auth_bad\', '.time().', \''.enano_date(ED_DATE | ED_TIME).'\', \''.$db->escape($username).'\', '
   725  									. '  (\'security\', \'admin_auth_bad\', '.time().', \''.enano_date(ED_DATE | ED_TIME).'\', \''.$db->escape($username).'\', '
   726                       . '\''.$db->escape($_SERVER['REMOTE_ADDR']).'\', ' . intval($level) . ')');
   726 											. '\''.$db->escape($_SERVER['REMOTE_ADDR']).'\', ' . intval($level) . ')');
   727       else
   727 			else
   728         $this->sql('INSERT INTO ' . table_prefix . "logs(log_type,action,time_id,date_string,author,edit_summary) VALUES\n"
   728 				$this->sql('INSERT INTO ' . table_prefix . "logs(log_type,action,time_id,date_string,author,edit_summary) VALUES\n"
   729                    . '  (\'security\', \'auth_bad\', '.time().', \''.enano_date(ED_DATE | ED_TIME).'\', \''.$db->escape($username).'\', '
   729  									. '  (\'security\', \'auth_bad\', '.time().', \''.enano_date(ED_DATE | ED_TIME).'\', \''.$db->escape($username).'\', '
   730                       . '\''.$db->escape($_SERVER['REMOTE_ADDR']).'\')');
   730 											. '\''.$db->escape($_SERVER['REMOTE_ADDR']).'\')');
   731       
   731 			
   732       // Do we also need to increment the lockout countdown?
   732 			// Do we also need to increment the lockout countdown?
   733       if ( !defined('IN_ENANO_INSTALL') )
   733 			if ( !defined('IN_ENANO_INSTALL') )
   734         $lockout_data = $this->get_lockout_info();
   734 				$lockout_data = $this->get_lockout_info();
   735       else
   735 			else
   736         $lockout_data = array(
   736 				$lockout_data = array(
   737           'lockout_policy' => 'disable'
   737 					'lockout_policy' => 'disable'
   738           );
   738 					);
   739       
   739 			
   740       if ( $lockout_data['policy'] != 'disable' && !defined('IN_ENANO_INSTALL') )
   740 			if ( $lockout_data['policy'] != 'disable' && !defined('IN_ENANO_INSTALL') )
   741       {
   741 			{
   742         $ipaddr = $db->escape($_SERVER['REMOTE_ADDR']);
   742 				$ipaddr = $db->escape($_SERVER['REMOTE_ADDR']);
   743         // increment fail count
   743 				// increment fail count
   744         $this->sql('INSERT INTO '.table_prefix.'lockout(ipaddr, timestamp, action, username) VALUES(\'' . $ipaddr . '\', ' . time() . ', \'credential\', \'' . $db->escape($username) . '\');');
   744 				$this->sql('INSERT INTO '.table_prefix.'lockout(ipaddr, timestamp, action, username) VALUES(\'' . $ipaddr . '\', ' . time() . ', \'credential\', \'' . $db->escape($username) . '\');');
   745         $lockout_data['fails']++;
   745 				$lockout_data['fails']++;
   746         return array(
   746 				return array(
   747             'success' => false,
   747 						'success' => false,
   748             'error' => ( $lockout_data['fails'] >= $lockout_data['threshold'] ) ? 'locked_out' : 'invalid_credentials',
   748 						'error' => ( $lockout_data['fails'] >= $lockout_data['threshold'] ) ? 'locked_out' : 'invalid_credentials',
   749             'lockout_threshold' => $lockout_data['threshold'],
   749 						'lockout_threshold' => $lockout_data['threshold'],
   750             'lockout_duration' => ( $lockout_data['duration'] ),
   750 						'lockout_duration' => ( $lockout_data['duration'] ),
   751             'lockout_fails' => $lockout_data['fails'],
   751 						'lockout_fails' => $lockout_data['fails'],
   752             'lockout_policy' => $lockout_data['policy']
   752 						'lockout_policy' => $lockout_data['policy']
   753           );
   753 					);
   754       }
   754 			}
   755       
   755 			
   756       return array(
   756 			return array(
   757         'success' => false,
   757 				'success' => false,
   758         'error' => 'invalid_credentials'
   758 				'error' => 'invalid_credentials'
   759       );
   759 			);
   760     }
   760 		}
   761     $row = $db->fetchrow();
   761 		$row = $db->fetchrow();
   762     
   762 		
   763     // Check to see if we're logging in using a temporary password
   763 		// Check to see if we're logging in using a temporary password
   764     
   764 		
   765     if((intval($row['temp_password_time']) + 3600*24) > time() )
   765 		if((intval($row['temp_password_time']) + 3600*24) > time() )
   766     {
   766 		{
   767       $temp_pass = hmac_sha1($password, $row['password_salt']);
   767 			$temp_pass = hmac_sha1($password, $row['password_salt']);
   768       if( $temp_pass === $row['temp_password'] )
   768 			if( $temp_pass === $row['temp_password'] )
   769       {
   769 			{
   770         $code = $plugins->setHook('login_password_reset');
   770 				$code = $plugins->setHook('login_password_reset');
   771         foreach ( $code as $cmd )
   771 				foreach ( $code as $cmd )
   772         {
   772 				{
   773           eval($cmd);
   773 					eval($cmd);
   774         }
   774 				}
   775         
   775 				
   776         return array(
   776 				return array(
   777             'success' => false,
   777 						'success' => false,
   778             'error' => 'valid_reset',
   778 						'error' => 'valid_reset',
   779             'redirect_url' => makeUrlComplete('Special', 'PasswordReset/stage2/' . $row['user_id'] . '/' . $this->pk_encrypt($password))
   779 						'redirect_url' => makeUrlComplete('Special', 'PasswordReset/stage2/' . $row['user_id'] . '/' . $this->pk_encrypt($password))
   780           );
   780 					);
   781       }
   781 			}
   782     }
   782 		}
   783     
   783 		
   784     if ( $row['old_encryption'] == 1 )
   784 		if ( $row['old_encryption'] == 1 )
   785     {
   785 		{
   786       // The user's password is stored using the obsolete and insecure MD5 algorithm - we'll update the field with the new password
   786 			// The user's password is stored using the obsolete and insecure MD5 algorithm - we'll update the field with the new password
   787       if(md5($password) === $row['password'])
   787 			if(md5($password) === $row['password'])
   788       {
   788 			{
   789         if ( !defined('IN_ENANO_UPGRADE') )
   789 				if ( !defined('IN_ENANO_UPGRADE') )
   790         {
   790 				{
   791           $hmac_secret = hexencode(AESCrypt::randkey(20), '', '');
   791 					$hmac_secret = hexencode(AESCrypt::randkey(20), '', '');
   792           $password_hmac = hmac_sha1($password, $hmac_secret);
   792 					$password_hmac = hmac_sha1($password, $hmac_secret);
   793           $this->sql('UPDATE '.table_prefix."users SET password = '$password_hmac', password_salt = '$hmac_secret', old_encryption = 0 WHERE user_id={$row['user_id']};");
   793 					$this->sql('UPDATE '.table_prefix."users SET password = '$password_hmac', password_salt = '$hmac_secret', old_encryption = 0 WHERE user_id={$row['user_id']};");
   794         }
   794 				}
   795         $success = true;
   795 				$success = true;
   796       }
   796 			}
   797     }
   797 		}
   798     else if ( $row['old_encryption'] == 2 || ( defined('ENANO_UPGRADE_USE_AES_PASSWORDS') ) && strlen($row['password']) != 40 )
   798 		else if ( $row['old_encryption'] == 2 || ( defined('ENANO_UPGRADE_USE_AES_PASSWORDS') ) && strlen($row['password']) != 40 )
   799     {
   799 		{
   800       // Our password field uses the 1.0RC1-1.1.5 encryption format
   800 			// Our password field uses the 1.0RC1-1.1.5 encryption format
   801       $real_pass = $aes->decrypt($row['password'], $this->private_key);
   801 			$real_pass = $aes->decrypt($row['password'], $this->private_key);
   802       if($password === $real_pass)
   802 			if($password === $real_pass)
   803       {
   803 			{
   804         if ( !defined('IN_ENANO_UPGRADE') )
   804 				if ( !defined('IN_ENANO_UPGRADE') )
   805         {
   805 				{
   806           $hmac_secret = hexencode(AESCrypt::randkey(20), '', '');
   806 					$hmac_secret = hexencode(AESCrypt::randkey(20), '', '');
   807           $password_hmac = hmac_sha1($password, $hmac_secret);
   807 					$password_hmac = hmac_sha1($password, $hmac_secret);
   808           $this->sql('UPDATE '.table_prefix."users SET password = '$password_hmac', password_salt = '$hmac_secret', old_encryption = 0 WHERE user_id={$row['user_id']};");
   808 					$this->sql('UPDATE '.table_prefix."users SET password = '$password_hmac', password_salt = '$hmac_secret', old_encryption = 0 WHERE user_id={$row['user_id']};");
   809         }
   809 				}
   810         $success = true;
   810 				$success = true;
   811       }
   811 			}
   812     }
   812 		}
   813     else
   813 		else
   814     {
   814 		{
   815       // Password uses HMAC-SHA1
   815 			// Password uses HMAC-SHA1
   816       $user_challenge = hmac_sha1($password, $row['password_salt']);
   816 			$user_challenge = hmac_sha1($password, $row['password_salt']);
   817       $password_hmac =& $row['password'];
   817 			$password_hmac =& $row['password'];
   818       if ( $user_challenge === $password_hmac )
   818 			if ( $user_challenge === $password_hmac )
   819       {
   819 			{
   820         $success = true;
   820 				$success = true;
   821       }
   821 			}
   822     }
   822 		}
   823     if($success)
   823 		if($success)
   824     {
   824 		{
   825       if((int)$level > (int)$row['user_level'])
   825 			if((int)$level > (int)$row['user_level'])
   826         return array(
   826 				return array(
   827           'success' => false,
   827 					'success' => false,
   828           'error' => 'too_big_for_britches'
   828 					'error' => 'too_big_for_britches'
   829         );
   829 				);
   830       
   830 			
   831       // grant session
   831 			// grant session
   832       $sess = $this->register_session($row['user_id'], $username, ( isset($password_hmac) ? $password_hmac : $password ), $level, $remember);
   832 			$sess = $this->register_session($row['user_id'], $username, ( isset($password_hmac) ? $password_hmac : $password ), $level, $remember);
   833       
   833 			
   834       if($sess)
   834 			if($sess)
   835       {
   835 			{
   836         if($level > USER_LEVEL_MEMBER)
   836 				if($level > USER_LEVEL_MEMBER)
   837           $this->sql('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,date_string,author,author_uid,edit_summary,page_text) VALUES(\'security\', \'admin_auth_good\', '.time().', \''.enano_date(ED_DATE | ED_TIME).'\', \''.$db->escape($username).'\', ' . $row['user_id'] . ', \''.$db->escape($_SERVER['REMOTE_ADDR']).'\', ' . intval($level) . ')');
   837 					$this->sql('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,date_string,author,author_uid,edit_summary,page_text) VALUES(\'security\', \'admin_auth_good\', '.time().', \''.enano_date(ED_DATE | ED_TIME).'\', \''.$db->escape($username).'\', ' . $row['user_id'] . ', \''.$db->escape($_SERVER['REMOTE_ADDR']).'\', ' . intval($level) . ')');
   838         else
   838 				else
   839           $this->sql('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,date_string,author,author_uid,edit_summary) VALUES(\'security\', \'auth_good\', '.time().', \''.enano_date(ED_DATE | ED_TIME).'\', \''.$db->escape($username).'\', ' . $row['user_id'] . ', \''.$db->escape($_SERVER['REMOTE_ADDR']).'\')');
   839 					$this->sql('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,date_string,author,author_uid,edit_summary) VALUES(\'security\', \'auth_good\', '.time().', \''.enano_date(ED_DATE | ED_TIME).'\', \''.$db->escape($username).'\', ' . $row['user_id'] . ', \''.$db->escape($_SERVER['REMOTE_ADDR']).'\')');
   840         
   840 				
   841         $code = $plugins->setHook('login_success');
   841 				$code = $plugins->setHook('login_success');
   842         foreach ( $code as $cmd )
   842 				foreach ( $code as $cmd )
   843         {
   843 				{
   844           eval($cmd);
   844 					eval($cmd);
   845         }
   845 				}
   846         
   846 				
   847         return array(
   847 				return array(
   848           'success' => true
   848 					'success' => true
   849           );
   849 					);
   850       }
   850 			}
   851       else
   851 			else
   852         return array(
   852 				return array(
   853           'success' => false,
   853 					'success' => false,
   854           'error' => 'backend_fail'
   854 					'error' => 'backend_fail'
   855         );
   855 				);
   856     }
   856 		}
   857     else
   857 		else
   858     {
   858 		{
   859       if($level > USER_LEVEL_MEMBER)
   859 			if($level > USER_LEVEL_MEMBER)
   860         $this->sql('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,date_string,author,edit_summary,page_text) VALUES(\'security\', \'admin_auth_bad\', '.time().', \''.enano_date(ED_DATE | ED_TIME).'\', \''.$db->escape($username).'\', \''.$db->escape($_SERVER['REMOTE_ADDR']).'\', ' . intval($level) . ')');
   860 				$this->sql('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,date_string,author,edit_summary,page_text) VALUES(\'security\', \'admin_auth_bad\', '.time().', \''.enano_date(ED_DATE | ED_TIME).'\', \''.$db->escape($username).'\', \''.$db->escape($_SERVER['REMOTE_ADDR']).'\', ' . intval($level) . ')');
   861       else
   861 			else
   862         $this->sql('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,date_string,author,edit_summary) VALUES(\'security\', \'auth_bad\', '.time().', \''.enano_date(ED_DATE | ED_TIME).'\', \''.$db->escape($username).'\', \''.$db->escape($_SERVER['REMOTE_ADDR']).'\')');
   862 				$this->sql('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,date_string,author,edit_summary) VALUES(\'security\', \'auth_bad\', '.time().', \''.enano_date(ED_DATE | ED_TIME).'\', \''.$db->escape($username).'\', \''.$db->escape($_SERVER['REMOTE_ADDR']).'\')');
   863         
   863 				
   864       // Do we also need to increment the lockout countdown?
   864 			// Do we also need to increment the lockout countdown?
   865       if ( !defined('IN_ENANO_INSTALL') && getConfig('lockout_policy', 'lockout') !== 'disable' )
   865 			if ( !defined('IN_ENANO_INSTALL') && getConfig('lockout_policy', 'lockout') !== 'disable' )
   866       {
   866 			{
   867         $ipaddr = $db->escape($_SERVER['REMOTE_ADDR']);
   867 				$ipaddr = $db->escape($_SERVER['REMOTE_ADDR']);
   868         // increment fail count
   868 				// increment fail count
   869         $this->sql('INSERT INTO '.table_prefix.'lockout(ipaddr, timestamp, action, username) VALUES(\'' . $ipaddr . '\', ' . time() . ', \'credential\', \'' . $db->escape($username) . '\');');
   869 				$this->sql('INSERT INTO '.table_prefix.'lockout(ipaddr, timestamp, action, username) VALUES(\'' . $ipaddr . '\', ' . time() . ', \'credential\', \'' . $db->escape($username) . '\');');
   870       }
   870 			}
   871         
   871 				
   872       return array(
   872 			return array(
   873         'success' => false,
   873 				'success' => false,
   874         'error' => 'invalid_credentials'
   874 				'error' => 'invalid_credentials'
   875       );
   875 			);
   876     }
   876 		}
   877   }
   877 	}
   878   
   878 	
   879   /**
   879 	/**
   880    * Attempts to log in using the old table structure and algorithm. This is for upgrades from old 1.0.x releases.
   880  	* Attempts to log in using the old table structure and algorithm. This is for upgrades from old 1.0.x releases.
   881    * @param string $username
   881  	* @param string $username
   882    * @param string $password This should be an MD5 hash
   882  	* @param string $password This should be an MD5 hash
   883    * @return string 'success' if successful, or error message on failure
   883  	* @return string 'success' if successful, or error message on failure
   884    */
   884  	*/
   885   
   885 	
   886   function login_compat($username, $password, $level = 0)
   886 	function login_compat($username, $password, $level = 0)
   887   {
   887 	{
   888     global $db, $session, $paths, $template, $plugins; // Common objects
   888 		global $db, $session, $paths, $template, $plugins; // Common objects
   889     $pass_hashed =& $password;
   889 		$pass_hashed =& $password;
   890     $this->sql('SELECT password,user_id,user_level FROM '.table_prefix.'users WHERE username=\''.$this->prepare_text($username).'\';');
   890 		$this->sql('SELECT password,user_id,user_level FROM '.table_prefix.'users WHERE username=\''.$this->prepare_text($username).'\';');
   891     if($db->numrows() < 1)
   891 		if($db->numrows() < 1)
   892       return 'The username and/or password is incorrect.';
   892 			return 'The username and/or password is incorrect.';
   893     $row = $db->fetchrow();
   893 		$row = $db->fetchrow();
   894     if($row['password'] == $password)
   894 		if($row['password'] == $password)
   895     {
   895 		{
   896       if((int)$level > (int)$row['user_level'])
   896 			if((int)$level > (int)$row['user_level'])
   897         return 'You are not authorized for this level of access.';
   897 				return 'You are not authorized for this level of access.';
   898       $sess = $this->register_session_compat(intval($row['user_id']), $username, $password, $level);
   898 			$sess = $this->register_session_compat(intval($row['user_id']), $username, $password, $level);
   899       if($sess)
   899 			if($sess)
   900         return 'success';
   900 				return 'success';
   901       else
   901 			else
   902         return 'Your login credentials were correct, but an internal error occured while registering the session key in the database.';
   902 				return 'Your login credentials were correct, but an internal error occured while registering the session key in the database.';
   903     }
   903 		}
   904     else
   904 		else
   905     {
   905 		{
   906       return 'The username and/or password is incorrect.';
   906 			return 'The username and/or password is incorrect.';
   907     }
   907 		}
   908   }
   908 	}
   909   
   909 	
   910   /**
   910 	/**
   911    * Registers a session key in the database. This function *ASSUMES* that the username and password have already been validated!
   911  	* Registers a session key in the database. This function *ASSUMES* that the username and password have already been validated!
   912    * Basically the session key is a hex-encoded cookie (encrypted with the site's private key) that says "u=[username];p=[sha1 of password];s=[unique key id]"
   912  	* Basically the session key is a hex-encoded cookie (encrypted with the site's private key) that says "u=[username];p=[sha1 of password];s=[unique key id]"
   913    * @param int $user_id
   913  	* @param int $user_id
   914    * @param string $username
   914  	* @param string $username
   915    * @param string $password_hmac The HMAC of the user's password, right from the database
   915  	* @param string $password_hmac The HMAC of the user's password, right from the database
   916    * @param int $level The level of access to grant, defaults to USER_LEVEL_MEMBER
   916  	* @param int $level The level of access to grant, defaults to USER_LEVEL_MEMBER
   917    * @param bool $remember Whether the session should be long-term (true) or not (false). Defaults to short-term.
   917  	* @param bool $remember Whether the session should be long-term (true) or not (false). Defaults to short-term.
   918    * @return bool
   918  	* @return bool
   919    */
   919  	*/
   920    
   920  	
   921   function register_session($user_id, $username, $password_hmac, $level = USER_LEVEL_MEMBER, $remember = false)
   921 	function register_session($user_id, $username, $password_hmac, $level = USER_LEVEL_MEMBER, $remember = false)
   922   {
   922 	{
   923     global $db, $session, $paths, $template, $plugins; // Common objects
   923 		global $db, $session, $paths, $template, $plugins; // Common objects
   924     
   924 		
   925     // Random key identifier
   925 		// Random key identifier
   926     $salt = '';
   926 		$salt = '';
   927     for ( $i = 0; $i < 32; $i++ )
   927 		for ( $i = 0; $i < 32; $i++ )
   928     {
   928 		{
   929       $salt .= chr(mt_rand(32, 126));
   929 			$salt .= chr(mt_rand(32, 126));
   930     }
   930 		}
   931     
   931 		
   932     // Session key
   932 		// Session key
   933     if ( defined('ENANO_UPGRADE_USE_AES_PASSWORDS') )
   933 		if ( defined('ENANO_UPGRADE_USE_AES_PASSWORDS') )
   934     {
   934 		{
   935       $session_key = $this->pk_encrypt("u=$username;p=" . sha1($password_hmac) . ";s=$salt");
   935 			$session_key = $this->pk_encrypt("u=$username;p=" . sha1($password_hmac) . ";s=$salt");
   936     }
   936 		}
   937     else
   937 		else
   938     {
   938 		{
   939       $key_pieces = array($password_hmac);
   939 			$key_pieces = array($password_hmac);
   940       $sk_mode = 'generate';
   940 			$sk_mode = 'generate';
   941       $code = $plugins->setHook('session_key_calc');
   941 			$code = $plugins->setHook('session_key_calc');
   942       foreach ( $code as $cmd )
   942 			foreach ( $code as $cmd )
   943       {
   943 			{
   944         eval($cmd);
   944 				eval($cmd);
   945       }
   945 			}
   946       $key_pieces = implode("\xFF", $key_pieces);
   946 			$key_pieces = implode("\xFF", $key_pieces);
   947       
   947 			
   948       $session_key = hmac_sha1($key_pieces, $salt);
   948 			$session_key = hmac_sha1($key_pieces, $salt);
   949     }
   949 		}
   950     
   950 		
   951     // Minimum level
   951 		// Minimum level
   952     $level = max(array($level, USER_LEVEL_MEMBER));
   952 		$level = max(array($level, USER_LEVEL_MEMBER));
   953     
   953 		
   954     // Type of key
   954 		// Type of key
   955     $key_type = ( $level > USER_LEVEL_MEMBER ) ? SK_ELEV : ( $remember ? SK_LONG : SK_SHORT );
   955 		$key_type = ( $level > USER_LEVEL_MEMBER ) ? SK_ELEV : ( $remember ? SK_LONG : SK_SHORT );
   956     
   956 		
   957     // If we're registering an elevated-privilege key, it needs to be on GET
   957 		// If we're registering an elevated-privilege key, it needs to be on GET
   958     if($level > USER_LEVEL_MEMBER)
   958 		if($level > USER_LEVEL_MEMBER)
   959     {
   959 		{
   960       $this->sid_super = $session_key;
   960 			$this->sid_super = $session_key;
   961       $_GET['auth'] = $session_key;
   961 			$_GET['auth'] = $session_key;
   962     }
   962 		}
   963     else
   963 		else
   964     {
   964 		{
   965       // Stash it in a cookie
   965 			// Stash it in a cookie
   966       // For now, make the cookie last forever, we can change this in 1.1.x
   966 			// For now, make the cookie last forever, we can change this in 1.1.x
   967       setcookie( 'sid', $session_key, time()+15552000, scriptPath.'/', null, $GLOBALS['is_https']);
   967 			setcookie( 'sid', $session_key, time()+15552000, scriptPath.'/', null, $GLOBALS['is_https']);
   968       $_COOKIE['sid'] = $session_key;
   968 			$_COOKIE['sid'] = $session_key;
   969       $this->sid = $session_key;
   969 			$this->sid = $session_key;
   970     }
   970 		}
   971     // $keyhash is stored in the database, this is for compatibility with the older DB structure
   971 		// $keyhash is stored in the database, this is for compatibility with the older DB structure
   972     $keyhash = md5($session_key);
   972 		$keyhash = md5($session_key);
   973     // Record the user's IP
   973 		// Record the user's IP
   974     $ip = $_SERVER['REMOTE_ADDR'];
   974 		$ip = $_SERVER['REMOTE_ADDR'];
   975     if(!is_valid_ip($ip))
   975 		if(!is_valid_ip($ip))
   976       die('$session->register_session: Remote-Addr was spoofed');
   976 			die('$session->register_session: Remote-Addr was spoofed');
   977     // The time needs to be stashed to enforce the 15-minute limit on elevated session keys
   977 		// The time needs to be stashed to enforce the 15-minute limit on elevated session keys
   978     $time = time();
   978 		$time = time();
   979     
   979 		
   980     // Sanity check
   980 		// Sanity check
   981     if(!is_int($user_id))
   981 		if(!is_int($user_id))
   982       die('Somehow an SQL injection attempt crawled into our session registrar! (1)');
   982 			die('Somehow an SQL injection attempt crawled into our session registrar! (1)');
   983     if(!is_int($level))
   983 		if(!is_int($level))
   984       die(var_dump($level) . '<br />Somehow an SQL injection attempt crawled into our session registrar! (2)');
   984 			die(var_dump($level) . '<br />Somehow an SQL injection attempt crawled into our session registrar! (2)');
   985     
   985 		
   986     // Update RAM
   986 		// Update RAM
   987     $this->user_id = $user_id;
   987 		$this->user_id = $user_id;
   988     $this->user_level = max(array($this->user_level, $level));
   988 		$this->user_level = max(array($this->user_level, $level));
   989     
   989 		
   990     // All done!
   990 		// All done!
   991     $query = $db->sql_query('INSERT INTO '.table_prefix.'session_keys(session_key, salt, user_id, auth_level, source_ip, time, key_type) VALUES(\''.$keyhash.'\', \''.$db->escape($salt).'\', '.$user_id.', '.$level.', \''.$ip.'\', '.$time.', ' . $key_type . ');');
   991 		$query = $db->sql_query('INSERT INTO '.table_prefix.'session_keys(session_key, salt, user_id, auth_level, source_ip, time, key_type) VALUES(\''.$keyhash.'\', \''.$db->escape($salt).'\', '.$user_id.', '.$level.', \''.$ip.'\', '.$time.', ' . $key_type . ');');
   992     if ( !$query && defined('IN_ENANO_UPGRADE') )
   992 		if ( !$query && defined('IN_ENANO_UPGRADE') )
   993       // we're trying to upgrade so the key_type column is probably missing - try it again without specifying the key type
   993 			// we're trying to upgrade so the key_type column is probably missing - try it again without specifying the key type
   994       $this->sql('INSERT INTO '.table_prefix.'session_keys(session_key, salt, user_id, auth_level, source_ip, time) VALUES(\''.$keyhash.'\', \''.$db->escape($salt).'\', '.$user_id.', '.$level.', \''.$ip.'\', '.$time.');');
   994 			$this->sql('INSERT INTO '.table_prefix.'session_keys(session_key, salt, user_id, auth_level, source_ip, time) VALUES(\''.$keyhash.'\', \''.$db->escape($salt).'\', '.$user_id.', '.$level.', \''.$ip.'\', '.$time.');');
   995       
   995 			
   996     return true;
   996 		return true;
   997   }
   997 	}
   998   
   998 	
   999   /**
   999 	/**
  1000    * Identical to register_session in nature, but uses the old login/table structure. DO NOT use this except in the upgrade script under very controlled circumstances.
  1000  	* Identical to register_session in nature, but uses the old login/table structure. DO NOT use this except in the upgrade script under very controlled circumstances.
  1001    * @see sessionManager::register_session()
  1001  	* @see sessionManager::register_session()
  1002    * @access private
  1002  	* @access private
  1003    */
  1003  	*/
  1004   
  1004 	
  1005   function register_session_compat($user_id, $username, $password, $level = 0)
  1005 	function register_session_compat($user_id, $username, $password, $level = 0)
  1006   {
  1006 	{
  1007     $salt = md5(microtime() . mt_rand());
  1007 		$salt = md5(microtime() . mt_rand());
  1008     $thekey = md5($password . $salt);
  1008 		$thekey = md5($password . $salt);
  1009     if($level > 0)
  1009 		if($level > 0)
  1010     {
  1010 		{
  1011       $this->sid_super = $thekey;
  1011 			$this->sid_super = $thekey;
  1012     }
  1012 		}
  1013     else
  1013 		else
  1014     {
  1014 		{
  1015       setcookie( 'sid', $thekey, time()+315360000, scriptPath.'/' );
  1015 			setcookie( 'sid', $thekey, time()+315360000, scriptPath.'/' );
  1016       $_COOKIE['sid'] = $thekey;
  1016 			$_COOKIE['sid'] = $thekey;
  1017     }
  1017 		}
  1018     $ip = ip2hex($_SERVER['REMOTE_ADDR']);
  1018 		$ip = ip2hex($_SERVER['REMOTE_ADDR']);
  1019     if(!$ip)
  1019 		if(!$ip)
  1020       die('$session->register_session: Remote-Addr was spoofed');
  1020 			die('$session->register_session: Remote-Addr was spoofed');
  1021     $time = time();
  1021 		$time = time();
  1022     if(!is_int($user_id))
  1022 		if(!is_int($user_id))
  1023       die('Somehow an SQL injection attempt crawled into our session registrar! (1)');
  1023 			die('Somehow an SQL injection attempt crawled into our session registrar! (1)');
  1024     if(!is_int($level))
  1024 		if(!is_int($level))
  1025       die('Somehow an SQL injection attempt crawled into our session registrar! (2)');
  1025 			die('Somehow an SQL injection attempt crawled into our session registrar! (2)');
  1026     $query = $this->sql('INSERT INTO '.table_prefix.'session_keys(session_key, salt, user_id, auth_level, source_ip, time) VALUES(\''.$thekey.'\', \''.$salt.'\', '.$user_id.', '.$level.', \''.$ip.'\', '.$time.');');
  1026 		$query = $this->sql('INSERT INTO '.table_prefix.'session_keys(session_key, salt, user_id, auth_level, source_ip, time) VALUES(\''.$thekey.'\', \''.$salt.'\', '.$user_id.', '.$level.', \''.$ip.'\', '.$time.');');
  1027     return true;
  1027 		return true;
  1028   }
  1028 	}
  1029   
  1029 	
  1030   /**
  1030 	/**
  1031    * Tells us if we're locked out from logging in or not.
  1031  	* Tells us if we're locked out from logging in or not.
  1032    * @param reference will be filled with information regarding in-progress lockout
  1032  	* @param reference will be filled with information regarding in-progress lockout
  1033    * @return bool True if locked out, false otherwise
  1033  	* @return bool True if locked out, false otherwise
  1034    */
  1034  	*/
  1035   
  1035 	
  1036   function get_lockout_info()
  1036 	function get_lockout_info()
  1037   {
  1037 	{
  1038     global $db;
  1038 		global $db;
  1039     
  1039 		
  1040     // this has to be initialized to hide warnings
  1040 		// this has to be initialized to hide warnings
  1041     $lockdata = null;
  1041 		$lockdata = null;
  1042     
  1042 		
  1043     // Query database for lockout info
  1043 		// Query database for lockout info
  1044     $locked_out = false;
  1044 		$locked_out = false;
  1045     $threshold = ( $_ = getConfig('lockout_threshold') ) ? intval($_) : 5;
  1045 		$threshold = ( $_ = getConfig('lockout_threshold') ) ? intval($_) : 5;
  1046     $duration  = ( $_ = getConfig('lockout_duration') ) ? intval($_) : 15;
  1046 		$duration  = ( $_ = getConfig('lockout_duration') ) ? intval($_) : 15;
  1047     // convert to seconds
  1047 		// convert to seconds
  1048     $duration  = $duration * 60;
  1048 		$duration  = $duration * 60;
  1049     // decide on policy
  1049 		// decide on policy
  1050     $policy = ( $x = getConfig('lockout_policy') && in_array(getConfig('lockout_policy'), array('lockout', 'disable', 'captcha')) ) ? getConfig('lockout_policy') : 'lockout';
  1050 		$policy = ( $x = getConfig('lockout_policy') && in_array(getConfig('lockout_policy'), array('lockout', 'disable', 'captcha')) ) ? getConfig('lockout_policy') : 'lockout';
  1051     if ( $policy != 'disable' )
  1051 		if ( $policy != 'disable' )
  1052     {
  1052 		{
  1053       // enabled; make decision
  1053 			// enabled; make decision
  1054       $ipaddr = $db->escape($_SERVER['REMOTE_ADDR']);
  1054 			$ipaddr = $db->escape($_SERVER['REMOTE_ADDR']);
  1055       $timestamp_cutoff = time() - $duration;
  1055 			$timestamp_cutoff = time() - $duration;
  1056       $q = $this->sql('SELECT timestamp FROM ' . table_prefix . 'lockout WHERE timestamp > ' . $timestamp_cutoff . ' AND ipaddr = \'' . $ipaddr . '\' ORDER BY timestamp DESC;');
  1056 			$q = $this->sql('SELECT timestamp FROM ' . table_prefix . 'lockout WHERE timestamp > ' . $timestamp_cutoff . ' AND ipaddr = \'' . $ipaddr . '\' ORDER BY timestamp DESC;');
  1057       $fails = $db->numrows($q);
  1057 			$fails = $db->numrows($q);
  1058       $row = $db->fetchrow($q);
  1058 			$row = $db->fetchrow($q);
  1059       $locked_out = ( $fails >= $threshold );
  1059 			$locked_out = ( $fails >= $threshold );
  1060       $lockdata = array(
  1060 			$lockdata = array(
  1061           'active' => $locked_out,
  1061 					'active' => $locked_out,
  1062           'threshold' => $threshold,
  1062 					'threshold' => $threshold,
  1063           'duration' => ( $duration / 60 ),
  1063 					'duration' => ( $duration / 60 ),
  1064           'fails' => $fails,
  1064 					'fails' => $fails,
  1065           'policy' => $policy,
  1065 					'policy' => $policy,
  1066           'last_time' => $row['timestamp'],
  1066 					'last_time' => $row['timestamp'],
  1067           'time_rem' => $locked_out ? ( $duration / 60 ) - round( ( time() - $row['timestamp'] ) / 60 ) : 0,
  1067 					'time_rem' => $locked_out ? ( $duration / 60 ) - round( ( time() - $row['timestamp'] ) / 60 ) : 0,
  1068           'captcha' => $policy == 'captcha' ? $this->make_captcha() : ''
  1068 					'captcha' => $policy == 'captcha' ? $this->make_captcha() : ''
  1069         );
  1069 				);
  1070       $db->free_result();
  1070 			$db->free_result();
  1071     }
  1071 		}
  1072     else
  1072 		else
  1073     {
  1073 		{
  1074       // disabled; send back default dataset
  1074 			// disabled; send back default dataset
  1075       $lockdata = array(
  1075 			$lockdata = array(
  1076         'active' => false,
  1076 				'active' => false,
  1077         'threshold' => $threshold,
  1077 				'threshold' => $threshold,
  1078         'duration' => ( $duration / 60 ),
  1078 				'duration' => ( $duration / 60 ),
  1079         'fails' => 0,
  1079 				'fails' => 0,
  1080         'policy' => $policy,
  1080 				'policy' => $policy,
  1081         'last_time' => 0,
  1081 				'last_time' => 0,
  1082         'time_rem' => 0,
  1082 				'time_rem' => 0,
  1083         'captcha' => ''
  1083 				'captcha' => ''
  1084       );
  1084 			);
  1085     }
  1085 		}
  1086     return $lockdata;
  1086 		return $lockdata;
  1087   }
  1087 	}
  1088   
  1088 	
  1089   /**
  1089 	/**
  1090    * Creates/restores a guest session
  1090  	* Creates/restores a guest session
  1091    * @todo implement real session management for guests
  1091  	* @todo implement real session management for guests
  1092    */
  1092  	*/
  1093    
  1093  	
  1094   function register_guest_session()
  1094 	function register_guest_session()
  1095   {
  1095 	{
  1096     global $db, $session, $paths, $template, $plugins; // Common objects
  1096 		global $db, $session, $paths, $template, $plugins; // Common objects
  1097     global $lang;
  1097 		global $lang;
  1098     $this->username = $_SERVER['REMOTE_ADDR'];
  1098 		$this->username = $_SERVER['REMOTE_ADDR'];
  1099     $this->user_level = USER_LEVEL_GUEST;
  1099 		$this->user_level = USER_LEVEL_GUEST;
  1100     if($this->compat || defined('IN_ENANO_INSTALL'))
  1100 		if($this->compat || defined('IN_ENANO_INSTALL'))
  1101     {
  1101 		{
  1102       $this->theme = 'oxygen';
  1102 			$this->theme = 'oxygen';
  1103       $this->style = 'bleu';
  1103 			$this->style = 'bleu';
  1104     }
  1104 		}
  1105     else
  1105 		else
  1106     {
  1106 		{
  1107       $this->theme = ( isset($_GET['theme']) && isset($template->named_theme_list[$_GET['theme']])) ? $_GET['theme'] : $template->default_theme;
  1107 			$this->theme = ( isset($_GET['theme']) && isset($template->named_theme_list[$_GET['theme']])) ? $_GET['theme'] : $template->default_theme;
  1108       $this->style = ( isset($_GET['style']) && file_exists(ENANO_ROOT.'/themes/'.$this->theme . '/css/'.$_GET['style'].'.css' )) ? $_GET['style'] : preg_replace('/\.css$/', '', $template->named_theme_list[$this->theme]['default_style']);
  1108 			$this->style = ( isset($_GET['style']) && file_exists(ENANO_ROOT.'/themes/'.$this->theme . '/css/'.$_GET['style'].'.css' )) ? $_GET['style'] : preg_replace('/\.css$/', '', $template->named_theme_list[$this->theme]['default_style']);
  1109     }
  1109 		}
  1110     $this->user_id = 1;
  1110 		$this->user_id = 1;
  1111     // This is a VERY special case we are allowing. It lets the installer create languages using the Enano API.
  1111 		// This is a VERY special case we are allowing. It lets the installer create languages using the Enano API.
  1112     if ( !defined('ENANO_ALLOW_LOAD_NOLANG') )
  1112 		if ( !defined('ENANO_ALLOW_LOAD_NOLANG') )
  1113     {
  1113 		{
  1114       $language = ( isset($_GET['lang']) && preg_match('/^[a-z0-9-_]+$/', @$_GET['lang']) ) ? $_GET['lang'] : intval(getConfig('default_language'));
  1114 			$language = ( isset($_GET['lang']) && preg_match('/^[a-z0-9-_]+$/', @$_GET['lang']) ) ? $_GET['lang'] : intval(getConfig('default_language'));
  1115       $lang = new Language($language);
  1115 			$lang = new Language($language);
  1116       @setlocale(LC_ALL, $lang->lang_code);
  1116 			@setlocale(LC_ALL, $lang->lang_code);
  1117     }
  1117 		}
  1118     // make a CSRF token
  1118 		// make a CSRF token
  1119     $this->csrf_token = hmac_sha1($_SERVER['REMOTE_ADDR'], sha1($this->private_key));
  1119 		$this->csrf_token = hmac_sha1($_SERVER['REMOTE_ADDR'], sha1($this->private_key));
  1120   }
  1120 	}
  1121   
  1121 	
  1122   /**
  1122 	/**
  1123    * Validates a session key, and returns the userdata associated with the key or false
  1123  	* Validates a session key, and returns the userdata associated with the key or false
  1124    * @param string $key The session key to validate
  1124  	* @param string $key The session key to validate
  1125    * @return array Keys are 'user_id', 'username', 'email', 'real_name', 'user_level', 'theme', 'style', 'signature', 'reg_time', 'account_active', 'activation_key', and 'auth_level' or bool false if validation failed. The key 'auth_level' is the maximum authorization level that this key provides.
  1125  	* @return array Keys are 'user_id', 'username', 'email', 'real_name', 'user_level', 'theme', 'style', 'signature', 'reg_time', 'account_active', 'activation_key', and 'auth_level' or bool false if validation failed. The key 'auth_level' is the maximum authorization level that this key provides.
  1126    */
  1126  	*/
  1127    
  1127  	
  1128   function validate_session($key)
  1128 	function validate_session($key)
  1129   {
  1129 	{
  1130     global $db, $session, $paths, $template, $plugins; // Common objects
  1130 		global $db, $session, $paths, $template, $plugins; // Common objects
  1131     profiler_log("SessionManager: checking session: " . sha1($key));
  1131 		profiler_log("SessionManager: checking session: " . sha1($key));
  1132     
  1132 		
  1133     if ( strlen($key) > 48 )
  1133 		if ( strlen($key) > 48 )
  1134     {
  1134 		{
  1135       return $this->validate_aes_session($key);
  1135 			return $this->validate_aes_session($key);
  1136     }
  1136 		}
  1137     
  1137 		
  1138     profiler_log("SessionManager: checking session: " . $key);
  1138 		profiler_log("SessionManager: checking session: " . $key);
  1139     
  1139 		
  1140     return $this->validate_session_shared($key, '');
  1140 		return $this->validate_session_shared($key, '');
  1141   }
  1141 	}
  1142   
  1142 	
  1143   /**
  1143 	/**
  1144    * Validates an old-format AES session key. DO NOT USE THIS. Will return false if called outside of an upgrade.
  1144  	* Validates an old-format AES session key. DO NOT USE THIS. Will return false if called outside of an upgrade.
  1145    * @param string Session key
  1145  	* @param string Session key
  1146    * @return array
  1146  	* @return array
  1147    */
  1147  	*/
  1148   
  1148 	
  1149   protected function validate_aes_session($key)
  1149 	protected function validate_aes_session($key)
  1150   {
  1150 	{
  1151     global $db, $session, $paths, $template, $plugins; // Common objects
  1151 		global $db, $session, $paths, $template, $plugins; // Common objects
  1152     
  1152 		
  1153     // No valid use except during upgrades
  1153 		// No valid use except during upgrades
  1154     if ( !preg_match('/^upg-/', enano_version()) && !defined('IN_ENANO_UPGRADE') )
  1154 		if ( !preg_match('/^upg-/', enano_version()) && !defined('IN_ENANO_UPGRADE') )
  1155       return false;
  1155 			return false;
  1156     
  1156 		
  1157     $decrypted_key = $this->pk_decrypt($key);
  1157 		$decrypted_key = $this->pk_decrypt($key);
  1158     if ( !$decrypted_key )
  1158 		if ( !$decrypted_key )
  1159     {
  1159 		{
  1160       // die_semicritical('AES encryption error', '<p>Something went wrong during the AES decryption process.</p><pre>'.print_r($decrypted_key, true).'</pre>');
  1160 			// die_semicritical('AES encryption error', '<p>Something went wrong during the AES decryption process.</p><pre>'.print_r($decrypted_key, true).'</pre>');
  1161       return false;
  1161 			return false;
  1162     }
  1162 		}
  1163     
  1163 		
  1164     $n = preg_match('/^u='.$this->valid_username.';p=([A-Fa-f0-9]+?);s=(.{32})$/', $decrypted_key, $keydata);
  1164 		$n = preg_match('/^u='.$this->valid_username.';p=([A-Fa-f0-9]+?);s=(.{32})$/', $decrypted_key, $keydata);
  1165     if($n < 1)
  1165 		if($n < 1)
  1166     {
  1166 		{
  1167       echo '(debug) $session->validate_session: Key does not match regex<br />Decrypted key: '.$decrypted_key;
  1167 			echo '(debug) $session->validate_session: Key does not match regex<br />Decrypted key: '.$decrypted_key;
  1168       return false;
  1168 			return false;
  1169     }
  1169 		}
  1170     $keyhash = md5($key);
  1170 		$keyhash = md5($key);
  1171     $salt = $db->escape($keydata[3]);
  1171 		$salt = $db->escape($keydata[3]);
  1172     
  1172 		
  1173     return $this->validate_session_shared($keyhash, $salt, true);
  1173 		return $this->validate_session_shared($keyhash, $salt, true);
  1174   }
  1174 	}
  1175   
  1175 	
  1176   /**
  1176 	/**
  1177    * Shared portion of session validation. Do not try to call this.
  1177  	* Shared portion of session validation. Do not try to call this.
  1178    * @return array
  1178  	* @return array
  1179    * @access private
  1179  	* @access private
  1180    */
  1180  	*/
  1181   
  1181 	
  1182   protected function validate_session_shared($key, $salt, $loose_call = false)
  1182 	protected function validate_session_shared($key, $salt, $loose_call = false)
  1183   {
  1183 	{
  1184     global $db, $session, $paths, $template, $plugins; // Common objects
  1184 		global $db, $session, $paths, $template, $plugins; // Common objects
  1185     
  1185 		
  1186     // using a normal call to $db->sql_query to avoid failing on errors here
  1186 		// using a normal call to $db->sql_query to avoid failing on errors here
  1187     $columns_select  = "u.user_id AS uid, u.username, u.password, u.password_salt, u.email, u.real_name, u.user_level, u.theme,\n"
  1187 		$columns_select  = "u.user_id AS uid, u.username, u.password, u.password_salt, u.email, u.real_name, u.user_level, u.theme,\n"
  1188                       . "  u.style,u.signature, u.reg_time, u.account_active, u.activation_key, u.user_lang, u.user_title, k.salt, k.source_ip,\n"
  1188 											. "  u.style,u.signature, u.reg_time, u.account_active, u.activation_key, u.user_lang, u.user_title, k.salt, k.source_ip,\n"
  1189                       . "  k.time, k.auth_level, k.key_type, COUNT(p.message_id) AS num_pms, u.user_timezone, u.user_dst, x.*";
  1189 											. "  k.time, k.auth_level, k.key_type, COUNT(p.message_id) AS num_pms, u.user_timezone, u.user_dst, x.*";
  1190     
  1190 		
  1191     $columns_groupby = "u.user_id, u.username, u.password, u.password_salt, u.email, u.real_name, u.user_level, u.theme, u.style, u.signature,\n"
  1191 		$columns_groupby = "u.user_id, u.username, u.password, u.password_salt, u.email, u.real_name, u.user_level, u.theme, u.style, u.signature,\n"
  1192                       . "           u.reg_time, u.account_active, u.activation_key, u.user_lang, u.user_timezone, u.user_title, u.user_dst,\n"
  1192 											. "           u.reg_time, u.account_active, u.activation_key, u.user_lang, u.user_timezone, u.user_title, u.user_dst,\n"
  1193                       . "           k.salt, k.source_ip, k.time, k.auth_level, k.key_type, x.user_id, x.user_aim, x.user_yahoo, x.user_msn,\n"
  1193 											. "           k.salt, k.source_ip, k.time, k.auth_level, k.key_type, x.user_id, x.user_aim, x.user_yahoo, x.user_msn,\n"
  1194                       . "           x.user_xmpp, x.user_homepage, x.user_location, x.user_job, x.user_hobbies, x.email_public,\n"
  1194 											. "           x.user_xmpp, x.user_homepage, x.user_location, x.user_job, x.user_hobbies, x.email_public,\n"
  1195                       . "           x.disable_js_fx, x.date_format, x.time_format";
  1195 											. "           x.disable_js_fx, x.date_format, x.time_format";
  1196     
  1196 		
  1197     $joins = "  LEFT JOIN " . table_prefix . "users AS u\n"
  1197 		$joins = "  LEFT JOIN " . table_prefix . "users AS u\n"
  1198             . "    ON ( u.user_id=k.user_id )\n"
  1198 						. "    ON ( u.user_id=k.user_id )\n"
  1199             . "  LEFT JOIN " . table_prefix . "users_extra AS x\n"
  1199 						. "  LEFT JOIN " . table_prefix . "users_extra AS x\n"
  1200             . "    ON ( u.user_id=x.user_id OR x.user_id IS NULL )\n"
  1200 						. "    ON ( u.user_id=x.user_id OR x.user_id IS NULL )\n"
  1201             . "  LEFT JOIN " . table_prefix . "privmsgs AS p\n"
  1201 						. "  LEFT JOIN " . table_prefix . "privmsgs AS p\n"
  1202             . "    ON ( p.message_to=u.username AND p.message_read=0 )\n";
  1202 						. "    ON ( p.message_to=u.username AND p.message_read=0 )\n";
  1203     if ( !$loose_call )
  1203 		if ( !$loose_call )
  1204     {
  1204 		{
  1205       $key_md5 = md5($key);
  1205 			$key_md5 = md5($key);
  1206       $query = $db->sql_query("SELECT $columns_select\n"
  1206 			$query = $db->sql_query("SELECT $columns_select\n"
  1207                             . "FROM " . table_prefix . "session_keys AS k\n"
  1207 														. "FROM " . table_prefix . "session_keys AS k\n"
  1208                             . $joins
  1208 														. $joins
  1209                             . "  WHERE k.session_key='$key_md5'\n"
  1209 														. "  WHERE k.session_key='$key_md5'\n"
  1210                             . "  GROUP BY $columns_groupby;");
  1210 														. "  GROUP BY $columns_groupby;");
  1211     }
  1211 		}
  1212     else
  1212 		else
  1213     {
  1213 		{
  1214       $query = $db->sql_query("SELECT $columns_select\n"
  1214 			$query = $db->sql_query("SELECT $columns_select\n"
  1215                             . "FROM " . table_prefix . "session_keys AS k\n"
  1215 														. "FROM " . table_prefix . "session_keys AS k\n"
  1216                             . $joins
  1216 														. $joins
  1217                             . "  WHERE k.session_key='$key'\n"
  1217 														. "  WHERE k.session_key='$key'\n"
  1218                             . "    AND k.salt='$salt'\n"
  1218 														. "    AND k.salt='$salt'\n"
  1219                             . "  GROUP BY $columns_groupby;");
  1219 														. "  GROUP BY $columns_groupby;");
  1220     }
  1220 		}
  1221     
  1221 		
  1222     if ( !$query && ( defined('IN_ENANO_INSTALL') or defined('IN_ENANO_UPGRADE') ) )
  1222 		if ( !$query && ( defined('IN_ENANO_INSTALL') or defined('IN_ENANO_UPGRADE') ) )
  1223     {
  1223 		{
  1224       $key_md5 = $loose_call ? $key : md5($key);
  1224 			$key_md5 = $loose_call ? $key : md5($key);
  1225       $query = $this->sql('SELECT u.user_id AS uid,u.username,u.password,\'\' AS password_salt,u.email,u.real_name,u.user_level,u.theme,u.style,u.signature,u.reg_time,u.account_active,u.activation_key,k.source_ip,k.time,k.auth_level,COUNT(p.message_id) AS num_pms, 1440 AS user_timezone, \'0;0;0;0;60\' AS user_dst, ' . SK_SHORT . ' AS key_type, k.salt FROM '.table_prefix.'session_keys AS k
  1225 			$query = $this->sql('SELECT u.user_id AS uid,u.username,u.password,\'\' AS password_salt,u.email,u.real_name,u.user_level,u.theme,u.style,u.signature,u.reg_time,u.account_active,u.activation_key,k.source_ip,k.time,k.auth_level,COUNT(p.message_id) AS num_pms, 1440 AS user_timezone, \'0;0;0;0;60\' AS user_dst, ' . SK_SHORT . ' AS key_type, k.salt FROM '.table_prefix.'session_keys AS k
  1226                              LEFT JOIN '.table_prefix.'users AS u
  1226  														LEFT JOIN '.table_prefix.'users AS u
  1227                                ON ( u.user_id=k.user_id )
  1227  															ON ( u.user_id=k.user_id )
  1228                              LEFT JOIN '.table_prefix.'privmsgs AS p
  1228  														LEFT JOIN '.table_prefix.'privmsgs AS p
  1229                                ON ( p.message_to=u.username AND p.message_read=0 )
  1229  															ON ( p.message_to=u.username AND p.message_read=0 )
  1230                              WHERE k.session_key=\''.$key_md5.'\'
  1230  														WHERE k.session_key=\''.$key_md5.'\'
  1231                              GROUP BY u.user_id,u.username,u.password,u.email,u.real_name,u.user_level,u.theme,u.style,u.signature,u.reg_time,u.account_active,u.activation_key,k.source_ip,k.time,k.auth_level,k.salt;');
  1231  														GROUP BY u.user_id,u.username,u.password,u.email,u.real_name,u.user_level,u.theme,u.style,u.signature,u.reg_time,u.account_active,u.activation_key,k.source_ip,k.time,k.auth_level,k.salt;');
  1232     }
  1232 		}
  1233     else if ( !$query )
  1233 		else if ( !$query )
  1234     {
  1234 		{
  1235       $db->_die();
  1235 			$db->_die();
  1236     }
  1236 		}
  1237     if($db->numrows() < 1)
  1237 		if($db->numrows() < 1)
  1238     {
  1238 		{
  1239       // echo '(debug) $session->validate_session: Key was not found in database: ' . $key_md5 . '<br />';
  1239 			// echo '(debug) $session->validate_session: Key was not found in database: ' . $key_md5 . '<br />';
  1240       return false;
  1240 			return false;
  1241     }
  1241 		}
  1242     $row = $db->fetchrow();
  1242 		$row = $db->fetchrow();
  1243     profiler_log("SessionManager: session check: selected and fetched results");
  1243 		profiler_log("SessionManager: session check: selected and fetched results");
  1244     
  1244 		
  1245     $row['user_id'] =& $row['uid'];
  1245 		$row['user_id'] =& $row['uid'];
  1246     $ip = $_SERVER['REMOTE_ADDR'];
  1246 		$ip = $_SERVER['REMOTE_ADDR'];
  1247     if($row['auth_level'] > $row['user_level'])
  1247 		if($row['auth_level'] > $row['user_level'])
  1248     {
  1248 		{
  1249       // Failed authorization check
  1249 			// Failed authorization check
  1250       // echo '(debug) $session->validate_session: access to this auth level denied<br />';
  1250 			// echo '(debug) $session->validate_session: access to this auth level denied<br />';
  1251       return false;
  1251 			return false;
  1252     }
  1252 		}
  1253     if($ip != $row['source_ip'])
  1253 		if($ip != $row['source_ip'])
  1254     {
  1254 		{
  1255       // Special exception for 1.1.x upgrade - the 1.1.3 upgrade changes the size of the column and this is what validate_session
  1255 			// Special exception for 1.1.x upgrade - the 1.1.3 upgrade changes the size of the column and this is what validate_session
  1256       // expects, but if the column size hasn't changed yet just check the first 10 digits of the IP.
  1256 			// expects, but if the column size hasn't changed yet just check the first 10 digits of the IP.
  1257       $fail = true;
  1257 			$fail = true;
  1258       if ( defined('IN_ENANO_UPGRADE') )
  1258 			if ( defined('IN_ENANO_UPGRADE') )
  1259       {
  1259 			{
  1260         if ( substr($ip, 0, 10) == substr($row['source_ip'], 0, 10) )
  1260 				if ( substr($ip, 0, 10) == substr($row['source_ip'], 0, 10) )
  1261           $fail = false;
  1261 					$fail = false;
  1262       }
  1262 			}
  1263       // Failed IP address check
  1263 			// Failed IP address check
  1264       // echo '(debug) $session->validate_session: IP address mismatch<br />';
  1264 			// echo '(debug) $session->validate_session: IP address mismatch<br />';
  1265       if ( $fail )
  1265 			if ( $fail )
  1266         return false;
  1266 				return false;
  1267     }
  1267 		}
  1268     
  1268 		
  1269     // $loose_call is turned on only from validate_aes_session
  1269 		// $loose_call is turned on only from validate_aes_session
  1270     if ( !$loose_call )
  1270 		if ( !$loose_call )
  1271     {
  1271 		{
  1272       $key_pieces = array($row['password']);
  1272 			$key_pieces = array($row['password']);
  1273       $user_id =& $row['uid'];
  1273 			$user_id =& $row['uid'];
  1274       $sk_mode = 'validate';
  1274 			$sk_mode = 'validate';
  1275       $code = $plugins->setHook('session_key_calc');
  1275 			$code = $plugins->setHook('session_key_calc');
  1276       foreach ( $code as $cmd )
  1276 			foreach ( $code as $cmd )
  1277       {
  1277 			{
  1278         eval($cmd);
  1278 				eval($cmd);
  1279       }
  1279 			}
  1280       $key_pieces = implode("\xFF", $key_pieces);
  1280 			$key_pieces = implode("\xFF", $key_pieces);
  1281       $correct_key = hexdecode(hmac_sha1($key_pieces, $row['salt']));
  1281 			$correct_key = hexdecode(hmac_sha1($key_pieces, $row['salt']));
  1282       $user_key = hexdecode($key);
  1282 			$user_key = hexdecode($key);
  1283       if ( $correct_key !== $user_key || !is_string($user_key) )
  1283 			if ( $correct_key !== $user_key || !is_string($user_key) )
  1284       {
  1284 			{
  1285         return false;
  1285 				return false;
  1286       }
  1286 			}
  1287     }
  1287 		}
  1288     else
  1288 		else
  1289     {
  1289 		{
  1290       // if this is a "loose call", this only works once (during the final upgrade stage). Destroy the contents of session_keys.
  1290 			// if this is a "loose call", this only works once (during the final upgrade stage). Destroy the contents of session_keys.
  1291       if ( $row['auth_level'] == USER_LEVEL_ADMIN && preg_match('/^upg-/', enano_version()) )
  1291 			if ( $row['auth_level'] == USER_LEVEL_ADMIN && preg_match('/^upg-/', enano_version()) )
  1292         $this->sql('DELETE FROM ' . table_prefix . "session_keys;");
  1292 				$this->sql('DELETE FROM ' . table_prefix . "session_keys;");
  1293     }
  1293 		}
  1294     
  1294 		
  1295     // timestamp check
  1295 		// timestamp check
  1296     switch ( $row['key_type'] )
  1296 		switch ( $row['key_type'] )
  1297     {
  1297 		{
  1298       case SK_SHORT:
  1298 			case SK_SHORT:
  1299         $time_now = time();
  1299 				$time_now = time();
  1300         $time_key = $row['time'] + ( 60 * intval(getConfig('session_short', '720')) );
  1300 				$time_key = $row['time'] + ( 60 * intval(getConfig('session_short', '720')) );
  1301         if ( $time_now > $time_key )
  1301 				if ( $time_now > $time_key )
  1302         {
  1302 				{
  1303           // Session timed out
  1303 					// Session timed out
  1304           return false;
  1304 					return false;
  1305         }
  1305 				}
  1306         break;
  1306 				break;
  1307       case SK_LONG:
  1307 			case SK_LONG:
  1308         if ( intval(getConfig('session_remember_time', '0')) === 0 )
  1308 				if ( intval(getConfig('session_remember_time', '0')) === 0 )
  1309         {
  1309 				{
  1310           // sessions last infinitely, timestamp validation is therefore successful
  1310 					// sessions last infinitely, timestamp validation is therefore successful
  1311           break;
  1311 					break;
  1312         }
  1312 				}
  1313         $time_now = time();
  1313 				$time_now = time();
  1314         $time_key = $row['time'] + ( 86400 * intval(getConfig('session_remember_time', '30')) );
  1314 				$time_key = $row['time'] + ( 86400 * intval(getConfig('session_remember_time', '30')) );
  1315         if ( $time_now > $time_key )
  1315 				if ( $time_now > $time_key )
  1316         {
  1316 				{
  1317           // Session timed out
  1317 					// Session timed out
  1318           return false;
  1318 					return false;
  1319         }
  1319 				}
  1320         break;
  1320 				break;
  1321       case SK_ELEV:
  1321 			case SK_ELEV:
  1322         $time_now = time();
  1322 				$time_now = time();
  1323         $time_key = $row['time'] + 900;
  1323 				$time_key = $row['time'] + 900;
  1324         if($time_now > $time_key && $row['auth_level'] > USER_LEVEL_MEMBER)
  1324 				if($time_now > $time_key && $row['auth_level'] > USER_LEVEL_MEMBER)
  1325         {
  1325 				{
  1326           // Session timed out
  1326 					// Session timed out
  1327           // echo '(debug) $session->validate_session: super session timed out<br />';
  1327 					// echo '(debug) $session->validate_session: super session timed out<br />';
  1328           $this->sw_timed_out = true;
  1328 					$this->sw_timed_out = true;
  1329           return false;
  1329 					return false;
  1330         }
  1330 				}
  1331         break;
  1331 				break;
  1332     }
  1332 		}
  1333         
  1333 				
  1334     // If this is an elevated-access or short-term session key, update the time
  1334 		// If this is an elevated-access or short-term session key, update the time
  1335     if( $row['key_type'] == SK_ELEV || $row['key_type'] == SK_SHORT )
  1335 		if( $row['key_type'] == SK_ELEV || $row['key_type'] == SK_SHORT )
  1336     {
  1336 		{
  1337       $this->sql('UPDATE '.table_prefix.'session_keys SET time='.time().' WHERE session_key=\''.md5($key).'\';');
  1337 			$this->sql('UPDATE '.table_prefix.'session_keys SET time='.time().' WHERE session_key=\''.md5($key).'\';');
  1338     }
  1338 		}
  1339     
  1339 		
  1340     $user_extra = array();
  1340 		$user_extra = array();
  1341     foreach ( array('user_aim', 'user_yahoo', 'user_msn', 'user_xmpp', 'user_homepage', 'user_location', 'user_job', 'user_hobbies', 'email_public', 'disable_js_fx') as $column )
  1341 		foreach ( array('user_aim', 'user_yahoo', 'user_msn', 'user_xmpp', 'user_homepage', 'user_location', 'user_job', 'user_hobbies', 'email_public', 'disable_js_fx') as $column )
  1342     {
  1342 		{
  1343       if ( isset($row[$column]) )
  1343 			if ( isset($row[$column]) )
  1344         $user_extra[$column] = $row[$column];
  1344 				$user_extra[$column] = $row[$column];
  1345       else
  1345 			else
  1346         $user_extra[$column] = '';
  1346 				$user_extra[$column] = '';
  1347     }
  1347 		}
  1348     
  1348 		
  1349     if ( isset($row['date_format']) )
  1349 		if ( isset($row['date_format']) )
  1350       $this->date_format = $row['date_format'];
  1350 			$this->date_format = $row['date_format'];
  1351     if ( isset($row['time_format']) )
  1351 		if ( isset($row['time_format']) )
  1352       $this->time_format = $row['time_format'];
  1352 			$this->time_format = $row['time_format'];
  1353     
  1353 		
  1354     $this->user_extra = $user_extra;
  1354 		$this->user_extra = $user_extra;
  1355     // Leave the rest to PHP's automatic garbage collector ;-)
  1355 		// Leave the rest to PHP's automatic garbage collector ;-)
  1356     
  1356 		
  1357     $row['password'] = '';
  1357 		$row['password'] = '';
  1358     $row['user_timezone'] = intval($row['user_timezone']) - 1440;
  1358 		$row['user_timezone'] = intval($row['user_timezone']) - 1440;
  1359     
  1359 		
  1360     profiler_log("SessionManager: finished session check");
  1360 		profiler_log("SessionManager: finished session check");
  1361     
  1361 		
  1362     return $row;
  1362 		return $row;
  1363   }
  1363 	}
  1364   
  1364 	
  1365   /**
  1365 	/**
  1366    * Validates a session key, and returns the userdata associated with the key or false. Optimized for compatibility with the old MD5-based auth system.
  1366  	* Validates a session key, and returns the userdata associated with the key or false. Optimized for compatibility with the old MD5-based auth system.
  1367    * @param string $key The session key to validate
  1367  	* @param string $key The session key to validate
  1368    * @return array Keys are 'user_id', 'username', 'email', 'real_name', 'user_level', 'theme', 'style', 'signature', 'reg_time', 'account_active', 'activation_key', and 'auth_level' or bool false if validation failed. The key 'auth_level' is the maximum authorization level that this key provides.
  1368  	* @return array Keys are 'user_id', 'username', 'email', 'real_name', 'user_level', 'theme', 'style', 'signature', 'reg_time', 'account_active', 'activation_key', and 'auth_level' or bool false if validation failed. The key 'auth_level' is the maximum authorization level that this key provides.
  1369    */
  1369  	*/
  1370    
  1370  	
  1371   function compat_validate_session($key)
  1371 	function compat_validate_session($key)
  1372   {
  1372 	{
  1373     global $db, $session, $paths, $template, $plugins; // Common objects
  1373 		global $db, $session, $paths, $template, $plugins; // Common objects
  1374     $key = $db->escape($key);
  1374 		$key = $db->escape($key);
  1375     
  1375 		
  1376     $query = $this->sql('SELECT u.user_id,u.username,u.password,u.email,u.real_name,u.user_level,k.source_ip,k.salt,k.time,k.auth_level,1440 AS user_timezone FROM '.table_prefix.'session_keys AS k
  1376 		$query = $this->sql('SELECT u.user_id,u.username,u.password,u.email,u.real_name,u.user_level,k.source_ip,k.salt,k.time,k.auth_level,1440 AS user_timezone FROM '.table_prefix.'session_keys AS k
  1377                            LEFT JOIN '.table_prefix.'users AS u
  1377  													LEFT JOIN '.table_prefix.'users AS u
  1378                              ON u.user_id=k.user_id
  1378  														ON u.user_id=k.user_id
  1379                            WHERE k.session_key=\''.$key.'\';');
  1379  													WHERE k.session_key=\''.$key.'\';');
  1380     if($db->numrows() < 1)
  1380 		if($db->numrows() < 1)
  1381     {
  1381 		{
  1382       // echo '(debug) $session->validate_session: Key '.$key.' was not found in database<br />';
  1382 			// echo '(debug) $session->validate_session: Key '.$key.' was not found in database<br />';
  1383       return false;
  1383 			return false;
  1384     }
  1384 		}
  1385     $row = $db->fetchrow();
  1385 		$row = $db->fetchrow();
  1386     $ip = ip2hex($_SERVER['REMOTE_ADDR']);
  1386 		$ip = ip2hex($_SERVER['REMOTE_ADDR']);
  1387     if($row['auth_level'] > $row['user_level'])
  1387 		if($row['auth_level'] > $row['user_level'])
  1388     {
  1388 		{
  1389       // Failed authorization check
  1389 			// Failed authorization check
  1390       // echo '(debug) $session->validate_session: user not authorized for this access level';
  1390 			// echo '(debug) $session->validate_session: user not authorized for this access level';
  1391       return false;
  1391 			return false;
  1392     }
  1392 		}
  1393     if($ip != $row['source_ip'])
  1393 		if($ip != $row['source_ip'])
  1394     {
  1394 		{
  1395       // Failed IP address check
  1395 			// Failed IP address check
  1396       // echo '(debug) $session->validate_session: IP address mismatch; IP in table: '.$row['source_ip'].'; reported IP: '.$ip.'';
  1396 			// echo '(debug) $session->validate_session: IP address mismatch; IP in table: '.$row['source_ip'].'; reported IP: '.$ip.'';
  1397       return false;
  1397 			return false;
  1398     }
  1398 		}
  1399     
  1399 		
  1400     // Do the password validation
  1400 		// Do the password validation
  1401     $real_key = md5($row['password'] . $row['salt']);
  1401 		$real_key = md5($row['password'] . $row['salt']);
  1402     
  1402 		
  1403     //die('<pre>'.print_r($keydata, true).'</pre>');
  1403 		//die('<pre>'.print_r($keydata, true).'</pre>');
  1404     if($real_key != $key)
  1404 		if($real_key != $key)
  1405     {
  1405 		{
  1406       // Failed password check
  1406 			// Failed password check
  1407       // echo '(debug) $session->validate_session: supplied password is wrong<br />Real key: '.$real_key.'<br />User key: '.$key;
  1407 			// echo '(debug) $session->validate_session: supplied password is wrong<br />Real key: '.$real_key.'<br />User key: '.$key;
  1408       return false;
  1408 			return false;
  1409     }
  1409 		}
  1410     
  1410 		
  1411     $time_now = time();
  1411 		$time_now = time();
  1412     $time_key = $row['time'] + 900;
  1412 		$time_key = $row['time'] + 900;
  1413     if($time_now > $time_key && $row['auth_level'] >= 1)
  1413 		if($time_now > $time_key && $row['auth_level'] >= 1)
  1414     {
  1414 		{
  1415       $this->sw_timed_out = true;
  1415 			$this->sw_timed_out = true;
  1416       // Session timed out
  1416 			// Session timed out
  1417       // echo '(debug) $session->validate_session: super session timed out<br />';
  1417 			// echo '(debug) $session->validate_session: super session timed out<br />';
  1418       return false;
  1418 			return false;
  1419     }
  1419 		}
  1420     
  1420 		
  1421     $row['user_timezone'] = intval($row['user_timezone']) - 1440;
  1421 		$row['user_timezone'] = intval($row['user_timezone']) - 1440;
  1422     
  1422 		
  1423     return $row;
  1423 		return $row;
  1424   }
  1424 	}
  1425    
  1425  	
  1426   /**
  1426 	/**
  1427    * Demotes us to one less than the specified auth level. AKA destroys elevated authentication and/or logs out the user, depending on $level
  1427  	* Demotes us to one less than the specified auth level. AKA destroys elevated authentication and/or logs out the user, depending on $level
  1428    * @param int $level How low we should go - USER_LEVEL_MEMBER means demote to USER_LEVEL_GUEST, and anything more powerful than USER_LEVEL_MEMBER means demote to USER_LEVEL_MEMBER
  1428  	* @param int $level How low we should go - USER_LEVEL_MEMBER means demote to USER_LEVEL_GUEST, and anything more powerful than USER_LEVEL_MEMBER means demote to USER_LEVEL_MEMBER
  1429    * @return string 'success' if successful, or error on failure
  1429  	* @return string 'success' if successful, or error on failure
  1430    */
  1430  	*/
  1431    
  1431  	
  1432   function logout($level = USER_LEVEL_MEMBER)
  1432 	function logout($level = USER_LEVEL_MEMBER)
  1433   {
  1433 	{
  1434     global $db, $session, $paths, $template, $plugins; // Common objects
  1434 		global $db, $session, $paths, $template, $plugins; // Common objects
  1435     $ou = $this->username;
  1435 		$ou = $this->username;
  1436     $oid = $this->user_id;
  1436 		$oid = $this->user_id;
  1437     if($level > USER_LEVEL_MEMBER)
  1437 		if($level > USER_LEVEL_MEMBER)
  1438     {
  1438 		{
  1439       $aes = AESCrypt::singleton(AES_BITS, AES_BLOCKSIZE);
  1439 			$aes = AESCrypt::singleton(AES_BITS, AES_BLOCKSIZE);
  1440       if(!$this->user_logged_in || $this->auth_level < ( USER_LEVEL_MEMBER + 1))
  1440 			if(!$this->user_logged_in || $this->auth_level < ( USER_LEVEL_MEMBER + 1))
  1441       {
  1441 			{
  1442         return 'success';
  1442 				return 'success';
  1443       }
  1443 			}
  1444       // Destroy elevated privileges
  1444 			// Destroy elevated privileges
  1445       $keyhash = md5($this->sid_super);
  1445 			$keyhash = md5($this->sid_super);
  1446       $this->sql('DELETE FROM '.table_prefix.'session_keys WHERE session_key=\''.$keyhash.'\' AND user_id=\'' . $this->user_id . '\';');
  1446 			$this->sql('DELETE FROM '.table_prefix.'session_keys WHERE session_key=\''.$keyhash.'\' AND user_id=\'' . $this->user_id . '\';');
  1447       $this->sid_super = false;
  1447 			$this->sid_super = false;
  1448       $this->auth_level = USER_LEVEL_MEMBER;
  1448 			$this->auth_level = USER_LEVEL_MEMBER;
  1449     }
  1449 		}
  1450     else
  1450 		else
  1451     {
  1451 		{
  1452       if($this->user_logged_in)
  1452 			if($this->user_logged_in)
  1453       {
  1453 			{
  1454         $aes = AESCrypt::singleton(AES_BITS, AES_BLOCKSIZE);
  1454 				$aes = AESCrypt::singleton(AES_BITS, AES_BLOCKSIZE);
  1455         // Completely destroy our session
  1455 				// Completely destroy our session
  1456         if($this->auth_level > USER_LEVEL_MEMBER)
  1456 				if($this->auth_level > USER_LEVEL_MEMBER)
  1457         {
  1457 				{
  1458           $this->logout(USER_LEVEL_ADMIN);
  1458 					$this->logout(USER_LEVEL_ADMIN);
  1459         }
  1459 				}
  1460         $this->sql('DELETE FROM '.table_prefix.'session_keys WHERE session_key=\''.md5($this->sid).'\';');
  1460 				$this->sql('DELETE FROM '.table_prefix.'session_keys WHERE session_key=\''.md5($this->sid).'\';');
  1461         setcookie( 'sid', '', time()-(3600*24), scriptPath.'/' );
  1461 				setcookie( 'sid', '', time()-(3600*24), scriptPath.'/' );
  1462       }
  1462 			}
  1463     }
  1463 		}
  1464     $code = $plugins->setHook('logout_success'); // , Array('level'=>$level,'old_username'=>$ou,'old_user_id'=>$oid));
  1464 		$code = $plugins->setHook('logout_success'); // , Array('level'=>$level,'old_username'=>$ou,'old_user_id'=>$oid));
  1465     foreach ( $code as $cmd )
  1465 		foreach ( $code as $cmd )
  1466     {
  1466 		{
  1467       eval($cmd);
  1467 			eval($cmd);
  1468     }
  1468 		}
  1469     return 'success';
  1469 		return 'success';
  1470   }
  1470 	}
  1471   
  1471 	
  1472   # Miscellaneous stuff
  1472 	# Miscellaneous stuff
  1473   
  1473 	
  1474   /**
  1474 	/**
  1475    * Alerts the user that their account is inactive, and tells them appropriate steps to remedy the situation. Halts execution.
  1475  	* Alerts the user that their account is inactive, and tells them appropriate steps to remedy the situation. Halts execution.
  1476    * @param array Return from validate_session()
  1476  	* @param array Return from validate_session()
  1477    */
  1477  	*/
  1478   
  1478 	
  1479   function show_inactive_error($userdata)
  1479 	function show_inactive_error($userdata)
  1480   {
  1480 	{
  1481     global $db, $session, $paths, $template, $plugins; // Common objects
  1481 		global $db, $session, $paths, $template, $plugins; // Common objects
  1482     global $lang;
  1482 		global $lang;
  1483     
  1483 		
  1484     global $title;
  1484 		global $title;
  1485     $paths->init($title);
  1485 		$paths->init($title);
  1486     
  1486 		
  1487     $language = intval(getConfig('default_language'));
  1487 		$language = intval(getConfig('default_language'));
  1488     $lang = new Language($language);
  1488 		$lang = new Language($language);
  1489     @setlocale(LC_ALL, $lang->lang_code);
  1489 		@setlocale(LC_ALL, $lang->lang_code);
  1490     
  1490 		
  1491     $a = getConfig('account_activation');
  1491 		$a = getConfig('account_activation');
  1492     switch($a)
  1492 		switch($a)
  1493     {
  1493 		{
  1494       case 'none':
  1494 			case 'none':
  1495       default:
  1495 			default:
  1496         $solution = $lang->get('user_login_noact_solution_none');
  1496 				$solution = $lang->get('user_login_noact_solution_none');
  1497         break;
  1497 				break;
  1498       case 'user':
  1498 			case 'user':
  1499         $solution = $lang->get('user_login_noact_solution_user');
  1499 				$solution = $lang->get('user_login_noact_solution_user');
  1500         break;
  1500 				break;
  1501       case 'admin':
  1501 			case 'admin':
  1502         $solution = $lang->get('user_login_noact_solution_admin');
  1502 				$solution = $lang->get('user_login_noact_solution_admin');
  1503         break;
  1503 				break;
  1504     }
  1504 		}
  1505     
  1505 		
  1506     // admin activation request opportunity
  1506 		// admin activation request opportunity
  1507     $q = $db->sql_query('SELECT 1 FROM '.table_prefix.'logs WHERE log_type=\'admin\' AND action=\'activ_req\' AND edit_summary=\'' . $db->escape($userdata['username']) . '\';');
  1507 		$q = $db->sql_query('SELECT 1 FROM '.table_prefix.'logs WHERE log_type=\'admin\' AND action=\'activ_req\' AND edit_summary=\'' . $db->escape($userdata['username']) . '\';');
  1508     if ( !$q )
  1508 		if ( !$q )
  1509       $db->_die();
  1509 			$db->_die();
  1510     
  1510 		
  1511     $can_request = ( $db->numrows() < 1 );
  1511 		$can_request = ( $db->numrows() < 1 );
  1512     $db->free_result();
  1512 		$db->free_result();
  1513     
  1513 		
  1514     if ( isset($_POST['logout']) )
  1514 		if ( isset($_POST['logout']) )
  1515     {
  1515 		{
  1516       $this->sid = $_COOKIE['sid'];
  1516 			$this->sid = $_COOKIE['sid'];
  1517       $this->user_logged_in = true;
  1517 			$this->user_logged_in = true;
  1518       $this->user_id =       intval($userdata['user_id']);
  1518 			$this->user_id =       intval($userdata['user_id']);
  1519       $this->username =      $userdata['username'];
  1519 			$this->username =      $userdata['username'];
  1520       $this->auth_level =    USER_LEVEL_MEMBER;
  1520 			$this->auth_level =    USER_LEVEL_MEMBER;
  1521       $this->user_level =    USER_LEVEL_MEMBER;
  1521 			$this->user_level =    USER_LEVEL_MEMBER;
  1522       $this->logout();
  1522 			$this->logout();
  1523       redirect(scriptPath . '/', $lang->get('user_login_noact_msg_logout_success_title'), $lang->get('user_login_noact_msg_logout_success_body'), 5);
  1523 			redirect(scriptPath . '/', $lang->get('user_login_noact_msg_logout_success_title'), $lang->get('user_login_noact_msg_logout_success_body'), 5);
  1524     }
  1524 		}
  1525     
  1525 		
  1526     if ( $can_request && !isset($_POST['activation_request']) )
  1526 		if ( $can_request && !isset($_POST['activation_request']) )
  1527     {
  1527 		{
  1528       $form = '<p>' . $lang->get('user_login_noact_msg_ask_admins') . '</p>
  1528 			$form = '<p>' . $lang->get('user_login_noact_msg_ask_admins') . '</p>
  1529                <form action="' . makeUrlNS('System', 'ActivateStub') . '" method="post">
  1529  							<form action="' . makeUrlNS('System', 'ActivateStub') . '" method="post">
  1530                  <p><input type="submit" name="activation_request" value="' . $lang->get('user_login_noact_btn_request_activation') . '" /> <input type="submit" name="logout" value="' . $lang->get('user_login_noact_btn_log_out') . '" /></p>
  1530  								<p><input type="submit" name="activation_request" value="' . $lang->get('user_login_noact_btn_request_activation') . '" /> <input type="submit" name="logout" value="' . $lang->get('user_login_noact_btn_log_out') . '" /></p>
  1531                </form>';
  1531  							</form>';
  1532     }
  1532 		}
  1533     else
  1533 		else
  1534     {
  1534 		{
  1535       if ( $can_request && isset($_POST['activation_request']) )
  1535 			if ( $can_request && isset($_POST['activation_request']) )
  1536       {
  1536 			{
  1537         $this->admin_activation_request($userdata['username']);
  1537 				$this->admin_activation_request($userdata['username']);
  1538         $form = '<p>' . $lang->get('user_login_noact_msg_admins_just_asked') . '</p>
  1538 				$form = '<p>' . $lang->get('user_login_noact_msg_admins_just_asked') . '</p>
  1539                  <form action="' . makeUrlNS('System', 'ActivateStub') . '" method="post">
  1539  								<form action="' . makeUrlNS('System', 'ActivateStub') . '" method="post">
  1540                    <p><input type="submit" name="logout" value="' . $lang->get('user_login_noact_btn_log_out') . '" /></p>
  1540  									<p><input type="submit" name="logout" value="' . $lang->get('user_login_noact_btn_log_out') . '" /></p>
  1541                  </form>';
  1541  								</form>';
  1542       }
  1542 			}
  1543       else
  1543 			else
  1544       {
  1544 			{
  1545         $form = '<p>' . $lang->get('user_login_noact_msg_admins_asked') . '</p>
  1545 				$form = '<p>' . $lang->get('user_login_noact_msg_admins_asked') . '</p>
  1546                  <form action="' . makeUrlNS('System', 'ActivateStub') . '" method="post">
  1546  								<form action="' . makeUrlNS('System', 'ActivateStub') . '" method="post">
  1547                    <p><input type="submit" name="logout" value="' . $lang->get('user_login_noact_btn_log_out') . '" /></p>
  1547  									<p><input type="submit" name="logout" value="' . $lang->get('user_login_noact_btn_log_out') . '" /></p>
  1548                  </form>';
  1548  								</form>';
  1549       }
  1549 			}
  1550     }
  1550 		}
  1551     
  1551 		
  1552     global $output;
  1552 		global $output;
  1553     $output = new Output_HTML();
  1553 		$output = new Output_HTML();
  1554     $output->set_title($lang->get('user_login_noact_title'));
  1554 		$output->set_title($lang->get('user_login_noact_title'));
  1555     die_friendly($lang->get('user_login_noact_title'), '<p>' . $lang->get('user_login_noact_msg_intro') . ' '.$solution.'</p>' . $form);
  1555 		die_friendly($lang->get('user_login_noact_title'), '<p>' . $lang->get('user_login_noact_msg_intro') . ' '.$solution.'</p>' . $form);
  1556   }
  1556 	}
  1557   
  1557 	
  1558   /**
  1558 	/**
  1559    * Appends the high-privilege session key to the URL if we are authorized to do high-privilege stuff
  1559  	* Appends the high-privilege session key to the URL if we are authorized to do high-privilege stuff
  1560    * @param string $url The URL to add session data to
  1560  	* @param string $url The URL to add session data to
  1561    * @return string
  1561  	* @return string
  1562    */
  1562  	*/
  1563   
  1563 	
  1564   function append_sid($url)
  1564 	function append_sid($url)
  1565   {
  1565 	{
  1566     $sep = ( strstr($url, '?') ) ? '&' : '?';
  1566 		$sep = ( strstr($url, '?') ) ? '&' : '?';
  1567     if ( $this->sid_super )
  1567 		if ( $this->sid_super )
  1568     {
  1568 		{
  1569       $url = $url . $sep . 'auth=' . urlencode($this->sid_super);
  1569 			$url = $url . $sep . 'auth=' . urlencode($this->sid_super);
  1570       // echo($this->sid_super.'<br/>');
  1570 			// echo($this->sid_super.'<br/>');
  1571     }
  1571 		}
  1572     return $url;
  1572 		return $url;
  1573   }
  1573 	}
  1574   
  1574 	
  1575   /**
  1575 	/**
  1576    * Prevent the user from changing their password. Authentication plugins may call this to enforce single sign-on.
  1576  	* Prevent the user from changing their password. Authentication plugins may call this to enforce single sign-on.
  1577    * @param string URL to page where the user may change their password
  1577  	* @param string URL to page where the user may change their password
  1578    * @param string Title of the page where the user may change their password
  1578  	* @param string Title of the page where the user may change their password
  1579    * @return null
  1579  	* @return null
  1580    */
  1580  	*/
  1581   
  1581 	
  1582   function disable_password_change($change_url = false, $change_title = false)
  1582 	function disable_password_change($change_url = false, $change_title = false)
  1583   {
  1583 	{
  1584     if ( $this->password_change_disabled )
  1584 		if ( $this->password_change_disabled )
  1585     {
  1585 		{
  1586       // don't allow calling twice. if we have two plugins doing this, somebody is bad at configuring websites.
  1586 			// don't allow calling twice. if we have two plugins doing this, somebody is bad at configuring websites.
  1587       return false;
  1587 			return false;
  1588     }
  1588 		}
  1589     
  1589 		
  1590     if ( is_string($change_url) && is_string($change_title) )
  1590 		if ( is_string($change_url) && is_string($change_title) )
  1591     {
  1591 		{
  1592       $this->password_change_dest = array(
  1592 			$this->password_change_dest = array(
  1593           'url' => $change_url,
  1593 					'url' => $change_url,
  1594           'title' => $change_title
  1594 					'title' => $change_title
  1595         );
  1595 				);
  1596     }
  1596 		}
  1597     else
  1597 		else
  1598     {
  1598 		{
  1599       $this->password_change_dest = array(
  1599 			$this->password_change_dest = array(
  1600           'url' => false,
  1600 					'url' => false,
  1601           'title' => false
  1601 					'title' => false
  1602         );
  1602 				);
  1603     }
  1603 		}
  1604     
  1604 		
  1605     $this->password_change_disabled = true;
  1605 		$this->password_change_disabled = true;
  1606   }
  1606 	}
  1607   
  1607 	
  1608   /**
  1608 	/**
  1609    * Grabs the user's password MD5 - NOW DEPRECATED AND DISABLED.
  1609  	* Grabs the user's password MD5 - NOW DEPRECATED AND DISABLED.
  1610    * @return bool false
  1610  	* @return bool false
  1611    */
  1611  	*/
  1612    
  1612  	
  1613   function grab_password_hash()
  1613 	function grab_password_hash()
  1614   {
  1614 	{
  1615     return false;
  1615 		return false;
  1616   }
  1616 	}
  1617   
  1617 	
  1618   /**
  1618 	/**
  1619    * Destroys the user's password MD5 in memory
  1619  	* Destroys the user's password MD5 in memory
  1620    */
  1620  	*/
  1621   
  1621 	
  1622   function disallow_password_grab()
  1622 	function disallow_password_grab()
  1623   {
  1623 	{
  1624     $this->password_hash = false;
  1624 		$this->password_hash = false;
  1625     return false;
  1625 		return false;
  1626   }
  1626 	}
  1627   
  1627 	
  1628   /**
  1628 	/**
  1629    * Generates an AES key and stashes it in the database
  1629  	* Generates an AES key and stashes it in the database
  1630    * @return string Hex-encoded AES key
  1630  	* @return string Hex-encoded AES key
  1631    */
  1631  	*/
  1632    
  1632  	
  1633   function rijndael_genkey()
  1633 	function rijndael_genkey()
  1634   {
  1634 	{
  1635     $aes = AESCrypt::singleton(AES_BITS, AES_BLOCKSIZE);
  1635 		$aes = AESCrypt::singleton(AES_BITS, AES_BLOCKSIZE);
  1636     $key = $aes->gen_readymade_key();
  1636 		$key = $aes->gen_readymade_key();
  1637     $keys = getConfig('login_key_cache');
  1637 		$keys = getConfig('login_key_cache');
  1638     if(is_string($keys))
  1638 		if(is_string($keys))
  1639       $keys .= $key;
  1639 			$keys .= $key;
  1640     else
  1640 		else
  1641       $keys = $key;
  1641 			$keys = $key;
  1642     setConfig('login_key_cache', $keys);
  1642 		setConfig('login_key_cache', $keys);
  1643     return $key;
  1643 		return $key;
  1644   }
  1644 	}
  1645   
  1645 	
  1646   /**
  1646 	/**
  1647    * Generate a totally random 128-bit value for MD5 challenges
  1647  	* Generate a totally random 128-bit value for MD5 challenges
  1648    * @return string
  1648  	* @return string
  1649    */
  1649  	*/
  1650    
  1650  	
  1651   function dss_rand()
  1651 	function dss_rand()
  1652   {
  1652 	{
  1653     $aes = AESCrypt::singleton(AES_BITS, AES_BLOCKSIZE);
  1653 		$aes = AESCrypt::singleton(AES_BITS, AES_BLOCKSIZE);
  1654     $random = $aes->randkey(128);
  1654 		$random = $aes->randkey(128);
  1655     unset($aes);
  1655 		unset($aes);
  1656     return md5(microtime() . $random);
  1656 		return md5(microtime() . $random);
  1657   }
  1657 	}
  1658   
  1658 	
  1659   /**
  1659 	/**
  1660    * Fetch a cached login public key using the MD5sum as an identifier. Each key can only be fetched once before it is destroyed.
  1660  	* Fetch a cached login public key using the MD5sum as an identifier. Each key can only be fetched once before it is destroyed.
  1661    * @param string $md5 The MD5 sum of the key
  1661  	* @param string $md5 The MD5 sum of the key
  1662    * @return string, or bool false on failure
  1662  	* @return string, or bool false on failure
  1663    */
  1663  	*/
  1664    
  1664  	
  1665   function fetch_public_key($md5)
  1665 	function fetch_public_key($md5)
  1666   {
  1666 	{
  1667     $keys = getConfig('login_key_cache');
  1667 		$keys = getConfig('login_key_cache');
  1668     $keys = enano_str_split($keys, AES_BITS / 4);
  1668 		$keys = enano_str_split($keys, AES_BITS / 4);
  1669     
  1669 		
  1670     foreach($keys as $i => $k)
  1670 		foreach($keys as $i => $k)
  1671     {
  1671 		{
  1672       if(md5($k) == $md5)
  1672 			if(md5($k) == $md5)
  1673       {
  1673 			{
  1674         unset($keys[$i]);
  1674 				unset($keys[$i]);
  1675         if(count($keys) > 0)
  1675 				if(count($keys) > 0)
  1676         {
  1676 				{
  1677           if ( strlen(getConfig('login_key_cache') ) > 64000 )
  1677 					if ( strlen(getConfig('login_key_cache') ) > 64000 )
  1678           {
  1678 					{
  1679             // This should only need to be done once every month or so for an average-size site
  1679 						// This should only need to be done once every month or so for an average-size site
  1680             setConfig('login_key_cache', '');
  1680 						setConfig('login_key_cache', '');
  1681           }
  1681 					}
  1682           else
  1682 					else
  1683           {
  1683 					{
  1684             $keys = implode('', array_values($keys));
  1684 						$keys = implode('', array_values($keys));
  1685             setConfig('login_key_cache', $keys);
  1685 						setConfig('login_key_cache', $keys);
  1686           }
  1686 					}
  1687         }
  1687 				}
  1688         else
  1688 				else
  1689         {
  1689 				{
  1690           setConfig('login_key_cache', '');
  1690 					setConfig('login_key_cache', '');
  1691         }
  1691 				}
  1692         return $k;
  1692 				return $k;
  1693       }
  1693 			}
  1694     }
  1694 		}
  1695     // Couldn't find the key...
  1695 		// Couldn't find the key...
  1696     return false;
  1696 		return false;
  1697   }
  1697 	}
  1698   
  1698 	
  1699   /**
  1699 	/**
  1700    * Adds a user to a group.
  1700  	* Adds a user to a group.
  1701    * @param int User ID
  1701  	* @param int User ID
  1702    * @param int Group ID
  1702  	* @param int Group ID
  1703    * @param bool Group moderator - defaults to false
  1703  	* @param bool Group moderator - defaults to false
  1704    * @return bool True on success, false on failure
  1704  	* @return bool True on success, false on failure
  1705    */
  1705  	*/
  1706   
  1706 	
  1707   function add_user_to_group($user_id, $group_id, $is_mod = false)
  1707 	function add_user_to_group($user_id, $group_id, $is_mod = false)
  1708   {
  1708 	{
  1709     global $db, $session, $paths, $template, $plugins; // Common objects
  1709 		global $db, $session, $paths, $template, $plugins; // Common objects
  1710     
  1710 		
  1711     // Validation
  1711 		// Validation
  1712     if ( !is_int($user_id) || !is_int($group_id) || !is_bool($is_mod) )
  1712 		if ( !is_int($user_id) || !is_int($group_id) || !is_bool($is_mod) )
  1713       return false;
  1713 			return false;
  1714     if ( $user_id < 1 || $group_id < 1 )
  1714 		if ( $user_id < 1 || $group_id < 1 )
  1715       return false;
  1715 			return false;
  1716     
  1716 		
  1717     $mod_switch = ( $is_mod ) ? '1' : '0';
  1717 		$mod_switch = ( $is_mod ) ? '1' : '0';
  1718     $q = $this->sql('SELECT member_id,is_mod FROM '.table_prefix.'group_members WHERE user_id=' . $user_id . ' AND group_id=' . $group_id . ';');
  1718 		$q = $this->sql('SELECT member_id,is_mod FROM '.table_prefix.'group_members WHERE user_id=' . $user_id . ' AND group_id=' . $group_id . ';');
  1719     if ( !$q )
  1719 		if ( !$q )
  1720       $db->_die();
  1720 			$db->_die();
  1721     if ( $db->numrows() < 1 )
  1721 		if ( $db->numrows() < 1 )
  1722     {
  1722 		{
  1723       // User is not in group
  1723 			// User is not in group
  1724       $this->sql('INSERT INTO '.table_prefix.'group_members(user_id,group_id,is_mod) VALUES(' . $user_id . ', ' . $group_id . ', ' . $mod_switch . ');');
  1724 			$this->sql('INSERT INTO '.table_prefix.'group_members(user_id,group_id,is_mod) VALUES(' . $user_id . ', ' . $group_id . ', ' . $mod_switch . ');');
  1725       return true;
  1725 			return true;
  1726     }
  1726 		}
  1727     else
  1727 		else
  1728     {
  1728 		{
  1729       $row = $db->fetchrow();
  1729 			$row = $db->fetchrow();
  1730       // Update modship status
  1730 			// Update modship status
  1731       if ( strval($row['is_mod']) == $mod_switch )
  1731 			if ( strval($row['is_mod']) == $mod_switch )
  1732       {
  1732 			{
  1733         // Modship unchanged
  1733 				// Modship unchanged
  1734         return true;
  1734 				return true;
  1735       }
  1735 			}
  1736       else
  1736 			else
  1737       {
  1737 			{
  1738         // Modship changed
  1738 				// Modship changed
  1739         $this->sql('UPDATE '.table_prefix.'group_members SET is_mod=' . $mod_switch . ' WHERE member_id=' . $row['member_id'] . ';');
  1739 				$this->sql('UPDATE '.table_prefix.'group_members SET is_mod=' . $mod_switch . ' WHERE member_id=' . $row['member_id'] . ';');
  1740         return true;
  1740 				return true;
  1741       }
  1741 			}
  1742     }
  1742 		}
  1743     return false;
  1743 		return false;
  1744   }
  1744 	}
  1745   
  1745 	
  1746   /**
  1746 	/**
  1747    * Removes a user from a group.
  1747  	* Removes a user from a group.
  1748    * @param int User ID
  1748  	* @param int User ID
  1749    * @param int Group ID
  1749  	* @param int Group ID
  1750    * @return bool True on success, false on failure
  1750  	* @return bool True on success, false on failure
  1751    * @todo put a little more error checking in...
  1751  	* @todo put a little more error checking in...
  1752    */
  1752  	*/
  1753   
  1753 	
  1754   function remove_user_from_group($user_id, $group_id)
  1754 	function remove_user_from_group($user_id, $group_id)
  1755   {
  1755 	{
  1756     if ( !is_int($user_id) || !is_int($group_id) )
  1756 		if ( !is_int($user_id) || !is_int($group_id) )
  1757       return false;
  1757 			return false;
  1758     $this->sql('DELETE FROM '.table_prefix."group_members WHERE user_id=$user_id AND group_id=$group_id;");
  1758 		$this->sql('DELETE FROM '.table_prefix."group_members WHERE user_id=$user_id AND group_id=$group_id;");
  1759     return true;
  1759 		return true;
  1760   }
  1760 	}
  1761   
  1761 	
  1762   /**
  1762 	/**
  1763    * Checks the banlist to ensure that we're an allowed user. Doesn't return anything because it dies if the user is banned.
  1763  	* Checks the banlist to ensure that we're an allowed user. Doesn't return anything because it dies if the user is banned.
  1764    */
  1764  	*/
  1765    
  1765  	
  1766   function check_banlist()
  1766 	function check_banlist()
  1767   {
  1767 	{
  1768     global $db, $session, $paths, $template, $plugins; // Common objects
  1768 		global $db, $session, $paths, $template, $plugins; // Common objects
  1769     global $lang;
  1769 		global $lang;
  1770     
  1770 		
  1771     $col_reason = ( $this->compat ) ? '\'No reason available (session manager is in compatibility mode)\' AS reason' : 'reason';
  1771 		$col_reason = ( $this->compat ) ? '\'No reason available (session manager is in compatibility mode)\' AS reason' : 'reason';
  1772     $remote_addr = ( strstr($_SERVER['REMOTE_ADDR'], ':') ) ? expand_ipv6_address($_SERVER['REMOTE_ADDR']) : $_SERVER['REMOTE_ADDR'];
  1772 		$remote_addr = ( strstr($_SERVER['REMOTE_ADDR'], ':') ) ? expand_ipv6_address($_SERVER['REMOTE_ADDR']) : $_SERVER['REMOTE_ADDR'];
  1773     
  1773 		
  1774     $banned = false;
  1774 		$banned = false;
  1775     if ( $this->user_logged_in )
  1775 		if ( $this->user_logged_in )
  1776     {
  1776 		{
  1777       // check by IP, email, and username
  1777 			// check by IP, email, and username
  1778       if ( ENANO_DBLAYER == 'MYSQL' )
  1778 			if ( ENANO_DBLAYER == 'MYSQL' )
  1779       {
  1779 			{
  1780         $sql = "SELECT $col_reason, ban_value, ban_type, is_regex FROM " . table_prefix . "banlist WHERE \n"
  1780 				$sql = "SELECT $col_reason, ban_value, ban_type, is_regex FROM " . table_prefix . "banlist WHERE \n"
  1781               . "    ( ban_type = " . BAN_IP    . " AND is_regex = 0 ) OR \n"
  1781 							. "    ( ban_type = " . BAN_IP    . " AND is_regex = 0 ) OR \n"
  1782               . "    ( ban_type = " . BAN_IP    . " AND is_regex = 1 AND '{$_SERVER['REMOTE_ADDR']}' REGEXP ban_value ) OR \n"
  1782 							. "    ( ban_type = " . BAN_IP    . " AND is_regex = 1 AND '{$_SERVER['REMOTE_ADDR']}' REGEXP ban_value ) OR \n"
  1783               . "    ( ban_type = " . BAN_USER  . " AND is_regex = 0 AND ban_value = '{$this->username}' ) OR \n"
  1783 							. "    ( ban_type = " . BAN_USER  . " AND is_regex = 0 AND ban_value = '{$this->username}' ) OR \n"
  1784               . "    ( ban_type = " . BAN_USER  . " AND is_regex = 1 AND '{$this->username}' REGEXP ban_value ) OR \n"
  1784 							. "    ( ban_type = " . BAN_USER  . " AND is_regex = 1 AND '{$this->username}' REGEXP ban_value ) OR \n"
  1785               . "    ( ban_type = " . BAN_EMAIL . " AND is_regex = 0 AND ban_value = '{$this->email}' ) OR \n"
  1785 							. "    ( ban_type = " . BAN_EMAIL . " AND is_regex = 0 AND ban_value = '{$this->email}' ) OR \n"
  1786               . "    ( ban_type = " . BAN_EMAIL . " AND is_regex = 1 AND '{$this->email}' REGEXP ban_value ) \n"
  1786 							. "    ( ban_type = " . BAN_EMAIL . " AND is_regex = 1 AND '{$this->email}' REGEXP ban_value ) \n"
  1787               . "  ORDER BY ban_type ASC;";
  1787 							. "  ORDER BY ban_type ASC;";
  1788       }
  1788 			}
  1789       else if ( ENANO_DBLAYER == 'PGSQL' )
  1789 			else if ( ENANO_DBLAYER == 'PGSQL' )
  1790       {
  1790 			{
  1791         $sql = "SELECT $col_reason, ban_value, ban_type, is_regex FROM " . table_prefix . "banlist WHERE \n"
  1791 				$sql = "SELECT $col_reason, ban_value, ban_type, is_regex FROM " . table_prefix . "banlist WHERE \n"
  1792               . "    ( ban_type = " . BAN_IP    . " AND is_regex = 0 ) OR \n"
  1792 							. "    ( ban_type = " . BAN_IP    . " AND is_regex = 0 ) OR \n"
  1793               . "    ( ban_type = " . BAN_IP    . " AND is_regex = 1 AND '{$_SERVER['REMOTE_ADDR']}' ~ ban_value ) OR \n"
  1793 							. "    ( ban_type = " . BAN_IP    . " AND is_regex = 1 AND '{$_SERVER['REMOTE_ADDR']}' ~ ban_value ) OR \n"
  1794               . "    ( ban_type = " . BAN_USER  . " AND is_regex = 0 AND ban_value = '{$this->username}' ) OR \n"
  1794 							. "    ( ban_type = " . BAN_USER  . " AND is_regex = 0 AND ban_value = '{$this->username}' ) OR \n"
  1795               . "    ( ban_type = " . BAN_USER  . " AND is_regex = 1 AND '{$this->username}' ~ ban_value ) OR \n"
  1795 							. "    ( ban_type = " . BAN_USER  . " AND is_regex = 1 AND '{$this->username}' ~ ban_value ) OR \n"
  1796               . "    ( ban_type = " . BAN_EMAIL . " AND is_regex = 0 AND ban_value = '{$this->email}' ) OR \n"
  1796 							. "    ( ban_type = " . BAN_EMAIL . " AND is_regex = 0 AND ban_value = '{$this->email}' ) OR \n"
  1797               . "    ( ban_type = " . BAN_EMAIL . " AND is_regex = 1 AND '{$this->email}' ~ ban_value ) \n"
  1797 							. "    ( ban_type = " . BAN_EMAIL . " AND is_regex = 1 AND '{$this->email}' ~ ban_value ) \n"
  1798               . "  ORDER BY ban_type ASC;";
  1798 							. "  ORDER BY ban_type ASC;";
  1799       }
  1799 			}
  1800       $q = $this->sql($sql);
  1800 			$q = $this->sql($sql);
  1801       if ( $db->numrows() > 0 )
  1801 			if ( $db->numrows() > 0 )
  1802       {
  1802 			{
  1803         while ( list($reason_temp, $ban_value, $ban_type, $is_regex) = $db->fetchrow_num() )
  1803 				while ( list($reason_temp, $ban_value, $ban_type, $is_regex) = $db->fetchrow_num() )
  1804         {
  1804 				{
  1805           if ( $ban_type == BAN_IP && $is_regex != 1 )
  1805 					if ( $ban_type == BAN_IP && $is_regex != 1 )
  1806           {
  1806 					{
  1807             // check range
  1807 						// check range
  1808             $regexp = parse_ip_range_regex($ban_value);
  1808 						$regexp = parse_ip_range_regex($ban_value);
  1809             if ( !$regexp )
  1809 						if ( !$regexp )
  1810             {
  1810 						{
  1811               continue;
  1811 							continue;
  1812             }
  1812 						}
  1813             if ( preg_match("/$regexp/", $remote_addr) )
  1813 						if ( preg_match("/$regexp/", $remote_addr) )
  1814             {
  1814 						{
  1815               $reason = $reason_temp;
  1815 							$reason = $reason_temp;
  1816               $banned = true;
  1816 							$banned = true;
  1817             }
  1817 						}
  1818           }
  1818 					}
  1819           else
  1819 					else
  1820           {
  1820 					{
  1821             // User is banned
  1821 						// User is banned
  1822             $banned = true;
  1822 						$banned = true;
  1823             $reason = $reason_temp;
  1823 						$reason = $reason_temp;
  1824           }
  1824 					}
  1825         }
  1825 				}
  1826       }
  1826 			}
  1827       $db->free_result();
  1827 			$db->free_result();
  1828     }
  1828 		}
  1829     else
  1829 		else
  1830     {
  1830 		{
  1831       // check by IP only
  1831 			// check by IP only
  1832       if ( ENANO_DBLAYER == 'MYSQL' )
  1832 			if ( ENANO_DBLAYER == 'MYSQL' )
  1833       {
  1833 			{
  1834         $sql = "SELECT $col_reason, ban_value, ban_type, is_regex FROM " . table_prefix . "banlist WHERE
  1834 				$sql = "SELECT $col_reason, ban_value, ban_type, is_regex FROM " . table_prefix . "banlist WHERE
  1835                   ( ban_type = " . BAN_IP    . " AND is_regex = 0 ) OR
  1835 									( ban_type = " . BAN_IP    . " AND is_regex = 0 ) OR
  1836                   ( ban_type = " . BAN_IP    . " AND is_regex = 1 AND '{$_SERVER['REMOTE_ADDR']}' REGEXP ban_value )
  1836 									( ban_type = " . BAN_IP    . " AND is_regex = 1 AND '{$_SERVER['REMOTE_ADDR']}' REGEXP ban_value )
  1837                 ORDER BY ban_type ASC;";
  1837 								ORDER BY ban_type ASC;";
  1838       }
  1838 			}
  1839       else if ( ENANO_DBLAYER == 'PGSQL' )
  1839 			else if ( ENANO_DBLAYER == 'PGSQL' )
  1840       {
  1840 			{
  1841         $sql = "SELECT $col_reason, ban_value, ban_type, is_regex FROM " . table_prefix . "banlist WHERE
  1841 				$sql = "SELECT $col_reason, ban_value, ban_type, is_regex FROM " . table_prefix . "banlist WHERE
  1842                   ( ban_type = " . BAN_IP    . " AND is_regex = 0 ) OR
  1842 									( ban_type = " . BAN_IP    . " AND is_regex = 0 ) OR
  1843                   ( ban_type = " . BAN_IP    . " AND is_regex = 1 AND '{$_SERVER['REMOTE_ADDR']}' ~ ban_value )
  1843 									( ban_type = " . BAN_IP    . " AND is_regex = 1 AND '{$_SERVER['REMOTE_ADDR']}' ~ ban_value )
  1844                 ORDER BY ban_type ASC;";
  1844 								ORDER BY ban_type ASC;";
  1845       }
  1845 			}
  1846       $q = $this->sql($sql);
  1846 			$q = $this->sql($sql);
  1847       if ( $db->numrows() > 0 )
  1847 			if ( $db->numrows() > 0 )
  1848       {
  1848 			{
  1849         while ( list($reason_temp, $ban_value, $ban_type, $is_regex) = $db->fetchrow_num() )
  1849 				while ( list($reason_temp, $ban_value, $ban_type, $is_regex) = $db->fetchrow_num() )
  1850         {
  1850 				{
  1851           if ( $ban_type == BAN_IP && $is_regex != 1 )
  1851 					if ( $ban_type == BAN_IP && $is_regex != 1 )
  1852           {
  1852 					{
  1853             // check range
  1853 						// check range
  1854             $regexp = parse_ip_range_regex($ban_value);
  1854 						$regexp = parse_ip_range_regex($ban_value);
  1855             if ( !$regexp )
  1855 						if ( !$regexp )
  1856             {
  1856 						{
  1857               die("bad regexp for $ban_value");
  1857 							die("bad regexp for $ban_value");
  1858               continue;
  1858 							continue;
  1859             }
  1859 						}
  1860             if ( preg_match("/$regexp/", $remote_addr) )
  1860 						if ( preg_match("/$regexp/", $remote_addr) )
  1861             {
  1861 						{
  1862               $reason = $reason_temp;
  1862 							$reason = $reason_temp;
  1863               $banned = true;
  1863 							$banned = true;
  1864             }
  1864 						}
  1865           }
  1865 					}
  1866           else
  1866 					else
  1867           {
  1867 					{
  1868             // User is banned
  1868 						// User is banned
  1869             $reason = $reason_temp;
  1869 						$reason = $reason_temp;
  1870             $banned = true;
  1870 						$banned = true;
  1871           }
  1871 					}
  1872         }
  1872 				}
  1873       }
  1873 			}
  1874       $db->free_result();
  1874 			$db->free_result();
  1875     }
  1875 		}
  1876     if ( $banned && !$this->on_critical_page(true) )
  1876 		if ( $banned && !$this->on_critical_page(true) )
  1877     {
  1877 		{
  1878       // This guy is banned - kill the session, kill the database connection, bail out, and be pretty about it
  1878 			// This guy is banned - kill the session, kill the database connection, bail out, and be pretty about it
  1879       die_semicritical($lang->get('user_ban_msg_title'), '<p>' . $lang->get('user_ban_msg_body') . '</p><div class="error-box"><b>' . $lang->get('user_ban_lbl_reason') . '</b><br />' . $reason . '</div>');
  1879 			die_semicritical($lang->get('user_ban_msg_title'), '<p>' . $lang->get('user_ban_msg_body') . '</p><div class="error-box"><b>' . $lang->get('user_ban_lbl_reason') . '</b><br />' . $reason . '</div>');
  1880       exit;
  1880 			exit;
  1881     }
  1881 		}
  1882   }
  1882 	}
  1883   
  1883 	
  1884   # Registration
  1884 	# Registration
  1885   
  1885 	
  1886   /**
  1886 	/**
  1887    * Registers a user. This does not perform any type of login.
  1887  	* Registers a user. This does not perform any type of login.
  1888    * @param string New user's username
  1888  	* @param string New user's username
  1889    * @param string This should be unencrypted.
  1889  	* @param string This should be unencrypted.
  1890    * @param string E-mail address.
  1890  	* @param string E-mail address.
  1891    * @param string Optional, defaults to ''.
  1891  	* @param string Optional, defaults to ''.
  1892    * @param bool Optional. If true, the account is not activated initially and an admin activation request is sent. The caller is responsible for sending the address info and notice.
  1892  	* @param bool Optional. If true, the account is not activated initially and an admin activation request is sent. The caller is responsible for sending the address info and notice.
  1893    */
  1893  	*/
  1894    
  1894  	
  1895   function create_user($username, $password, $email, $real_name = '', $coppa = false)
  1895 	function create_user($username, $password, $email, $real_name = '', $coppa = false)
  1896   {
  1896 	{
  1897     global $db, $session, $paths, $template, $plugins; // Common objects
  1897 		global $db, $session, $paths, $template, $plugins; // Common objects
  1898     global $lang;
  1898 		global $lang;
  1899     
  1899 		
  1900     // Initialize AES
  1900 		// Initialize AES
  1901     $aes = AESCrypt::singleton(AES_BITS, AES_BLOCKSIZE);
  1901 		$aes = AESCrypt::singleton(AES_BITS, AES_BLOCKSIZE);
  1902     
  1902 		
  1903     // Since we're recording IP addresses, make sure the user's IP is safe.
  1903 		// Since we're recording IP addresses, make sure the user's IP is safe.
  1904     $ip =& $_SERVER['REMOTE_ADDR'];
  1904 		$ip =& $_SERVER['REMOTE_ADDR'];
  1905     if ( !is_valid_ip($ip) )
  1905 		if ( !is_valid_ip($ip) )
  1906       return 'Invalid IP';
  1906 			return 'Invalid IP';
  1907     
  1907 		
  1908     if ( !preg_match('#^'.$this->valid_username.'$#', $username) )
  1908 		if ( !preg_match('#^'.$this->valid_username.'$#', $username) )
  1909       return $lang->get('user_reg_err_username_banned_chars');
  1909 			return $lang->get('user_reg_err_username_banned_chars');
  1910     
  1910 		
  1911     $username = str_replace('_', ' ', $username);
  1911 		$username = str_replace('_', ' ', $username);
  1912     $user_orig = $username;
  1912 		$user_orig = $username;
  1913     $username = $this->prepare_text($username);
  1913 		$username = $this->prepare_text($username);
  1914     $email = $this->prepare_text($email);
  1914 		$email = $this->prepare_text($email);
  1915     $real_name = $this->prepare_text($real_name);
  1915 		$real_name = $this->prepare_text($real_name);
  1916     
  1916 		
  1917     $nameclause = ( $real_name != '' ) ? ' OR real_name=\''.$real_name.'\'' : '';
  1917 		$nameclause = ( $real_name != '' ) ? ' OR real_name=\''.$real_name.'\'' : '';
  1918     $q = $this->sql('SELECT * FROM '.table_prefix.'users WHERE ' . ENANO_SQLFUNC_LOWERCASE . '(username)=\''.strtolower($username).'\' OR email=\''.$email.'\''.$nameclause.';');
  1918 		$q = $this->sql('SELECT * FROM '.table_prefix.'users WHERE ' . ENANO_SQLFUNC_LOWERCASE . '(username)=\''.strtolower($username).'\' OR email=\''.$email.'\''.$nameclause.';');
  1919     if($db->numrows() > 0)
  1919 		if($db->numrows() > 0)
  1920     {
  1920 		{
  1921       $row = $db->fetchrow();
  1921 			$row = $db->fetchrow();
  1922       $str = 'user_reg_err_dupe';
  1922 			$str = 'user_reg_err_dupe';
  1923       
  1923 			
  1924       if ( $row['username'] == $username )
  1924 			if ( $row['username'] == $username )
  1925       {
  1925 			{
  1926         $str .= '_username';
  1926 				$str .= '_username';
  1927       }
  1927 			}
  1928       if ( $row['email'] == $email )
  1928 			if ( $row['email'] == $email )
  1929       {
  1929 			{
  1930         $str .= '_email';
  1930 				$str .= '_email';
  1931       }
  1931 			}
  1932       if ( $row['real_name'] == $real_name && $real_name != '' )
  1932 			if ( $row['real_name'] == $real_name && $real_name != '' )
  1933       {
  1933 			{
  1934         $str .= '_realname';
  1934 				$str .= '_realname';
  1935       }
  1935 			}
  1936       
  1936 			
  1937       return $lang->get($str);
  1937 			return $lang->get($str);
  1938     }
  1938 		}
  1939     
  1939 		
  1940     // Is the password strong enough?
  1940 		// Is the password strong enough?
  1941     if ( getConfig('pw_strength_enable') )
  1941 		if ( getConfig('pw_strength_enable') )
  1942     {
  1942 		{
  1943       $min_score = intval( getConfig('pw_strength_minimum') );
  1943 			$min_score = intval( getConfig('pw_strength_minimum') );
  1944       $pass_score = password_score($password);
  1944 			$pass_score = password_score($password);
  1945       if ( $pass_score < $min_score )
  1945 			if ( $pass_score < $min_score )
  1946       {
  1946 			{
  1947         return $lang->get('user_reg_err_password_too_weak');
  1947 				return $lang->get('user_reg_err_password_too_weak');
  1948       }
  1948 			}
  1949     }
  1949 		}
  1950     
  1950 		
  1951     // Require the account to be activated?
  1951 		// Require the account to be activated?
  1952     switch(getConfig('account_activation'))
  1952 		switch(getConfig('account_activation'))
  1953     {
  1953 		{
  1954       case 'none':
  1954 			case 'none':
  1955       default:
  1955 			default:
  1956         $active = '1';
  1956 				$active = '1';
  1957         break;
  1957 				break;
  1958       case 'user':
  1958 			case 'user':
  1959         $active = '0';
  1959 				$active = '0';
  1960         break;
  1960 				break;
  1961       case 'admin':
  1961 			case 'admin':
  1962         $active = '0';
  1962 				$active = '0';
  1963         break;
  1963 				break;
  1964     }
  1964 		}
  1965     if ( $coppa )
  1965 		if ( $coppa )
  1966       $active = '0';
  1966 			$active = '0';
  1967     
  1967 		
  1968     $coppa_col = ( $coppa ) ? '1' : '0';
  1968 		$coppa_col = ( $coppa ) ? '1' : '0';
  1969     
  1969 		
  1970     // Generate a totally random activation key
  1970 		// Generate a totally random activation key
  1971     $actkey = sha1 ( microtime() . mt_rand() );
  1971 		$actkey = sha1 ( microtime() . mt_rand() );
  1972 
  1972 
  1973     // We good, create the user
  1973 		// We good, create the user
  1974     $this->sql('INSERT INTO ' . table_prefix . "users ( username, email, real_name, theme, style, reg_time, account_active, activation_key, user_level, user_coppa,\n"
  1974 		$this->sql('INSERT INTO ' . table_prefix . "users ( username, email, real_name, theme, style, reg_time, account_active, activation_key, user_level, user_coppa,\n"
  1975              . "                                        user_registration_ip, user_lang, user_has_avatar, avatar_type ) VALUES\n"
  1975  						. "                                        user_registration_ip, user_lang, user_has_avatar, avatar_type ) VALUES\n"
  1976              . "  ( '$username', '$email', '$real_name', '$template->default_theme', '$template->default_style', " . time() . ", $active, '$actkey', \n"
  1976  						. "  ( '$username', '$email', '$real_name', '$template->default_theme', '$template->default_style', " . time() . ", $active, '$actkey', \n"
  1977              . "    " . USER_LEVEL_CHPREF . ", $coppa_col, '$ip', $lang->lang_id, 0, 'png' );");
  1977  						. "    " . USER_LEVEL_CHPREF . ", $coppa_col, '$ip', $lang->lang_id, 0, 'png' );");
  1978     
  1978 		
  1979     // Get user ID and create users_extra entry
  1979 		// Get user ID and create users_extra entry
  1980     $q = $this->sql('SELECT user_id FROM '.table_prefix."users WHERE username='$username';");
  1980 		$q = $this->sql('SELECT user_id FROM '.table_prefix."users WHERE username='$username';");
  1981     if ( $db->numrows() > 0 )
  1981 		if ( $db->numrows() > 0 )
  1982     {
  1982 		{
  1983       list($user_id) = $db->fetchrow_num();
  1983 			list($user_id) = $db->fetchrow_num();
  1984       $db->free_result();
  1984 			$db->free_result();
  1985       
  1985 			
  1986       $this->sql('INSERT INTO '.table_prefix.'users_extra(user_id) VALUES(' . $user_id . ');');
  1986 			$this->sql('INSERT INTO '.table_prefix.'users_extra(user_id) VALUES(' . $user_id . ');');
  1987     }
  1987 		}
  1988     
  1988 		
  1989     // Set the password
  1989 		// Set the password
  1990     $this->set_password($user_id, $password);
  1990 		$this->set_password($user_id, $password);
  1991     
  1991 		
  1992     // Config option added, 1.1.5
  1992 		// Config option added, 1.1.5
  1993     if ( getConfig('userpage_grant_acl', '1') == '1' )             
  1993 		if ( getConfig('userpage_grant_acl', '1') == '1' )             
  1994     {
  1994 		{
  1995       // Grant edit and very limited mod access to the userpage
  1995 			// Grant edit and very limited mod access to the userpage
  1996       $acl_data = array(
  1996 			$acl_data = array(
  1997           'read' => AUTH_ALLOW,
  1997 					'read' => AUTH_ALLOW,
  1998           'view_source' => AUTH_ALLOW,
  1998 					'view_source' => AUTH_ALLOW,
  1999           'edit_page' => AUTH_ALLOW,
  1999 					'edit_page' => AUTH_ALLOW,
  2000           'post_comments' => AUTH_ALLOW,
  2000 					'post_comments' => AUTH_ALLOW,
  2001           'edit_comments' => AUTH_ALLOW, // only allows editing own comments
  2001 					'edit_comments' => AUTH_ALLOW, // only allows editing own comments
  2002           'history_view' => AUTH_ALLOW,
  2002 					'history_view' => AUTH_ALLOW,
  2003           'history_rollback' => AUTH_ALLOW,
  2003 					'history_rollback' => AUTH_ALLOW,
  2004           'rename' => AUTH_ALLOW,
  2004 					'rename' => AUTH_ALLOW,
  2005           'delete_page' => AUTH_ALLOW,
  2005 					'delete_page' => AUTH_ALLOW,
  2006           'tag_create' => AUTH_ALLOW,
  2006 					'tag_create' => AUTH_ALLOW,
  2007           'tag_delete_own' => AUTH_ALLOW,
  2007 					'tag_delete_own' => AUTH_ALLOW,
  2008           'tag_delete_other' => AUTH_ALLOW,
  2008 					'tag_delete_other' => AUTH_ALLOW,
  2009           'edit_cat' => AUTH_ALLOW,
  2009 					'edit_cat' => AUTH_ALLOW,
  2010           'create_page' => AUTH_ALLOW
  2010 					'create_page' => AUTH_ALLOW
  2011         );
  2011 				);
  2012       $acl_data = $db->escape($this->perm_to_string($acl_data));
  2012 			$acl_data = $db->escape($this->perm_to_string($acl_data));
  2013       $userpage = $db->escape(sanitize_page_id($user_orig));
  2013 			$userpage = $db->escape(sanitize_page_id($user_orig));
  2014       $cols = "target_type, target_id, page_id, namespace, rules";
  2014 			$cols = "target_type, target_id, page_id, namespace, rules";
  2015       $vals = ACL_TYPE_USER . ", $user_id, '$userpage', 'User', '$acl_data'";
  2015 			$vals = ACL_TYPE_USER . ", $user_id, '$userpage', 'User', '$acl_data'";
  2016       $q = "INSERT INTO ".table_prefix."acl($cols) VALUES($vals);";
  2016 			$q = "INSERT INTO ".table_prefix."acl($cols) VALUES($vals);";
  2017       $this->sql($q);
  2017 			$this->sql($q);
  2018     }
  2018 		}
  2019     
  2019 		
  2020     // Require the account to be activated?
  2020 		// Require the account to be activated?
  2021     if ( $coppa )
  2021 		if ( $coppa )
  2022     {
  2022 		{
  2023       $this->admin_activation_request($user_orig);
  2023 			$this->admin_activation_request($user_orig);
  2024       $this->send_coppa_mail($user_orig, $email);
  2024 			$this->send_coppa_mail($user_orig, $email);
  2025     }
  2025 		}
  2026     else
  2026 		else
  2027     {
  2027 		{
  2028       switch(getConfig('account_activation'))
  2028 			switch(getConfig('account_activation'))
  2029       {
  2029 			{
  2030         case 'none':
  2030 				case 'none':
  2031         default:
  2031 				default:
  2032           break;
  2032 					break;
  2033         case 'user':
  2033 				case 'user':
  2034           $a = $this->send_activation_mail($user_orig);
  2034 					$a = $this->send_activation_mail($user_orig);
  2035           if(!$a)
  2035 					if(!$a)
  2036           {
  2036 					{
  2037             $this->admin_activation_request($user_orig);
  2037 						$this->admin_activation_request($user_orig);
  2038             return $lang->get('user_reg_err_actmail_failed') . ' ' . $a;
  2038 						return $lang->get('user_reg_err_actmail_failed') . ' ' . $a;
  2039           }
  2039 					}
  2040           break;
  2040 					break;
  2041         case 'admin':
  2041 				case 'admin':
  2042           $this->admin_activation_request($user_orig);
  2042 					$this->admin_activation_request($user_orig);
  2043           break;
  2043 					break;
  2044       }
  2044 			}
  2045     }
  2045 		}
  2046     
  2046 		
  2047     // Leave some data behind for the hook
  2047 		// Leave some data behind for the hook
  2048     $code = $plugins->setHook('user_registered');
  2048 		$code = $plugins->setHook('user_registered');
  2049     foreach ( $code as $cmd )
  2049 		foreach ( $code as $cmd )
  2050     {
  2050 		{
  2051       eval($cmd);
  2051 			eval($cmd);
  2052     }
  2052 		}
  2053     
  2053 		
  2054     return 'success';
  2054 		return 'success';
  2055   }
  2055 	}
  2056   
  2056 	
  2057   /**
  2057 	/**
  2058    * Attempts to send an e-mail to the specified user with activation instructions.
  2058  	* Attempts to send an e-mail to the specified user with activation instructions.
  2059    * @param string $u The usernamd of the user requesting activation
  2059  	* @param string $u The usernamd of the user requesting activation
  2060    * @return bool true on success, false on failure
  2060  	* @return bool true on success, false on failure
  2061    */
  2061  	*/
  2062    
  2062  	
  2063   function send_activation_mail($u, $actkey = false)
  2063 	function send_activation_mail($u, $actkey = false)
  2064   {
  2064 	{
  2065     global $db, $session, $paths, $template, $plugins; // Common objects
  2065 		global $db, $session, $paths, $template, $plugins; // Common objects
  2066     global $lang;
  2066 		global $lang;
  2067     $q = $this->sql('SELECT username,activation_key,account_active,email FROM '.table_prefix.'users WHERE username=\''.$db->escape($u).'\';');
  2067 		$q = $this->sql('SELECT username,activation_key,account_active,email FROM '.table_prefix.'users WHERE username=\''.$db->escape($u).'\';');
  2068     $r = $db->fetchrow();
  2068 		$r = $db->fetchrow();
  2069     if ( empty($r['email']) )
  2069 		if ( empty($r['email']) )
  2070       $db->_die('BUG: $session->send_activation_mail(): no e-mail address in row');
  2070 			$db->_die('BUG: $session->send_activation_mail(): no e-mail address in row');
  2071     
  2071 		
  2072     $aklink = makeUrlComplete('Special', 'ActivateAccount/'.str_replace(' ', '_', $u).'/'. ( ( is_string($actkey) ) ? $actkey : $r['activation_key'] ) );
  2072 		$aklink = makeUrlComplete('Special', 'ActivateAccount/'.str_replace(' ', '_', $u).'/'. ( ( is_string($actkey) ) ? $actkey : $r['activation_key'] ) );
  2073     $message = $lang->get('user_reg_activation_email', array(
  2073 		$message = $lang->get('user_reg_activation_email', array(
  2074         'activation_link' => $aklink,
  2074 				'activation_link' => $aklink,
  2075         'username' => $u
  2075 				'username' => $u
  2076       ));
  2076 			));
  2077       
  2077 			
  2078     if ( getConfig('smtp_enabled') == '1' )
  2078 		if ( getConfig('smtp_enabled') == '1' )
  2079     {
  2079 		{
  2080       $result = smtp_send_email($r['email'], $lang->get('user_reg_activation_email_subject'), preg_replace("#(?<!\r)\n#s", "\n", $message), getConfig('contact_email'));
  2080 			$result = smtp_send_email($r['email'], $lang->get('user_reg_activation_email_subject'), preg_replace("#(?<!\r)\n#s", "\n", $message), getConfig('contact_email'));
  2081       if ( $result == 'success' )
  2081 			if ( $result == 'success' )
  2082       {
  2082 			{
  2083         $result = true;
  2083 				$result = true;
  2084       }
  2084 			}
  2085       else
  2085 			else
  2086       {
  2086 			{
  2087         echo $result;
  2087 				echo $result;
  2088         $result = false;
  2088 				$result = false;
  2089       }
  2089 			}
  2090     }
  2090 		}
  2091     else
  2091 		else
  2092     {
  2092 		{
  2093       $result = mail($r['email'], $lang->get('user_reg_activation_email_subject'), preg_replace("#(?<!\r)\n#s", "\n", $message), 'From: '.getConfig('contact_email'));
  2093 			$result = mail($r['email'], $lang->get('user_reg_activation_email_subject'), preg_replace("#(?<!\r)\n#s", "\n", $message), 'From: '.getConfig('contact_email'));
  2094     }
  2094 		}
  2095     return $result;
  2095 		return $result;
  2096   }
  2096 	}
  2097   
  2097 	
  2098   /**
  2098 	/**
  2099    * Attempts to send an e-mail to the specified user's e-mail address on file intended for the parents
  2099  	* Attempts to send an e-mail to the specified user's e-mail address on file intended for the parents
  2100    * @param string $u The usernamd of the user requesting activation
  2100  	* @param string $u The usernamd of the user requesting activation
  2101    * @return bool true on success, false on failure
  2101  	* @return bool true on success, false on failure
  2102    */
  2102  	*/
  2103    
  2103  	
  2104   function send_coppa_mail($u, $actkey = false)
  2104 	function send_coppa_mail($u, $actkey = false)
  2105   {
  2105 	{
  2106     global $db, $session, $paths, $template, $plugins; // Common objects
  2106 		global $db, $session, $paths, $template, $plugins; // Common objects
  2107     global $lang;
  2107 		global $lang;
  2108     
  2108 		
  2109     $q = $this->sql('SELECT username,email FROM '.table_prefix.'users WHERE user_id=2 OR user_level=' . USER_LEVEL_ADMIN . ' ORDER BY user_id ASC;');
  2109 		$q = $this->sql('SELECT username,email FROM '.table_prefix.'users WHERE user_id=2 OR user_level=' . USER_LEVEL_ADMIN . ' ORDER BY user_id ASC;');
  2110     $un = $db->fetchrow();
  2110 		$un = $db->fetchrow();
  2111     $admin_user = $un['username'];
  2111 		$admin_user = $un['username'];
  2112     
  2112 		
  2113     $q = $this->sql('SELECT username,activation_key,account_active,email FROM '.table_prefix.'users WHERE username=\''.$db->escape($u).'\';');
  2113 		$q = $this->sql('SELECT username,activation_key,account_active,email FROM '.table_prefix.'users WHERE username=\''.$db->escape($u).'\';');
  2114     $r = $db->fetchrow();
  2114 		$r = $db->fetchrow();
  2115     if ( empty($r['email']) )
  2115 		if ( empty($r['email']) )
  2116       $db->_die('BUG: $session->send_activation_mail(): no e-mail address in row');
  2116 			$db->_die('BUG: $session->send_activation_mail(): no e-mail address in row');
  2117       
  2117 			
  2118     if(isset($_SERVER['HTTPS'])) $prot = 'https';
  2118 		if(isset($_SERVER['HTTPS'])) $prot = 'https';
  2119     else $prot = 'http';                                                                           
  2119 		else $prot = 'http';                                                                           
  2120     if($_SERVER['SERVER_PORT'] == '80') $p = '';
  2120 		if($_SERVER['SERVER_PORT'] == '80') $p = '';
  2121     else $p = ':'.$_SERVER['SERVER_PORT'];
  2121 		else $p = ':'.$_SERVER['SERVER_PORT'];
  2122     $sidbak = false;
  2122 		$sidbak = false;
  2123     if($this->sid_super)
  2123 		if($this->sid_super)
  2124       $sidbak = $this->sid_super;
  2124 			$sidbak = $this->sid_super;
  2125     $this->sid_super = false;
  2125 		$this->sid_super = false;
  2126     if($sidbak)
  2126 		if($sidbak)
  2127       $this->sid_super = $sidbak;
  2127 			$this->sid_super = $sidbak;
  2128     unset($sidbak);
  2128 		unset($sidbak);
  2129     $link = "$prot://".$_SERVER['HTTP_HOST'].scriptPath;
  2129 		$link = "$prot://".$_SERVER['HTTP_HOST'].scriptPath;
  2130     
  2130 		
  2131     $message = $lang->get(
  2131 		$message = $lang->get(
  2132         'user_reg_activation_email_coppa',
  2132 				'user_reg_activation_email_coppa',
  2133         array(
  2133 				array(
  2134           'username' => $u,
  2134 					'username' => $u,
  2135           'admin_user' => $admin_user,
  2135 					'admin_user' => $admin_user,
  2136           'site_link' => $link
  2136 					'site_link' => $link
  2137         )
  2137 				)
  2138       );
  2138 			);
  2139     
  2139 		
  2140     
  2140 		
  2141     if(getConfig('smtp_enabled') == '1')
  2141 		if(getConfig('smtp_enabled') == '1')
  2142     {
  2142 		{
  2143       $result = smtp_send_email($r['email'], getConfig('site_name').' website account activation', preg_replace("#(?<!\r)\n#s", "\n", $message), getConfig('contact_email'));
  2143 			$result = smtp_send_email($r['email'], getConfig('site_name').' website account activation', preg_replace("#(?<!\r)\n#s", "\n", $message), getConfig('contact_email'));
  2144       if($result == 'success') 
  2144 			if($result == 'success') 
  2145       {
  2145 			{
  2146         $result = true;
  2146 				$result = true;
  2147       }
  2147 			}
  2148       else
  2148 			else
  2149       {
  2149 			{
  2150         echo $result;
  2150 				echo $result;
  2151         $result = false;
  2151 				$result = false;
  2152       }
  2152 			}
  2153     } 
  2153 		} 
  2154     else
  2154 		else
  2155     {
  2155 		{
  2156       $result = mail($r['email'], getConfig('site_name').' website account activation', preg_replace("#(?<!\r)\n#s", "\n", $message), 'From: '.getConfig('contact_email'));
  2156 			$result = mail($r['email'], getConfig('site_name').' website account activation', preg_replace("#(?<!\r)\n#s", "\n", $message), 'From: '.getConfig('contact_email'));
  2157     }
  2157 		}
  2158     return $result;
  2158 		return $result;
  2159   }
  2159 	}
  2160   
  2160 	
  2161   /**
  2161 	/**
  2162    * Sends an e-mail to a user so they can reset their password.
  2162  	* Sends an e-mail to a user so they can reset their password.
  2163    * @param int $user The user ID, or username if it's a string
  2163  	* @param int $user The user ID, or username if it's a string
  2164    * @return bool true on success, false on failure
  2164  	* @return bool true on success, false on failure
  2165    */
  2165  	*/
  2166    
  2166  	
  2167   function mail_password_reset($user)
  2167 	function mail_password_reset($user)
  2168   {
  2168 	{
  2169     global $db, $session, $paths, $template, $plugins; // Common objects
  2169 		global $db, $session, $paths, $template, $plugins; // Common objects
  2170     global $lang;
  2170 		global $lang;
  2171     
  2171 		
  2172     if(is_int($user))
  2172 		if(is_int($user))
  2173     {
  2173 		{
  2174       $q = $this->sql('SELECT user_id,username,email FROM '.table_prefix.'users WHERE user_id='.$user.';'); // This is SAFE! This is only called if $user is an integer
  2174 			$q = $this->sql('SELECT user_id,username,email FROM '.table_prefix.'users WHERE user_id='.$user.';'); // This is SAFE! This is only called if $user is an integer
  2175     }
  2175 		}
  2176     elseif(is_string($user))
  2176 		elseif(is_string($user))
  2177     {
  2177 		{
  2178       $q = $this->sql('SELECT user_id,username,email FROM '.table_prefix.'users WHERE ' . ENANO_SQLFUNC_LOWERCASE . '(username)=' . ENANO_SQLFUNC_LOWERCASE . '(\''.$db->escape($user).'\');');
  2178 			$q = $this->sql('SELECT user_id,username,email FROM '.table_prefix.'users WHERE ' . ENANO_SQLFUNC_LOWERCASE . '(username)=' . ENANO_SQLFUNC_LOWERCASE . '(\''.$db->escape($user).'\');');
  2179     }
  2179 		}
  2180     else
  2180 		else
  2181     {
  2181 		{
  2182       return false;
  2182 			return false;
  2183     }
  2183 		}
  2184     
  2184 		
  2185     $row = $db->fetchrow();
  2185 		$row = $db->fetchrow();
  2186     $temp_pass = $this->random_pass();
  2186 		$temp_pass = $this->random_pass();
  2187     
  2187 		
  2188     $this->register_temp_password($row['user_id'], $temp_pass);
  2188 		$this->register_temp_password($row['user_id'], $temp_pass);
  2189     
  2189 		
  2190     $site_name = getConfig('site_name');
  2190 		$site_name = getConfig('site_name');
  2191  
  2191  
  2192     $message = $lang->get('userfuncs_passreset_email', array(
  2192 		$message = $lang->get('userfuncs_passreset_email', array(
  2193         'username' => $row['username'],
  2193 				'username' => $row['username'],
  2194         'site_name' => $site_name,
  2194 				'site_name' => $site_name,
  2195         'remote_addr' => $_SERVER['REMOTE_ADDR'],
  2195 				'remote_addr' => $_SERVER['REMOTE_ADDR'],
  2196         'temp_pass' => $temp_pass
  2196 				'temp_pass' => $temp_pass
  2197       ));
  2197 			));
  2198     
  2198 		
  2199     if(getConfig('smtp_enabled') == '1')
  2199 		if(getConfig('smtp_enabled') == '1')
  2200     {
  2200 		{
  2201       $result = smtp_send_email($row['email'], getConfig('site_name').' password reset', preg_replace("#(?<!\r)\n#s", "\n", $message), getConfig('contact_email'));
  2201 			$result = smtp_send_email($row['email'], getConfig('site_name').' password reset', preg_replace("#(?<!\r)\n#s", "\n", $message), getConfig('contact_email'));
  2202       if($result == 'success')
  2202 			if($result == 'success')
  2203       {
  2203 			{
  2204         $result = true;
  2204 				$result = true;
  2205       }
  2205 			}
  2206       else
  2206 			else
  2207       {
  2207 			{
  2208         echo '<p>'.$result.'</p>';
  2208 				echo '<p>'.$result.'</p>';
  2209         $result = false;
  2209 				$result = false;
  2210       }
  2210 			}
  2211     } else {
  2211 		} else {
  2212       $result = mail($row['email'], getConfig('site_name').' password reset', preg_replace("#(?<!\r)\n#s", "\n", $message), 'From: '.getConfig('contact_email'));
  2212 			$result = mail($row['email'], getConfig('site_name').' password reset', preg_replace("#(?<!\r)\n#s", "\n", $message), 'From: '.getConfig('contact_email'));
  2213     }
  2213 		}
  2214     return $result;
  2214 		return $result;
  2215   }
  2215 	}
  2216   
  2216 	
  2217   /**
  2217 	/**
  2218    * Sets the temporary password for the specified user to whatever is specified.
  2218  	* Sets the temporary password for the specified user to whatever is specified.
  2219    * @param int $user_id
  2219  	* @param int $user_id
  2220    * @param string $password
  2220  	* @param string $password
  2221    * @return bool
  2221  	* @return bool
  2222    */
  2222  	*/
  2223    
  2223  	
  2224   function register_temp_password($user_id, $password)
  2224 	function register_temp_password($user_id, $password)
  2225   {
  2225 	{
  2226     global $db;
  2226 		global $db;
  2227     if ( !is_int($user_id) )
  2227 		if ( !is_int($user_id) )
  2228       return false;
  2228 			return false;
  2229     
  2229 		
  2230     $this->sql('SELECT password_salt FROM ' . table_prefix . "users WHERE user_id = $user_id;");
  2230 		$this->sql('SELECT password_salt FROM ' . table_prefix . "users WHERE user_id = $user_id;");
  2231     if ( $db->numrows() < 1 )
  2231 		if ( $db->numrows() < 1 )
  2232       return false;
  2232 			return false;
  2233     
  2233 		
  2234     list($salt) = $db->fetchrow_num();
  2234 		list($salt) = $db->fetchrow_num();
  2235     $db->free_result();
  2235 		$db->free_result();
  2236     
  2236 		
  2237     $temp_pass = hmac_sha1($password, $salt);
  2237 		$temp_pass = hmac_sha1($password, $salt);
  2238     $this->sql('UPDATE '.table_prefix.'users SET temp_password=\'' . $temp_pass . '\',temp_password_time='.time().' WHERE user_id='.intval($user_id).';');
  2238 		$this->sql('UPDATE '.table_prefix.'users SET temp_password=\'' . $temp_pass . '\',temp_password_time='.time().' WHERE user_id='.intval($user_id).';');
  2239   }
  2239 	}
  2240   
  2240 	
  2241   /**
  2241 	/**
  2242    * Sends a request to the admin panel to have the username $u activated.
  2242  	* Sends a request to the admin panel to have the username $u activated.
  2243    * @param string $u The username of the user requesting activation
  2243  	* @param string $u The username of the user requesting activation
  2244    */
  2244  	*/
  2245   
  2245 	
  2246   function admin_activation_request($u)
  2246 	function admin_activation_request($u)
  2247   {
  2247 	{
  2248     global $db;
  2248 		global $db;
  2249     $this->sql('INSERT INTO '.table_prefix.'logs(log_type, action, time_id, date_string, author, edit_summary) VALUES(\'admin\', \'activ_req\', '.time().', \''.enano_date(ED_DATE | ED_TIME).'\', \''.$this->username.'\', \''.$db->escape($u).'\');');
  2249 		$this->sql('INSERT INTO '.table_prefix.'logs(log_type, action, time_id, date_string, author, edit_summary) VALUES(\'admin\', \'activ_req\', '.time().', \''.enano_date(ED_DATE | ED_TIME).'\', \''.$this->username.'\', \''.$db->escape($u).'\');');
  2250   }
  2250 	}
  2251   
  2251 	
  2252   /**
  2252 	/**
  2253    * Activates a user account. If the action fails, a report is sent to the admin.
  2253  	* Activates a user account. If the action fails, a report is sent to the admin.
  2254    * @param string $user The username of the user requesting activation
  2254  	* @param string $user The username of the user requesting activation
  2255    * @param string $key The activation key
  2255  	* @param string $key The activation key
  2256    */
  2256  	*/
  2257   
  2257 	
  2258   function activate_account($user, $key)
  2258 	function activate_account($user, $key)
  2259   {
  2259 	{
  2260     global $db, $session, $paths, $template, $plugins; // Common objects
  2260 		global $db, $session, $paths, $template, $plugins; // Common objects
  2261     $q = $this->sql('SELECT 1 FROM ' . table_prefix . 'users WHERE username = \''.$db->escape($user).'\' AND activation_key = \''.$db->escape($key).'\'');
  2261 		$q = $this->sql('SELECT 1 FROM ' . table_prefix . 'users WHERE username = \''.$db->escape($user).'\' AND activation_key = \''.$db->escape($key).'\'');
  2262     if ( $db->numrows() > 0 )
  2262 		if ( $db->numrows() > 0 )
  2263     {
  2263 		{
  2264       $new_key = md5(AESCrypt::randkey());
  2264 			$new_key = md5(AESCrypt::randkey());
  2265       $this->sql('UPDATE ' . table_prefix . 'users SET account_active = 1, activation_key = \'' . $new_key . '\' WHERE username=\''.$db->escape($user).'\' AND activation_key=\''.$db->escape($key).'\';');
  2265 			$this->sql('UPDATE ' . table_prefix . 'users SET account_active = 1, activation_key = \'' . $new_key . '\' WHERE username=\''.$db->escape($user).'\' AND activation_key=\''.$db->escape($key).'\';');
  2266       $this->sql('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,date_string,author,edit_summary) VALUES(\'security\', \'activ_good\', '.time().', \''.enano_date(ED_DATE | ED_TIME).'\', \''.$db->escape($user).'\', \''.$_SERVER['REMOTE_ADDR'].'\')');
  2266 			$this->sql('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,date_string,author,edit_summary) VALUES(\'security\', \'activ_good\', '.time().', \''.enano_date(ED_DATE | ED_TIME).'\', \''.$db->escape($user).'\', \''.$_SERVER['REMOTE_ADDR'].'\')');
  2267       return true;
  2267 			return true;
  2268     }
  2268 		}
  2269     else
  2269 		else
  2270     {
  2270 		{
  2271       $this->sql('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,date_string,author,edit_summary) VALUES(\'security\', \'activ_bad\', '.time().', \''.enano_date(ED_DATE | ED_TIME).'\', \''.$db->escape($user).'\', \''.$_SERVER['REMOTE_ADDR'].'\')');
  2271 			$this->sql('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,date_string,author,edit_summary) VALUES(\'security\', \'activ_bad\', '.time().', \''.enano_date(ED_DATE | ED_TIME).'\', \''.$db->escape($user).'\', \''.$_SERVER['REMOTE_ADDR'].'\')');
  2272       return false;
  2272 			return false;
  2273     }
  2273 		}
  2274   }
  2274 	}
  2275   
  2275 	
  2276   /**
  2276 	/**
  2277    * For a given user level identifier (USER_LEVEL_*), returns a string describing that user level.
  2277  	* For a given user level identifier (USER_LEVEL_*), returns a string describing that user level.
  2278    * @param int User level
  2278  	* @param int User level
  2279    * @param bool If true, returns a shorter string. Optional.
  2279  	* @param bool If true, returns a shorter string. Optional.
  2280    * @return string
  2280  	* @return string
  2281    */
  2281  	*/
  2282   
  2282 	
  2283   function userlevel_to_string($user_level, $short = false)
  2283 	function userlevel_to_string($user_level, $short = false)
  2284   {
  2284 	{
  2285     global $lang;
  2285 		global $lang;
  2286     
  2286 		
  2287     static $levels = array(
  2287 		static $levels = array(
  2288         'short' => array(
  2288 				'short' => array(
  2289             USER_LEVEL_GUEST => 'Guest',
  2289 						USER_LEVEL_GUEST => 'Guest',
  2290             USER_LEVEL_MEMBER => 'Member',
  2290 						USER_LEVEL_MEMBER => 'Member',
  2291             USER_LEVEL_CHPREF => 'Sensitive preferences changeable',
  2291 						USER_LEVEL_CHPREF => 'Sensitive preferences changeable',
  2292             USER_LEVEL_MOD => 'Moderator',
  2292 						USER_LEVEL_MOD => 'Moderator',
  2293             USER_LEVEL_ADMIN => 'Administrative'
  2293 						USER_LEVEL_ADMIN => 'Administrative'
  2294           ),
  2294 					),
  2295         'long' => array(
  2295 				'long' => array(
  2296             USER_LEVEL_GUEST => 'Low - guest privileges',
  2296 						USER_LEVEL_GUEST => 'Low - guest privileges',
  2297             USER_LEVEL_MEMBER => 'Standard - normal member level',
  2297 						USER_LEVEL_MEMBER => 'Standard - normal member level',
  2298             USER_LEVEL_CHPREF => 'Medium - user can change his/her own e-mail address and password',
  2298 						USER_LEVEL_CHPREF => 'Medium - user can change his/her own e-mail address and password',
  2299             USER_LEVEL_MOD => 'High - moderator privileges',
  2299 						USER_LEVEL_MOD => 'High - moderator privileges',
  2300             USER_LEVEL_ADMIN => 'Highest - administrative privileges'
  2300 						USER_LEVEL_ADMIN => 'Highest - administrative privileges'
  2301           ),
  2301 					),
  2302         'l10n' => false
  2302 				'l10n' => false
  2303       );
  2303 			);
  2304     
  2304 		
  2305     if ( is_object($lang) && !$levels['l10n'] )
  2305 		if ( is_object($lang) && !$levels['l10n'] )
  2306     {
  2306 		{
  2307       $levels = array(
  2307 			$levels = array(
  2308           'short' => array(
  2308 					'short' => array(
  2309               USER_LEVEL_GUEST => $lang->get('user_level_short_guest'),
  2309 							USER_LEVEL_GUEST => $lang->get('user_level_short_guest'),
  2310               USER_LEVEL_MEMBER => $lang->get('user_level_short_member'),
  2310 							USER_LEVEL_MEMBER => $lang->get('user_level_short_member'),
  2311               USER_LEVEL_CHPREF => $lang->get('user_level_short_chpref'),
  2311 							USER_LEVEL_CHPREF => $lang->get('user_level_short_chpref'),
  2312               USER_LEVEL_MOD => $lang->get('user_level_short_mod'),
  2312 							USER_LEVEL_MOD => $lang->get('user_level_short_mod'),
  2313               USER_LEVEL_ADMIN => $lang->get('user_level_short_admin')
  2313 							USER_LEVEL_ADMIN => $lang->get('user_level_short_admin')
  2314             ),
  2314 						),
  2315           'long' => array(
  2315 					'long' => array(
  2316               USER_LEVEL_GUEST => $lang->get('user_level_long_guest'),
  2316 							USER_LEVEL_GUEST => $lang->get('user_level_long_guest'),
  2317               USER_LEVEL_MEMBER => $lang->get('user_level_long_member'),
  2317 							USER_LEVEL_MEMBER => $lang->get('user_level_long_member'),
  2318               USER_LEVEL_CHPREF => $lang->get('user_level_long_chpref'),
  2318 							USER_LEVEL_CHPREF => $lang->get('user_level_long_chpref'),
  2319               USER_LEVEL_MOD => $lang->get('user_level_long_mod'),
  2319 							USER_LEVEL_MOD => $lang->get('user_level_long_mod'),
  2320               USER_LEVEL_ADMIN => $lang->get('user_level_long_admin')
  2320 							USER_LEVEL_ADMIN => $lang->get('user_level_long_admin')
  2321             ),
  2321 						),
  2322           'l10n' => true
  2322 					'l10n' => true
  2323         );
  2323 				);
  2324     }
  2324 		}
  2325     
  2325 		
  2326     $key = ( $short ) ? 'short' : 'long';
  2326 		$key = ( $short ) ? 'short' : 'long';
  2327     if ( isset($levels[$key][$user_level]) )
  2327 		if ( isset($levels[$key][$user_level]) )
  2328     {
  2328 		{
  2329       return $levels[$key][$user_level];
  2329 			return $levels[$key][$user_level];
  2330     }
  2330 		}
  2331     else
  2331 		else
  2332     {
  2332 		{
  2333       if ( $short )
  2333 			if ( $short )
  2334       {
  2334 			{
  2335         return ( is_object($lang) ) ? $lang->get('user_level_short_unknown', array('user_level' => $user_level)) : "Unknown - $user_level";
  2335 				return ( is_object($lang) ) ? $lang->get('user_level_short_unknown', array('user_level' => $user_level)) : "Unknown - $user_level";
  2336       }
  2336 			}
  2337       else
  2337 			else
  2338       {
  2338 			{
  2339         return ( is_object($lang) ) ? $lang->get('user_level_long_unknown', array('user_level' => $user_level)) : "Unknown level ($user_level)";
  2339 				return ( is_object($lang) ) ? $lang->get('user_level_long_unknown', array('user_level' => $user_level)) : "Unknown level ($user_level)";
  2340       }
  2340 			}
  2341     }
  2341 		}
  2342     
  2342 		
  2343     return 'Linux rocks!';
  2343 		return 'Linux rocks!';
  2344     
  2344 		
  2345   }
  2345 	}
  2346   
  2346 	
  2347   /**
  2347 	/**
  2348    * Change a user's e-mail address.
  2348  	* Change a user's e-mail address.
  2349    * @param int $user_id The user ID of the user to update - this cannot be changed
  2349  	* @param int $user_id The user ID of the user to update - this cannot be changed
  2350    * @param string $email The new e-mail address
  2350  	* @param string $email The new e-mail address
  2351    * @return string 'success' if successful, or array of error strings on failure
  2351  	* @return string 'success' if successful, or array of error strings on failure
  2352    */
  2352  	*/
  2353    
  2353  	
  2354   function change_email($user_id, $email)
  2354 	function change_email($user_id, $email)
  2355   {
  2355 	{
  2356     global $db, $session, $paths, $template, $plugins; // Common objects
  2356 		global $db, $session, $paths, $template, $plugins; // Common objects
  2357     
  2357 		
  2358     // Create some arrays
  2358 		// Create some arrays
  2359     
  2359 		
  2360     $errors = array(); // Used to hold error strings
  2360 		$errors = array(); // Used to hold error strings
  2361     
  2361 		
  2362     // Scan the user ID for problems
  2362 		// Scan the user ID for problems
  2363     if ( intval($user_id) < 1 )
  2363 		if ( intval($user_id) < 1 )
  2364       $errors[] = 'SQL injection attempt';
  2364 			$errors[] = 'SQL injection attempt';
  2365     
  2365 		
  2366     $user_id = intval($user_id);
  2366 		$user_id = intval($user_id);
  2367     
  2367 		
  2368     // Verify e-mail address
  2368 		// Verify e-mail address
  2369     if ( !check_email_address($email) )
  2369 		if ( !check_email_address($email) )
  2370       $errors[] = 'user_err_email_not_valid';
  2370 			$errors[] = 'user_err_email_not_valid';
  2371     
  2371 		
  2372     if ( count($errors) > 0 )
  2372 		if ( count($errors) > 0 )
  2373       return $errors;
  2373 			return $errors;
  2374     
  2374 		
  2375     // Make query
  2375 		// Make query
  2376     $email = $db->escape($email);
  2376 		$email = $db->escape($email);
  2377     $q = $db->sql_query('UPDATE ' . table_prefix . "users SET email = '$email' WHERE user_id = $user_id;");
  2377 		$q = $db->sql_query('UPDATE ' . table_prefix . "users SET email = '$email' WHERE user_id = $user_id;");
  2378     
  2378 		
  2379     // We also need to trigger re-activation.
  2379 		// We also need to trigger re-activation.
  2380     switch(getConfig('account_activation', 'none'))
  2380 		switch(getConfig('account_activation', 'none'))
  2381     {
  2381 		{
  2382       case 'user':
  2382 			case 'user':
  2383       case 'admin':
  2383 			case 'admin':
  2384         
  2384 				
  2385         // Note: even with admin activation, activation e-mails are sent when an e-mail is changed.
  2385 				// Note: even with admin activation, activation e-mails are sent when an e-mail is changed.
  2386         
  2386 				
  2387         if ( $session->user_level >= USER_LEVEL_MOD && getConfig('account_activation') == 'admin' )
  2387 				if ( $session->user_level >= USER_LEVEL_MOD && getConfig('account_activation') == 'admin' )
  2388           // Trust admins and moderators
  2388 					// Trust admins and moderators
  2389           break;
  2389 					break;
  2390         
  2390 				
  2391         // retrieve username
  2391 				// retrieve username
  2392         if ( !$username )
  2392 				if ( !$username )
  2393         {
  2393 				{
  2394           $q = $this->sql('SELECT username FROM ' . table_prefix . "users WHERE user_id = $user_id;");
  2394 					$q = $this->sql('SELECT username FROM ' . table_prefix . "users WHERE user_id = $user_id;");
  2395           if($db->numrows() < 1)
  2395 					if($db->numrows() < 1)
  2396           {
  2396 					{
  2397             $errors[] = 'The username could not be selected.';
  2397 						$errors[] = 'The username could not be selected.';
  2398           }
  2398 					}
  2399           else
  2399 					else
  2400           {
  2400 					{
  2401             $row = $db->fetchrow();
  2401 						$row = $db->fetchrow();
  2402             $username = $row['username'];
  2402 						$username = $row['username'];
  2403           }
  2403 					}
  2404         }
  2404 				}
  2405         if ( !$username )
  2405 				if ( !$username )
  2406           return $errors;
  2406 					return $errors;
  2407         
  2407 				
  2408         // Generate an activation key
  2408 				// Generate an activation key
  2409         $actkey = sha1 ( microtime() . mt_rand() );
  2409 				$actkey = sha1 ( microtime() . mt_rand() );
  2410         $a = $this->send_activation_mail($username, $actkey);
  2410 				$a = $this->send_activation_mail($username, $actkey);
  2411         if(!$a)
  2411 				if(!$a)
  2412         {
  2412 				{
  2413           $this->admin_activation_request($username);
  2413 					$this->admin_activation_request($username);
  2414         }
  2414 				}
  2415         // Deactivate the account until e-mail is confirmed
  2415 				// Deactivate the account until e-mail is confirmed
  2416         $q = $db->sql_query('UPDATE ' . table_prefix . "users SET account_active = 0, activation_key = '$actkey' WHERE user_id = $user_id;");
  2416 				$q = $db->sql_query('UPDATE ' . table_prefix . "users SET account_active = 0, activation_key = '$actkey' WHERE user_id = $user_id;");
  2417         break;
  2417 				break;
  2418     }
  2418 		}
  2419     
  2419 		
  2420     // Yay! We're done
  2420 		// Yay! We're done
  2421     return 'success';
  2421 		return 'success';
  2422   }
  2422 	}
  2423   
  2423 	
  2424   /**
  2424 	/**
  2425    * Sets a user's password.
  2425  	* Sets a user's password.
  2426    * @param int|string User ID or username
  2426  	* @param int|string User ID or username
  2427    * @param string New password
  2427  	* @param string New password
  2428    */
  2428  	*/
  2429   
  2429 	
  2430   function set_password($user, $password)
  2430 	function set_password($user, $password)
  2431   {
  2431 	{
  2432     // Generate new password and salt
  2432 		// Generate new password and salt
  2433     $hmac_secret = hexencode(AESCrypt::randkey(20), '', '');
  2433 		$hmac_secret = hexencode(AESCrypt::randkey(20), '', '');
  2434     $password_hmac = hmac_sha1($password, $hmac_secret);
  2434 		$password_hmac = hmac_sha1($password, $hmac_secret);
  2435     
  2435 		
  2436     // Figure out how we want to specify the user
  2436 		// Figure out how we want to specify the user
  2437     $uidcol = is_int($user) ? "user_id = $user" : ENANO_SQLFUNC_LOWERCASE . "(username) = '" . strtolower($this->prepare_text($user)) . "'";
  2437 		$uidcol = is_int($user) ? "user_id = $user" : ENANO_SQLFUNC_LOWERCASE . "(username) = '" . strtolower($this->prepare_text($user)) . "'";
  2438     
  2438 		
  2439     // Perform update
  2439 		// Perform update
  2440     $this->sql('UPDATE ' . table_prefix . "users SET old_encryption = 0, password = '$password_hmac', password_salt = '$hmac_secret' WHERE $uidcol;");
  2440 		$this->sql('UPDATE ' . table_prefix . "users SET old_encryption = 0, password = '$password_hmac', password_salt = '$hmac_secret' WHERE $uidcol;");
  2441     
  2441 		
  2442     return true;
  2442 		return true;
  2443   }
  2443 	}
  2444   
  2444 	
  2445   /**
  2445 	/**
  2446    * Encrypts a string using the site's private key.
  2446  	* Encrypts a string using the site's private key.
  2447    * @param string
  2447  	* @param string
  2448    * @param int Return type - one of ENC_BINARY, ENC_HEX, ENC_BASE64
  2448  	* @param int Return type - one of ENC_BINARY, ENC_HEX, ENC_BASE64
  2449    * @return string
  2449  	* @return string
  2450    */
  2450  	*/
  2451   
  2451 	
  2452   function pk_encrypt($string, $return_type = ENC_HEX)
  2452 	function pk_encrypt($string, $return_type = ENC_HEX)
  2453   {
  2453 	{
  2454     $aes = AESCrypt::singleton(AES_BITS, AES_BLOCKSIZE);
  2454 		$aes = AESCrypt::singleton(AES_BITS, AES_BLOCKSIZE);
  2455     return $aes->encrypt($string, $this->private_key, $return_type);
  2455 		return $aes->encrypt($string, $this->private_key, $return_type);
  2456   }
  2456 	}
  2457   
  2457 	
  2458   /**
  2458 	/**
  2459    * Encrypts a string using the site's private key.
  2459  	* Encrypts a string using the site's private key.
  2460    * @param string
  2460  	* @param string
  2461    * @param int Input type - one of ENC_BINARY, ENC_HEX, ENC_BASE64
  2461  	* @param int Input type - one of ENC_BINARY, ENC_HEX, ENC_BASE64
  2462    * @return string
  2462  	* @return string
  2463    */
  2463  	*/
  2464   
  2464 	
  2465   function pk_decrypt($string, $input_type = ENC_HEX)
  2465 	function pk_decrypt($string, $input_type = ENC_HEX)
  2466   {
  2466 	{
  2467     $aes = AESCrypt::singleton(AES_BITS, AES_BLOCKSIZE);
  2467 		$aes = AESCrypt::singleton(AES_BITS, AES_BLOCKSIZE);
  2468     return $aes->decrypt($string, $this->private_key, $input_type);
  2468 		return $aes->decrypt($string, $this->private_key, $input_type);
  2469   }
  2469 	}
  2470   
  2470 	
  2471   #
  2471 	#
  2472   # USER RANKS
  2472 	# USER RANKS
  2473   #
  2473 	#
  2474   
  2474 	
  2475   /**
  2475 	/**
  2476    * SYNOPSIS OF THE RANK SYSTEM
  2476  	* SYNOPSIS OF THE RANK SYSTEM
  2477    * Enano's rank logic calculates a user's rank based on a precedence scale. The way things are checked is:
  2477  	* Enano's rank logic calculates a user's rank based on a precedence scale. The way things are checked is:
  2478    *   1. Check to see if the user has a specific rank assigned. Use that if possible.
  2478  	*   1. Check to see if the user has a specific rank assigned. Use that if possible.
  2479    *   2. Check the user's primary group to see if it specifies a rank. Use that if possible.
  2479  	*   2. Check the user's primary group to see if it specifies a rank. Use that if possible.
  2480    *   3. Check the other groups a user is in. If one that has a custom rank is encountered, use that rank.
  2480  	*   3. Check the other groups a user is in. If one that has a custom rank is encountered, use that rank.
  2481    *   4. See if the user's user level has a specific rank hard-coded to be associated with it. (Always overrideable as can be seen above)
  2481  	*   4. See if the user's user level has a specific rank hard-coded to be associated with it. (Always overrideable as can be seen above)
  2482    *   5. Use the "member" rank
  2482  	*   5. Use the "member" rank
  2483    */
  2483  	*/
  2484   
  2484 	
  2485   /**
  2485 	/**
  2486    * Generates a textual SQL query for fetching rank data to be sent to calculate_user_rank().
  2486  	* Generates a textual SQL query for fetching rank data to be sent to calculate_user_rank().
  2487    * @param string Text to append, possibly a WHERE clause or so
  2487  	* @param string Text to append, possibly a WHERE clause or so
  2488    * @return string
  2488  	* @return string
  2489    */
  2489  	*/
  2490   
  2490 	
  2491   function generate_rank_sql($append = '')
  2491 	function generate_rank_sql($append = '')
  2492   {
  2492 	{
  2493     // Generate level-to-rank associations
  2493 		// Generate level-to-rank associations
  2494     $assoc = array();
  2494 		$assoc = array();
  2495     foreach ( $this->level_rank_table as $level => $rank )
  2495 		foreach ( $this->level_rank_table as $level => $rank )
  2496     {
  2496 		{
  2497       $assoc[] = "        ( u.user_level = $level AND rl.rank_id = $rank )";
  2497 			$assoc[] = "        ( u.user_level = $level AND rl.rank_id = $rank )";
  2498     }
  2498 		}
  2499     $assoc = implode(" OR\n", $assoc) . "\n";
  2499 		$assoc = implode(" OR\n", $assoc) . "\n";
  2500     
  2500 		
  2501     $gid_col = ( ENANO_DBLAYER == 'PGSQL' ) ?
  2501 		$gid_col = ( ENANO_DBLAYER == 'PGSQL' ) ?
  2502       'array_to_string(' . table_prefix . 'array_accum(m.group_id), \',\') AS group_list' :
  2502 			'array_to_string(' . table_prefix . 'array_accum(m.group_id), \',\') AS group_list' :
  2503       'GROUP_CONCAT(m.group_id) AS group_list';
  2503 			'GROUP_CONCAT(m.group_id) AS group_list';
  2504     
  2504 		
  2505     // The actual query
  2505 		// The actual query
  2506     $sql = "SELECT u.user_id, u.username, u.user_level, u.user_group, u.user_rank, u.user_title, g.group_rank,\n"
  2506 		$sql = "SELECT u.user_id, u.username, u.user_level, u.user_group, u.user_rank, u.user_title, g.group_rank,\n"
  2507          . "       COALESCE(ru.rank_id,    rg.rank_id,    rl.rank_id,    rd.rank_id   ) AS rank_id,\n"
  2507  				. "       COALESCE(ru.rank_id,    rg.rank_id,    rl.rank_id,    rd.rank_id   ) AS rank_id,\n"
  2508          . "       COALESCE(ru.rank_title, rg.rank_title, rl.rank_title, rd.rank_title) AS rank_title,\n"
  2508  				. "       COALESCE(ru.rank_title, rg.rank_title, rl.rank_title, rd.rank_title) AS rank_title,\n"
  2509          . "       COALESCE(ru.rank_style, rg.rank_style, rl.rank_style, rd.rank_style) AS rank_style,\n"
  2509  				. "       COALESCE(ru.rank_style, rg.rank_style, rl.rank_style, rd.rank_style) AS rank_style,\n"
  2510          . "       rg.rank_id AS group_rank_id,\n"
  2510  				. "       rg.rank_id AS group_rank_id,\n"
  2511          . "       ( ru.rank_id IS NULL AND rg.rank_id IS NULL ) AS using_default,\n"
  2511  				. "       ( ru.rank_id IS NULL AND rg.rank_id IS NULL ) AS using_default,\n"
  2512          . "       ( ru.rank_id IS NULL AND rg.rank_id IS NOT NULL ) AS using_group,\n"
  2512  				. "       ( ru.rank_id IS NULL AND rg.rank_id IS NOT NULL ) AS using_group,\n"
  2513          . "       ( ru.rank_id IS NOT NULL ) AS using_user,\n"
  2513  				. "       ( ru.rank_id IS NOT NULL ) AS using_user,\n"
  2514          . "       u.user_rank_userset,\n"
  2514  				. "       u.user_rank_userset,\n"
  2515          . "       $gid_col\n"
  2515  				. "       $gid_col\n"
  2516          . "  FROM " . table_prefix . "users AS u\n"
  2516  				. "  FROM " . table_prefix . "users AS u\n"
  2517          . "  LEFT JOIN " . table_prefix . "groups AS g\n"
  2517  				. "  LEFT JOIN " . table_prefix . "groups AS g\n"
  2518          . "    ON ( g.group_id = u.user_group )\n"
  2518  				. "    ON ( g.group_id = u.user_group )\n"
  2519          . "  LEFT JOIN " . table_prefix . "group_members AS m\n"
  2519  				. "  LEFT JOIN " . table_prefix . "group_members AS m\n"
  2520          . "    ON ( u.user_id = m.user_id )\n"
  2520  				. "    ON ( u.user_id = m.user_id )\n"
  2521          . "  LEFT JOIN " . table_prefix . "ranks AS ru\n"
  2521  				. "  LEFT JOIN " . table_prefix . "ranks AS ru\n"
  2522          . "    ON ( u.user_rank = ru.rank_id )\n"
  2522  				. "    ON ( u.user_rank = ru.rank_id )\n"
  2523          . "  LEFT JOIN " . table_prefix . "ranks AS rg\n"
  2523  				. "  LEFT JOIN " . table_prefix . "ranks AS rg\n"
  2524          . "    ON ( g.group_rank = rg.rank_id )\n"
  2524  				. "    ON ( g.group_rank = rg.rank_id )\n"
  2525          . "  LEFT JOIN " . table_prefix . "ranks AS rl\n"
  2525  				. "  LEFT JOIN " . table_prefix . "ranks AS rl\n"
  2526          . "    ON (\n"
  2526  				. "    ON (\n"
  2527          . $assoc
  2527  				. $assoc
  2528          . "      )\n"
  2528  				. "      )\n"
  2529          . "  LEFT JOIN " . table_prefix . "ranks AS rd\n"
  2529  				. "  LEFT JOIN " . table_prefix . "ranks AS rd\n"
  2530          . "    ON ( rd.rank_id = 1 )$append\n"
  2530  				. "    ON ( rd.rank_id = 1 )$append\n"
  2531          . "  GROUP BY u.user_id, u.username, u.user_level, u.user_group, u.user_rank, u.user_title, u.user_rank_userset, g.group_rank,\n"
  2531  				. "  GROUP BY u.user_id, u.username, u.user_level, u.user_group, u.user_rank, u.user_title, u.user_rank_userset, g.group_rank,\n"
  2532          . "       ru.rank_id, ru.rank_title, ru.rank_style,rg.rank_id, rg.rank_title, rg.rank_style,\n"
  2532  				. "       ru.rank_id, ru.rank_title, ru.rank_style,rg.rank_id, rg.rank_title, rg.rank_style,\n"
  2533          . "       rl.rank_id, rl.rank_title, rl.rank_style,rd.rank_id, rd.rank_title, rd.rank_style;";
  2533  				. "       rl.rank_id, rl.rank_title, rl.rank_style,rd.rank_id, rd.rank_title, rd.rank_style;";
  2534     
  2534 		
  2535     return $sql;
  2535 		return $sql;
  2536   }
  2536 	}
  2537   
  2537 	
  2538   /**
  2538 	/**
  2539    * Returns an associative array with a user's rank information.
  2539  	* Returns an associative array with a user's rank information.
  2540    * The array will contain the following values:
  2540  	* The array will contain the following values:
  2541    *   username: string  The user's username
  2541  	*   username: string  The user's username
  2542    *   user_id:  integer Numerical user ID
  2542  	*   user_id:  integer Numerical user ID
  2543    *   rank_id:  integer Numerical rank ID
  2543  	*   rank_id:  integer Numerical rank ID
  2544    *   rank:     string  The user's current rank
  2544  	*   rank:     string  The user's current rank
  2545    *   title:    string  The user's custom user title if applicable; should be displayed one line below the rank
  2545  	*   title:    string  The user's custom user title if applicable; should be displayed one line below the rank
  2546    *   style:    string  CSS for the username
  2546  	*   style:    string  CSS for the username
  2547    * @param int|string Username *or* user ID
  2547  	* @param int|string Username *or* user ID
  2548    * @return array or false on failure
  2548  	* @return array or false on failure
  2549    */
  2549  	*/
  2550   
  2550 	
  2551   function get_user_rank($id)
  2551 	function get_user_rank($id)
  2552   {
  2552 	{
  2553     global $db, $session, $paths, $template, $plugins; // Common objects
  2553 		global $db, $session, $paths, $template, $plugins; // Common objects
  2554     global $lang;
  2554 		global $lang;
  2555     global $user_ranks;
  2555 		global $user_ranks;
  2556     // cache info in RAM if possible
  2556 		// cache info in RAM if possible
  2557     static $_cache = array();
  2557 		static $_cache = array();
  2558     
  2558 		
  2559     if ( is_int($id) && $id == 0 )
  2559 		if ( is_int($id) && $id == 0 )
  2560       $id = 1;
  2560 			$id = 1;
  2561     
  2561 		
  2562     if ( is_int($id) )
  2562 		if ( is_int($id) )
  2563       $col = "u.user_id = $id";
  2563 			$col = "u.user_id = $id";
  2564     else if ( is_string($id) )
  2564 		else if ( is_string($id) )
  2565       $col = ENANO_SQLFUNC_LOWERCASE . "(username) = " . ENANO_SQLFUNC_LOWERCASE . "('" . $db->escape($id) . "')";
  2565 			$col = ENANO_SQLFUNC_LOWERCASE . "(username) = " . ENANO_SQLFUNC_LOWERCASE . "('" . $db->escape($id) . "')";
  2566     else
  2566 		else
  2567       // invalid parameter
  2567 			// invalid parameter
  2568       return false;
  2568 			return false;
  2569       
  2569 			
  2570     // check the RAM cache
  2570 		// check the RAM cache
  2571     if ( isset($_cache[$id]) )
  2571 		if ( isset($_cache[$id]) )
  2572       return $_cache[$id];
  2572 			return $_cache[$id];
  2573     
  2573 		
  2574     // check the disk cache
  2574 		// check the disk cache
  2575     if ( is_int($id) )
  2575 		if ( is_int($id) )
  2576     {
  2576 		{
  2577       if ( isset($user_ranks[$id]) )
  2577 			if ( isset($user_ranks[$id]) )
  2578       {
  2578 			{
  2579         $_cache[$id] =& $user_ranks[$id];
  2579 				$_cache[$id] =& $user_ranks[$id];
  2580         return $user_ranks[$id];
  2580 				return $user_ranks[$id];
  2581       }
  2581 			}
  2582     }
  2582 		}
  2583     else if ( is_string($id) )
  2583 		else if ( is_string($id) )
  2584     {
  2584 		{
  2585       foreach ( $user_ranks as $key => $valarray )
  2585 			foreach ( $user_ranks as $key => $valarray )
  2586       {
  2586 			{
  2587         if ( is_string($key) && strtolower($key) == strtolower($id) )
  2587 				if ( is_string($key) && strtolower($key) == strtolower($id) )
  2588         {
  2588 				{
  2589           $_cache[$id] = $valarray;
  2589 					$_cache[$id] = $valarray;
  2590           return $valarray;
  2590 					return $valarray;
  2591         }
  2591 				}
  2592       }
  2592 			}
  2593     }
  2593 		}
  2594     
  2594 		
  2595     $sql = $this->generate_rank_sql("\n  WHERE $col");
  2595 		$sql = $this->generate_rank_sql("\n  WHERE $col");
  2596     
  2596 		
  2597     $q = $this->sql($sql);
  2597 		$q = $this->sql($sql);
  2598     // any results?
  2598 		// any results?
  2599     if ( $db->numrows() < 1 )
  2599 		if ( $db->numrows() < 1 )
  2600     {
  2600 		{
  2601       // nuttin'.
  2601 			// nuttin'.
  2602       $db->free_result();
  2602 			$db->free_result();
  2603       $_cache[$id] = false;
  2603 			$_cache[$id] = false;
  2604       return false;
  2604 			return false;
  2605     }
  2605 		}
  2606     
  2606 		
  2607     // Found something.
  2607 		// Found something.
  2608     $row = $db->fetchrow();
  2608 		$row = $db->fetchrow();
  2609     $db->free_result();
  2609 		$db->free_result();
  2610     
  2610 		
  2611     $row = $this->calculate_user_rank($row);
  2611 		$row = $this->calculate_user_rank($row);
  2612     
  2612 		
  2613     $_cache[$id] = $row;
  2613 		$_cache[$id] = $row;
  2614     return $row;
  2614 		return $row;
  2615   }
  2615 	}
  2616   
  2616 	
  2617   /**
  2617 	/**
  2618    * Performs the actual rank calculation based on the contents of a row.
  2618  	* Performs the actual rank calculation based on the contents of a row.
  2619    * @param array
  2619  	* @param array
  2620    * @return array
  2620  	* @return array
  2621    */
  2621  	*/
  2622   
  2622 	
  2623   function calculate_user_rank($row)
  2623 	function calculate_user_rank($row)
  2624   {
  2624 	{
  2625     global $db, $session, $paths, $template, $plugins; // Common objects
  2625 		global $db, $session, $paths, $template, $plugins; // Common objects
  2626     global $lang;
  2626 		global $lang;
  2627     
  2627 		
  2628     static $rank_cache = array();
  2628 		static $rank_cache = array();
  2629     static $group_ranks = array();
  2629 		static $group_ranks = array();
  2630     
  2630 		
  2631     // try to cache that rank info
  2631 		// try to cache that rank info
  2632     if ( !isset($rank_cache[ intval($row['rank_id']) ]) && $row['rank_id'] )
  2632 		if ( !isset($rank_cache[ intval($row['rank_id']) ]) && $row['rank_id'] )
  2633     {
  2633 		{
  2634       $rank_cache[ intval($row['rank_id']) ] = array(
  2634 			$rank_cache[ intval($row['rank_id']) ] = array(
  2635           'rank_id' => intval($row['rank_id']),
  2635 					'rank_id' => intval($row['rank_id']),
  2636           'rank_title' => intval($row['rank_title']),
  2636 					'rank_title' => intval($row['rank_title']),
  2637           'rank_style' => intval($row['rank_style'])
  2637 					'rank_style' => intval($row['rank_style'])
  2638         );
  2638 				);
  2639     }
  2639 		}
  2640     // cache group info (if appropriate)
  2640 		// cache group info (if appropriate)
  2641     if ( $row['using_group'] && !isset($group_ranks[ intval($row['user_group']) ]) )
  2641 		if ( $row['using_group'] && !isset($group_ranks[ intval($row['user_group']) ]) )
  2642     {
  2642 		{
  2643       $group_ranks[ intval($row['user_group']) ] = intval($row['group_rank_id']);
  2643 			$group_ranks[ intval($row['user_group']) ] = intval($row['group_rank_id']);
  2644     }
  2644 		}
  2645     
  2645 		
  2646     // sanitize and process the as-of-yet rank data
  2646 		// sanitize and process the as-of-yet rank data
  2647     $row['rank_id'] = intval($row["rank_id"]);
  2647 		$row['rank_id'] = intval($row["rank_id"]);
  2648     $row['rank_title'] = $row["rank_title"];
  2648 		$row['rank_title'] = $row["rank_title"];
  2649     
  2649 		
  2650     // if we're falling back to some default, then see if we can use one of the user's other groups
  2650 		// if we're falling back to some default, then see if we can use one of the user's other groups
  2651     if ( $row['using_default'] && !empty($row['group_list']) )
  2651 		if ( $row['using_default'] && !empty($row['group_list']) )
  2652     {
  2652 		{
  2653       $group_list = explode(',', $row['group_list']);
  2653 			$group_list = explode(',', $row['group_list']);
  2654       if ( array_walk($group_list, 'intval') )
  2654 			if ( array_walk($group_list, 'intval') )
  2655       {
  2655 			{
  2656         // go through the group list and see if any of them has a rank assigned
  2656 				// go through the group list and see if any of them has a rank assigned
  2657         foreach ( $group_list as $group_id )
  2657 				foreach ( $group_list as $group_id )
  2658         {
  2658 				{
  2659           // cached in RAM? Preferably use that.
  2659 					// cached in RAM? Preferably use that.
  2660           if ( !isset($group_ranks[$group_id]) )
  2660 					if ( !isset($group_ranks[$group_id]) )
  2661           {
  2661 					{
  2662             // Not cached - grab it
  2662 						// Not cached - grab it
  2663             $q = $this->sql('SELECT group_rank FROM ' . table_prefix . "groups WHERE group_id = $group_id;");
  2663 						$q = $this->sql('SELECT group_rank FROM ' . table_prefix . "groups WHERE group_id = $group_id;");
  2664             if ( $db->numrows() < 1 )
  2664 						if ( $db->numrows() < 1 )
  2665             {
  2665 						{
  2666               $db->free_result();
  2666 							$db->free_result();
  2667               continue;
  2667 							continue;
  2668             }
  2668 						}
  2669             list($result) = $db->fetchrow_num();
  2669 						list($result) = $db->fetchrow_num();
  2670             $db->free_result();
  2670 						$db->free_result();
  2671             
  2671 						
  2672             if ( $result === null || $result < 1 )
  2672 						if ( $result === null || $result < 1 )
  2673             {
  2673 						{
  2674               $group_ranks[$group_id] = false;
  2674 							$group_ranks[$group_id] = false;
  2675             }
  2675 						}
  2676             else
  2676 						else
  2677             {
  2677 						{
  2678               $group_ranks[$group_id] = intval($result);
  2678 							$group_ranks[$group_id] = intval($result);
  2679             }
  2679 						}
  2680           }
  2680 					}
  2681           // we've got it now
  2681 					// we've got it now
  2682           if ( $group_ranks[$group_id] )
  2682 					if ( $group_ranks[$group_id] )
  2683           {
  2683 					{
  2684             // found a group with a rank assigned
  2684 						// found a group with a rank assigned
  2685             // so get the rank info
  2685 						// so get the rank info
  2686             $rank_id =& $group_ranks[$group_id];
  2686 						$rank_id =& $group_ranks[$group_id];
  2687             if ( !isset($rank_cache[$rank_id]) )
  2687 						if ( !isset($rank_cache[$rank_id]) )
  2688             {
  2688 						{
  2689               $q = $this->sql('SELECT rank_id, rank_title, rank_style FROM ' . table_prefix . "ranks WHERE rank_id = $rank_id;");
  2689 							$q = $this->sql('SELECT rank_id, rank_title, rank_style FROM ' . table_prefix . "ranks WHERE rank_id = $rank_id;");
  2690               if ( $db->numrows() < 1 )
  2690 							if ( $db->numrows() < 1 )
  2691               {
  2691 							{
  2692                 $db->free_result();
  2692 								$db->free_result();
  2693                 continue;
  2693 								continue;
  2694               }
  2694 							}
  2695               $rank_cache[$rank_id] = $db->fetchrow();
  2695 							$rank_cache[$rank_id] = $db->fetchrow();
  2696               $db->free_result();
  2696 							$db->free_result();
  2697             }
  2697 						}
  2698             // set the final rank parameters
  2698 						// set the final rank parameters
  2699             // die("found member-of-group exception with uid {$row['user_id']} gid $group_id rid $rank_id rt {$rank_cache[$rank_id]['rank_title']}");
  2699 						// die("found member-of-group exception with uid {$row['user_id']} gid $group_id rid $rank_id rt {$rank_cache[$rank_id]['rank_title']}");
  2700             $row['rank_id'] = $rank_id;
  2700 						$row['rank_id'] = $rank_id;
  2701             $row['rank_title'] = $rank_cache[$rank_id]['rank_title'];
  2701 						$row['rank_title'] = $rank_cache[$rank_id]['rank_title'];
  2702             $row['rank_style'] = $rank_cache[$rank_id]['rank_style'];
  2702 						$row['rank_style'] = $rank_cache[$rank_id]['rank_style'];
  2703             break;
  2703 						break;
  2704           }
  2704 					}
  2705         }
  2705 				}
  2706       }
  2706 			}
  2707     }
  2707 		}
  2708     
  2708 		
  2709     if ( $row['user_title'] === NULL )
  2709 		if ( $row['user_title'] === NULL )
  2710       $row['user_title'] = false;
  2710 			$row['user_title'] = false;
  2711     
  2711 		
  2712     $row['user_id'] = intval($row['user_id']);
  2712 		$row['user_id'] = intval($row['user_id']);
  2713     $row['user_level'] = intval($row['user_level']);
  2713 		$row['user_level'] = intval($row['user_level']);
  2714     $row['user_group'] = intval($row['user_group']);
  2714 		$row['user_group'] = intval($row['user_group']);
  2715     
  2715 		
  2716     unset($row['user_rank'], $row['group_rank'], $row['group_list'], $row['using_default'], $row['using_group'], $row['user_level'], $row['user_group'], $row['username']);
  2716 		unset($row['user_rank'], $row['group_rank'], $row['group_list'], $row['using_default'], $row['using_group'], $row['user_level'], $row['user_group'], $row['username']);
  2717     return $row;
  2717 		return $row;
  2718   }
  2718 	}
  2719   
  2719 	
  2720   /**
  2720 	/**
  2721    * Get the list of ranks that a user is allowed to use. Returns false if they cannot change it.
  2721  	* Get the list of ranks that a user is allowed to use. Returns false if they cannot change it.
  2722    * @param string|int User ID or username
  2722  	* @param string|int User ID or username
  2723    * @return array Associative by rank ID
  2723  	* @return array Associative by rank ID
  2724    */
  2724  	*/
  2725   
  2725 	
  2726   function get_user_possible_ranks($id)
  2726 	function get_user_possible_ranks($id)
  2727   {
  2727 	{
  2728     global $db, $session, $paths, $template, $plugins; // Common objects
  2728 		global $db, $session, $paths, $template, $plugins; // Common objects
  2729     
  2729 		
  2730     // cache info in RAM if possible
  2730 		// cache info in RAM if possible
  2731     static $_cache = array();
  2731 		static $_cache = array();
  2732     
  2732 		
  2733     if ( is_int($id) && $id == 0 )
  2733 		if ( is_int($id) && $id == 0 )
  2734       $id = 1;
  2734 			$id = 1;
  2735     
  2735 		
  2736     if ( is_int($id) )
  2736 		if ( is_int($id) )
  2737       $col = "u.user_id = $id";
  2737 			$col = "u.user_id = $id";
  2738     else if ( is_string($id) )
  2738 		else if ( is_string($id) )
  2739       $col = ENANO_SQLFUNC_LOWERCASE . "(username) = " . ENANO_SQLFUNC_LOWERCASE . "('" . $db->escape($id) . "')";
  2739 			$col = ENANO_SQLFUNC_LOWERCASE . "(username) = " . ENANO_SQLFUNC_LOWERCASE . "('" . $db->escape($id) . "')";
  2740     else
  2740 		else
  2741       // invalid parameter
  2741 			// invalid parameter
  2742       return false;
  2742 			return false;
  2743       
  2743 			
  2744     // check the RAM cache
  2744 		// check the RAM cache
  2745     if ( isset($_cache[$id]) )
  2745 		if ( isset($_cache[$id]) )
  2746       return $_cache[$id];
  2746 			return $_cache[$id];
  2747     
  2747 		
  2748     $sql = $this->generate_rank_sql("\n  WHERE $col");
  2748 		$sql = $this->generate_rank_sql("\n  WHERE $col");
  2749     
  2749 		
  2750     $q = $this->sql($sql);
  2750 		$q = $this->sql($sql);
  2751     // any results?
  2751 		// any results?
  2752     if ( $db->numrows() < 1 )
  2752 		if ( $db->numrows() < 1 )
  2753     {
  2753 		{
  2754       // nuttin'.
  2754 			// nuttin'.
  2755       $db->free_result();
  2755 			$db->free_result();
  2756       $_cache[$id] = false;
  2756 			$_cache[$id] = false;
  2757       return false;
  2757 			return false;
  2758     }
  2758 		}
  2759     
  2759 		
  2760     // Found something.
  2760 		// Found something.
  2761     $row = $db->fetchrow();
  2761 		$row = $db->fetchrow();
  2762     $db->free_result();
  2762 		$db->free_result();
  2763     
  2763 		
  2764     if ( $row['using_user'] && !$row['user_rank_userset'] )
  2764 		if ( $row['using_user'] && !$row['user_rank_userset'] )
  2765     {
  2765 		{
  2766       // The user's rank was set manually by an admin.
  2766 			// The user's rank was set manually by an admin.
  2767       $result = array(
  2767 			$result = array(
  2768         array(
  2768 				array(
  2769           'rank_id' => $row['rank_id'],
  2769 					'rank_id' => $row['rank_id'],
  2770           'rank_title' => $row['rank_title'],
  2770 					'rank_title' => $row['rank_title'],
  2771           'rank_style' => $row['rank_style'],
  2771 					'rank_style' => $row['rank_style'],
  2772           'rank_type' => 'user'
  2772 					'rank_type' => 'user'
  2773           )
  2773 					)
  2774         );
  2774 				);
  2775       $_cache[$id] = $result;
  2775 			$_cache[$id] = $result;
  2776       return $result;
  2776 			return $result;
  2777     }
  2777 		}
  2778     
  2778 		
  2779     // copy the result to a more permanent array so we can reference this later
  2779 		// copy the result to a more permanent array so we can reference this later
  2780     $current_settings = $row;
  2780 		$current_settings = $row;
  2781     unset($row);
  2781 		unset($row);
  2782     
  2782 		
  2783     $result = array();
  2783 		$result = array();
  2784     
  2784 		
  2785     // first rank available to us will be the one set by the user's user level
  2785 		// first rank available to us will be the one set by the user's user level
  2786     if ( isset($this->level_rank_table[$current_settings['user_level']]) )
  2786 		if ( isset($this->level_rank_table[$current_settings['user_level']]) )
  2787     {
  2787 		{
  2788       $q = $this->sql('SELECT rank_id, rank_title, rank_style FROM ' . table_prefix . "ranks WHERE rank_id = {$this->level_rank_table[$this->user_level]};");
  2788 			$q = $this->sql('SELECT rank_id, rank_title, rank_style FROM ' . table_prefix . "ranks WHERE rank_id = {$this->level_rank_table[$this->user_level]};");
  2789       if ( $db->numrows() > 0 )
  2789 			if ( $db->numrows() > 0 )
  2790       {
  2790 			{
  2791         $row = $db->fetchrow();
  2791 				$row = $db->fetchrow();
  2792         $row['rank_type'] = 'ulevel';
  2792 				$row['rank_type'] = 'ulevel';
  2793         
  2793 				
  2794         $result[] = $row;
  2794 				$result[] = $row;
  2795       }
  2795 			}
  2796       $db->free_result();
  2796 			$db->free_result();
  2797     }
  2797 		}
  2798     
  2798 		
  2799     // for each group the user is in, figure out if it has a rank associated with it
  2799 		// for each group the user is in, figure out if it has a rank associated with it
  2800     $group_list = explode(',', $current_settings['group_list']);
  2800 		$group_list = explode(',', $current_settings['group_list']);
  2801     foreach ( $group_list as $group_id )
  2801 		foreach ( $group_list as $group_id )
  2802     {
  2802 		{
  2803       $group_id = intval($group_id);
  2803 			$group_id = intval($group_id);
  2804       $q = $this->sql('SELECT r.rank_id, r.rank_title, r.rank_style FROM ' . table_prefix . "groups AS g\n"
  2804 			$q = $this->sql('SELECT r.rank_id, r.rank_title, r.rank_style FROM ' . table_prefix . "groups AS g\n"
  2805                     . "  LEFT JOIN " . table_prefix . "ranks AS r\n"
  2805 										. "  LEFT JOIN " . table_prefix . "ranks AS r\n"
  2806                     . "    ON ( g.group_rank = r.rank_id )\n"
  2806 										. "    ON ( g.group_rank = r.rank_id )\n"
  2807                     . "  WHERE g.group_id = $group_id\n"
  2807 										. "  WHERE g.group_id = $group_id\n"
  2808                     . "    AND r.rank_id IS NOT NULL;");
  2808 										. "    AND r.rank_id IS NOT NULL;");
  2809       if ( $db->numrows() > 0 )
  2809 			if ( $db->numrows() > 0 )
  2810       {
  2810 			{
  2811         $row = $db->fetchrow();
  2811 				$row = $db->fetchrow();
  2812         $row['rank_type'] = 'group';
  2812 				$row['rank_type'] = 'group';
  2813         
  2813 				
  2814         $result[] = $row;
  2814 				$result[] = $row;
  2815       }
  2815 			}
  2816       $db->free_result();
  2816 			$db->free_result();
  2817     }
  2817 		}
  2818     
  2818 		
  2819     $_cache[$id] = $result;
  2819 		$_cache[$id] = $result;
  2820     return $result;
  2820 		return $result;
  2821   }
  2821 	}
  2822   
  2822 	
  2823   #
  2823 	#
  2824   # Access Control Lists
  2824 	# Access Control Lists
  2825   #
  2825 	#
  2826   
  2826 	
  2827   /**
  2827 	/**
  2828    * Creates a new permission field in memory. If the permissions are set in the database, they are used. Otherwise, $default_perm is used.
  2828  	* Creates a new permission field in memory. If the permissions are set in the database, they are used. Otherwise, $default_perm is used.
  2829    * @param string $acl_type An identifier for this field
  2829  	* @param string $acl_type An identifier for this field
  2830    * @param int $default_perm Whether permission should be granted or not if it's not specified in the ACLs.
  2830  	* @param int $default_perm Whether permission should be granted or not if it's not specified in the ACLs.
  2831    * @param string $desc A human readable name for the permission type
  2831  	* @param string $desc A human readable name for the permission type
  2832    * @param array $deps The list of dependencies - this should be an array of ACL types
  2832  	* @param array $deps The list of dependencies - this should be an array of ACL types
  2833    * @param string $scope Which namespaces this field should apply to. This should be either a pipe-delimited list of namespace IDs or just "All".
  2833  	* @param string $scope Which namespaces this field should apply to. This should be either a pipe-delimited list of namespace IDs or just "All".
  2834    */
  2834  	*/
  2835    
  2835  	
  2836   function register_acl_type($acl_type, $default_perm = AUTH_DISALLOW, $desc = false, $deps = Array(), $scope = 'All')
  2836 	function register_acl_type($acl_type, $default_perm = AUTH_DISALLOW, $desc = false, $deps = Array(), $scope = 'All')
  2837   {
  2837 	{
  2838     if(isset($this->acl_types[$acl_type]))
  2838 		if(isset($this->acl_types[$acl_type]))
  2839       return false;
  2839 			return false;
  2840     else
  2840 		else
  2841     {
  2841 		{
  2842       if(!$desc)
  2842 			if(!$desc)
  2843       {
  2843 			{
  2844         $desc = capitalize_first_letter(str_replace('_', ' ', $acl_type));
  2844 				$desc = capitalize_first_letter(str_replace('_', ' ', $acl_type));
  2845       }
  2845 			}
  2846       $this->acl_types[$acl_type] = $default_perm;
  2846 			$this->acl_types[$acl_type] = $default_perm;
  2847       $this->acl_descs[$acl_type] = $desc;
  2847 			$this->acl_descs[$acl_type] = $desc;
  2848       $this->acl_deps[$acl_type] = $deps;
  2848 			$this->acl_deps[$acl_type] = $deps;
  2849       $this->acl_scope[$acl_type] = explode('|', $scope);
  2849 			$this->acl_scope[$acl_type] = explode('|', $scope);
  2850     }
  2850 		}
  2851     return true;
  2851 		return true;
  2852   }
  2852 	}
  2853   
  2853 	
  2854   /**
  2854 	/**
  2855    * Tells us whether permission $type is allowed or not based on the current rules.
  2855  	* Tells us whether permission $type is allowed or not based on the current rules.
  2856    * @param string $type The permission identifier ($acl_type passed to sessionManager::register_acl_type())
  2856  	* @param string $type The permission identifier ($acl_type passed to sessionManager::register_acl_type())
  2857    * @param bool $no_deps If true, disables dependency checking
  2857  	* @param bool $no_deps If true, disables dependency checking
  2858    * @return bool True if allowed, false if denied or if an error occured
  2858  	* @return bool True if allowed, false if denied or if an error occured
  2859    */
  2859  	*/
  2860    
  2860  	
  2861   function get_permissions($type, $no_deps = false)
  2861 	function get_permissions($type, $no_deps = false)
  2862   {
  2862 	{
  2863     global $db, $session, $paths, $template, $plugins; // Common objects
  2863 		global $db, $session, $paths, $template, $plugins; // Common objects
  2864     if ( isset( $this->perms[$type] ) )
  2864 		if ( isset( $this->perms[$type] ) )
  2865     {
  2865 		{
  2866       if ( $this->perms[$type] == AUTH_DENY )
  2866 			if ( $this->perms[$type] == AUTH_DENY )
  2867         $ret = false;
  2867 				$ret = false;
  2868       else if ( $this->perms[$type] == AUTH_WIKIMODE && $paths->wiki_mode )
  2868 			else if ( $this->perms[$type] == AUTH_WIKIMODE && $paths->wiki_mode )
  2869         $ret = true;
  2869 				$ret = true;
  2870       else if ( $this->perms[$type] == AUTH_WIKIMODE && !$paths->wiki_mode )
  2870 			else if ( $this->perms[$type] == AUTH_WIKIMODE && !$paths->wiki_mode )
  2871         $ret = false;
  2871 				$ret = false;
  2872       else if ( $this->perms[$type] == AUTH_ALLOW )
  2872 			else if ( $this->perms[$type] == AUTH_ALLOW )
  2873         $ret = true;
  2873 				$ret = true;
  2874       else if ( $this->perms[$type] == AUTH_DISALLOW )
  2874 			else if ( $this->perms[$type] == AUTH_DISALLOW )
  2875         $ret = false;
  2875 				$ret = false;
  2876     }
  2876 		}
  2877     else if(isset($this->acl_types[$type]))
  2877 		else if(isset($this->acl_types[$type]))
  2878     {
  2878 		{
  2879       if ( $this->acl_types[$type] == AUTH_DENY )
  2879 			if ( $this->acl_types[$type] == AUTH_DENY )
  2880         $ret = false;
  2880 				$ret = false;
  2881       else if ( $this->acl_types[$type] == AUTH_WIKIMODE && $paths->wiki_mode )
  2881 			else if ( $this->acl_types[$type] == AUTH_WIKIMODE && $paths->wiki_mode )
  2882         $ret = true;
  2882 				$ret = true;
  2883       else if ( $this->acl_types[$type] == AUTH_WIKIMODE && !$paths->wiki_mode )
  2883 			else if ( $this->acl_types[$type] == AUTH_WIKIMODE && !$paths->wiki_mode )
  2884         $ret = false;
  2884 				$ret = false;
  2885       else if ( $this->acl_types[$type] == AUTH_ALLOW )
  2885 			else if ( $this->acl_types[$type] == AUTH_ALLOW )
  2886         $ret = true;
  2886 				$ret = true;
  2887       else if ( $this->acl_types[$type] == AUTH_DISALLOW )
  2887 			else if ( $this->acl_types[$type] == AUTH_DISALLOW )
  2888         $ret = false;
  2888 				$ret = false;
  2889     }
  2889 		}
  2890     else
  2890 		else
  2891     {
  2891 		{
  2892       // ACL type is undefined
  2892 			// ACL type is undefined
  2893       trigger_error('Unknown access type "' . $type . '"', E_USER_WARNING);
  2893 			trigger_error('Unknown access type "' . $type . '"', E_USER_WARNING);
  2894       return false; // Be on the safe side and deny access
  2894 			return false; // Be on the safe side and deny access
  2895     }
  2895 		}
  2896     if ( !$no_deps )
  2896 		if ( !$no_deps )
  2897     {
  2897 		{
  2898       if ( !$this->acl_check_deps($type) )
  2898 			if ( !$this->acl_check_deps($type) )
  2899         return false;
  2899 				return false;
  2900     }
  2900 		}
  2901     return $ret;
  2901 		return $ret;
  2902   }
  2902 	}
  2903   
  2903 	
  2904   /**
  2904 	/**
  2905    * Fetch the permissions that apply to the current user for the page specified. The object you get will have the get_permissions method
  2905  	* Fetch the permissions that apply to the current user for the page specified. The object you get will have the get_permissions method
  2906    * and several other abilities.
  2906  	* and several other abilities.
  2907    * @param string $page_id
  2907  	* @param string $page_id
  2908    * @param string $namespace
  2908  	* @param string $namespace
  2909    * @return object
  2909  	* @return object
  2910    */
  2910  	*/
  2911    
  2911  	
  2912   function fetch_page_acl($page_id, $namespace)
  2912 	function fetch_page_acl($page_id, $namespace)
  2913   {
  2913 	{
  2914     global $db, $session, $paths, $template, $plugins; // Common objects
  2914 		global $db, $session, $paths, $template, $plugins; // Common objects
  2915     
  2915 		
  2916     if ( count ( $this->acl_base_cache ) < 1 )
  2916 		if ( count ( $this->acl_base_cache ) < 1 )
  2917     {
  2917 		{
  2918       // Permissions table not yet initialized
  2918 			// Permissions table not yet initialized
  2919       return false;
  2919 			return false;
  2920     }
  2920 		}
  2921     
  2921 		
  2922     // cache of permission objects (to save RAM and SQL queries)
  2922 		// cache of permission objects (to save RAM and SQL queries)
  2923     static $objcache = array();
  2923 		static $objcache = array();
  2924     
  2924 		
  2925     if ( count($objcache) == 0 )
  2925 		if ( count($objcache) == 0 )
  2926     {
  2926 		{
  2927       foreach ( $paths->nslist as $key => $_ )
  2927 			foreach ( $paths->nslist as $key => $_ )
  2928       {
  2928 			{
  2929         $objcache[$key] = array();
  2929 				$objcache[$key] = array();
  2930       }
  2930 			}
  2931     }
  2931 		}
  2932     
  2932 		
  2933     if ( isset($objcache[$namespace][$page_id]) )
  2933 		if ( isset($objcache[$namespace][$page_id]) )
  2934     {
  2934 		{
  2935       return $objcache[$namespace][$page_id];
  2935 			return $objcache[$namespace][$page_id];
  2936     }
  2936 		}
  2937     
  2937 		
  2938     $objcache[$namespace][$page_id] = new Session_ACLPageInfo( $page_id, $namespace, $this->acl_types, $this->acl_descs, $this->acl_deps, $this->acl_base_cache );
  2938 		$objcache[$namespace][$page_id] = new Session_ACLPageInfo( $page_id, $namespace, $this->acl_types, $this->acl_descs, $this->acl_deps, $this->acl_base_cache );
  2939     $object =& $objcache[$namespace][$page_id];
  2939 		$object =& $objcache[$namespace][$page_id];
  2940     
  2940 		
  2941     profiler_log("session: fetched ACLs for page {$namespace}:{$page_id}");
  2941 		profiler_log("session: fetched ACLs for page {$namespace}:{$page_id}");
  2942     
  2942 		
  2943     return $object;
  2943 		return $object;
  2944   }
  2944 	}
  2945   
  2945 	
  2946   /**
  2946 	/**
  2947    * Fetch the permissions that apply to an arbitrary user for the page specified. The object you get will have the get_permissions method
  2947  	* Fetch the permissions that apply to an arbitrary user for the page specified. The object you get will have the get_permissions method
  2948    * and several other abilities.
  2948  	* and several other abilities.
  2949    * @param int|string $user_id_or_name; user ID *or* username of the user
  2949  	* @param int|string $user_id_or_name; user ID *or* username of the user
  2950    * @param string $page_id; if null, will be default effective permissions. 
  2950  	* @param string $page_id; if null, will be default effective permissions. 
  2951    * @param string $namespace; if null, will be default effective permissions.
  2951  	* @param string $namespace; if null, will be default effective permissions.
  2952    * @return object
  2952  	* @return object
  2953    */
  2953  	*/
  2954   
  2954 	
  2955   function fetch_page_acl_user($user_id_or_name, $page_id, $namespace)
  2955 	function fetch_page_acl_user($user_id_or_name, $page_id, $namespace)
  2956   {
  2956 	{
  2957     global $db, $session, $paths, $template, $plugins; // Common objects
  2957 		global $db, $session, $paths, $template, $plugins; // Common objects
  2958     
  2958 		
  2959     // cache user info
  2959 		// cache user info
  2960     static $user_info_cache = null;
  2960 		static $user_info_cache = null;
  2961     
  2961 		
  2962     if ( isset($user_info_cache[$user_id_or_name]) )
  2962 		if ( isset($user_info_cache[$user_id_or_name]) )
  2963     {
  2963 		{
  2964       $user_id =& $user_info_cache[$user_id_or_name]['user_id'];
  2964 			$user_id =& $user_info_cache[$user_id_or_name]['user_id'];
  2965       $groups =& $user_info_cache[$user_id_or_name]['groups'];
  2965 			$groups =& $user_info_cache[$user_id_or_name]['groups'];
  2966     }
  2966 		}
  2967     else
  2967 		else
  2968     {
  2968 		{
  2969       $uid_column = ( is_int($user_id_or_name) ) ? "user_id = $user_id_or_name" : "username = '" . $db->escape($user_id_or_name) . "'";
  2969 			$uid_column = ( is_int($user_id_or_name) ) ? "user_id = $user_id_or_name" : "username = '" . $db->escape($user_id_or_name) . "'";
  2970       $q = $db->sql_query('SELECT u.user_id, m.group_id, g.group_name FROM ' . table_prefix . "users AS u\n"
  2970 			$q = $db->sql_query('SELECT u.user_id, m.group_id, g.group_name FROM ' . table_prefix . "users AS u\n"
  2971                         . "  LEFT JOIN " . table_prefix . "group_members AS m\n"
  2971 												. "  LEFT JOIN " . table_prefix . "group_members AS m\n"
  2972                         . "    ON ( ( u.user_id = m.user_id AND m.pending = 0 ) OR m.member_id IS NULL )\n"
  2972 												. "    ON ( ( u.user_id = m.user_id AND m.pending = 0 ) OR m.member_id IS NULL )\n"
  2973                         . "  LEFT JOIN " . table_prefix . "groups AS g\n"
  2973 												. "  LEFT JOIN " . table_prefix . "groups AS g\n"
  2974                         . "    ON ( g.group_id = m.group_id )\n"
  2974 												. "    ON ( g.group_id = m.group_id )\n"
  2975                         . "  WHERE $uid_column;");
  2975 												. "  WHERE $uid_column;");
  2976       if ( !$q )
  2976 			if ( !$q )
  2977         $db->_die();
  2977 				$db->_die();
  2978       
  2978 			
  2979       // The l10n engine takes care of this later.
  2979 			// The l10n engine takes care of this later.
  2980       $groups = array(1 => 'Everyone');
  2980 			$groups = array(1 => 'Everyone');
  2981       
  2981 			
  2982       if ( $row = $db->fetchrow() )
  2982 			if ( $row = $db->fetchrow() )
  2983       {
  2983 			{
  2984         $user_id = intval($row['user_id']);
  2984 				$user_id = intval($row['user_id']);
  2985         if ( $row['group_id'] )
  2985 				if ( $row['group_id'] )
  2986         {
  2986 				{
  2987           do
  2987 					do
  2988           {
  2988 					{
  2989             $groups[ intval($row['group_id'] ) ] = $row['group_name'];
  2989 						$groups[ intval($row['group_id'] ) ] = $row['group_name'];
  2990           }
  2990 					}
  2991           while ( $row = $db->fetchrow() );
  2991 					while ( $row = $db->fetchrow() );
  2992         }
  2992 				}
  2993         $db->free_result();
  2993 				$db->free_result();
  2994       }
  2994 			}
  2995       else
  2995 			else
  2996       {
  2996 			{
  2997         $db->free_result();
  2997 				$db->free_result();
  2998         throw new Exception('Unknown user ID or username');
  2998 				throw new Exception('Unknown user ID or username');
  2999       }
  2999 			}
  3000       
  3000 			
  3001       $user_info_cache[$user_id_or_name] = array(
  3001 			$user_info_cache[$user_id_or_name] = array(
  3002           'user_id' => $user_id,
  3002 					'user_id' => $user_id,
  3003           'groups' => $groups
  3003 					'groups' => $groups
  3004         );
  3004 				);
  3005     }
  3005 		}
  3006     
  3006 		
  3007     // cache base permissions
  3007 		// cache base permissions
  3008     static $base_cache = array();
  3008 		static $base_cache = array();
  3009     if ( !isset($base_cache[$user_id_or_name]) )
  3009 		if ( !isset($base_cache[$user_id_or_name]) )
  3010     {
  3010 		{
  3011       $base_cache[$user_id_or_name] = $this->acl_types;
  3011 			$base_cache[$user_id_or_name] = $this->acl_types;
  3012       $current_perms =& $base_cache[$user_id_or_name];
  3012 			$current_perms =& $base_cache[$user_id_or_name];
  3013       $current_perms['__resolve_table'] = array();
  3013 			$current_perms['__resolve_table'] = array();
  3014       
  3014 			
  3015       $bs = 'SELECT rules, target_type, target_id, rule_id, page_id, namespace, g.group_name FROM '.table_prefix."acl AS a\n"
  3015 			$bs = 'SELECT rules, target_type, target_id, rule_id, page_id, namespace, g.group_name FROM '.table_prefix."acl AS a\n"
  3016           . "  LEFT JOIN " . table_prefix . "groups AS g\n"
  3016 					. "  LEFT JOIN " . table_prefix . "groups AS g\n"
  3017           . "    ON ( ( a.target_type = " . ACL_TYPE_GROUP . " AND a.target_id = g.group_id ) OR ( a.target_type != " . ACL_TYPE_GROUP . " ) )\n"
  3017 					. "    ON ( ( a.target_type = " . ACL_TYPE_GROUP . " AND a.target_id = g.group_id ) OR ( a.target_type != " . ACL_TYPE_GROUP . " ) )\n"
  3018           . '  WHERE page_id IS NULL AND namespace IS NULL AND' . "\n"
  3018 					. '  WHERE page_id IS NULL AND namespace IS NULL AND' . "\n"
  3019           . '  ( ';
  3019 					. '  ( ';
  3020     
  3020 		
  3021       $q = Array();
  3021 			$q = Array();
  3022       $q[] = '( target_type='.ACL_TYPE_USER.' AND target_id= ' . $user_id . ' )';
  3022 			$q[] = '( target_type='.ACL_TYPE_USER.' AND target_id= ' . $user_id . ' )';
  3023       if(count($groups) > 0)
  3023 			if(count($groups) > 0)
  3024       {
  3024 			{
  3025         foreach($groups as $g_id => $g_name)
  3025 				foreach($groups as $g_id => $g_name)
  3026         {
  3026 				{
  3027           $q[] = '( target_type='.ACL_TYPE_GROUP.' AND target_id='.intval($g_id).' )';
  3027 					$q[] = '( target_type='.ACL_TYPE_GROUP.' AND target_id='.intval($g_id).' )';
  3028         }
  3028 				}
  3029       }
  3029 			}
  3030       $bs .= implode(" OR \n    ", $q) . " ) \n  ORDER BY target_type ASC, target_id ASC;";
  3030 			$bs .= implode(" OR \n    ", $q) . " ) \n  ORDER BY target_type ASC, target_id ASC;";
  3031       $q = $this->sql($bs);
  3031 			$q = $this->sql($bs);
  3032       foreach ( $this->acl_types as $perm_type => $_ )
  3032 			foreach ( $this->acl_types as $perm_type => $_ )
  3033       {
  3033 			{
  3034         // init the resolver table with blanks
  3034 				// init the resolver table with blanks
  3035         $current_perms['__resolve_table'][$perm_type] = array(
  3035 				$current_perms['__resolve_table'][$perm_type] = array(
  3036             'src' => ACL_INHERIT_ENANO_DEFAULT,
  3036 						'src' => ACL_INHERIT_ENANO_DEFAULT,
  3037             'rule_id' => -1
  3037 						'rule_id' => -1
  3038           );
  3038 					);
  3039       }
  3039 			}
  3040       if ( $row = $db->fetchrow() )
  3040 			if ( $row = $db->fetchrow() )
  3041       {
  3041 			{
  3042         do {
  3042 				do {
  3043           $rules = $this->string_to_perm($row['rules']);
  3043 					$rules = $this->string_to_perm($row['rules']);
  3044           $is_everyone = ( $row['target_type'] == ACL_TYPE_GROUP && $row['target_id'] == 1 );
  3044 					$is_everyone = ( $row['target_type'] == ACL_TYPE_GROUP && $row['target_id'] == 1 );
  3045           // track where these rulings are coming from
  3045 					// track where these rulings are coming from
  3046           $src = ( $is_everyone ) ? ACL_INHERIT_GLOBAL_EVERYONE : ( $row['target_type'] == ACL_TYPE_GROUP ? ACL_INHERIT_GLOBAL_GROUP : ACL_INHERIT_GLOBAL_USER );
  3046 					$src = ( $is_everyone ) ? ACL_INHERIT_GLOBAL_EVERYONE : ( $row['target_type'] == ACL_TYPE_GROUP ? ACL_INHERIT_GLOBAL_GROUP : ACL_INHERIT_GLOBAL_USER );
  3047           foreach ( $rules as $perm_type => $_ )
  3047 					foreach ( $rules as $perm_type => $_ )
  3048           {
  3048 					{
  3049             $current_perms['__resolve_table'][$perm_type] = array(
  3049 						$current_perms['__resolve_table'][$perm_type] = array(
  3050                 'src' => $src,
  3050 								'src' => $src,
  3051                 'rule_id' => $row['rule_id']
  3051 								'rule_id' => $row['rule_id']
  3052               );
  3052 							);
  3053             if ( $row['group_name'] )
  3053 						if ( $row['group_name'] )
  3054             {
  3054 						{
  3055               $current_perms['__resolve_table'][$perm_type]['group_name'] = $row['group_name'];
  3055 							$current_perms['__resolve_table'][$perm_type]['group_name'] = $row['group_name'];
  3056             }
  3056 						}
  3057           }
  3057 					}
  3058           // merge it in
  3058 					// merge it in
  3059           $current_perms = $this->acl_merge($current_perms, $rules, $is_everyone, $_defaults_used);
  3059 					$current_perms = $this->acl_merge($current_perms, $rules, $is_everyone, $_defaults_used);
  3060         } while ( $row = $db->fetchrow() );
  3060 				} while ( $row = $db->fetchrow() );
  3061       }
  3061 			}
  3062     }
  3062 		}
  3063     
  3063 		
  3064     // cache of permission objects (to save RAM and SQL queries)
  3064 		// cache of permission objects (to save RAM and SQL queries)
  3065     static $objcache = array();
  3065 		static $objcache = array();
  3066     
  3066 		
  3067     if ( count($objcache) == 0 )
  3067 		if ( count($objcache) == 0 )
  3068     {
  3068 		{
  3069       foreach ( $paths->nslist as $key => $_ )
  3069 			foreach ( $paths->nslist as $key => $_ )
  3070       {
  3070 			{
  3071         $objcache[$key] = array();
  3071 				$objcache[$key] = array();
  3072       }
  3072 			}
  3073     }
  3073 		}
  3074     
  3074 		
  3075     if ( !isset($objcache[$namespace][$page_id]) )
  3075 		if ( !isset($objcache[$namespace][$page_id]) )
  3076     {
  3076 		{
  3077       $objcache[$namespace][$page_id] = array();
  3077 			$objcache[$namespace][$page_id] = array();
  3078     }
  3078 		}
  3079     
  3079 		
  3080     if ( isset($objcache[$namespace][$page_id][$user_id_or_name]) )
  3080 		if ( isset($objcache[$namespace][$page_id][$user_id_or_name]) )
  3081     {
  3081 		{
  3082       return $objcache[$namespace][$page_id][$user_id_or_name];
  3082 			return $objcache[$namespace][$page_id][$user_id_or_name];
  3083     }
  3083 		}
  3084     
  3084 		
  3085     //if ( !isset( $paths->pages[$paths->nslist[$namespace] . $page_id] ) )
  3085 		//if ( !isset( $paths->pages[$paths->nslist[$namespace] . $page_id] ) )
  3086     //{
  3086 		//{
  3087     //  // Page does not exist
  3087 		//  // Page does not exist
  3088     //  return false;
  3088 		//  return false;
  3089     //}
  3089 		//}
  3090     
  3090 		
  3091     $objcache[$namespace][$page_id][$user_id_or_name] = new Session_ACLPageInfo(
  3091 		$objcache[$namespace][$page_id][$user_id_or_name] = new Session_ACLPageInfo(
  3092       $page_id,                                        // $page_id, 
  3092 			$page_id,                                        // $page_id, 
  3093       $namespace,                                      // $namespace,
  3093 			$namespace,                                      // $namespace,
  3094       $this->acl_types,                                // $acl_types,
  3094 			$this->acl_types,                                // $acl_types,
  3095       $this->acl_descs,                                // $acl_descs,
  3095 			$this->acl_descs,                                // $acl_descs,
  3096       $this->acl_deps,                                 // $acl_deps,
  3096 			$this->acl_deps,                                 // $acl_deps,
  3097       $base_cache[$user_id_or_name],                   // $base,
  3097 			$base_cache[$user_id_or_name],                   // $base,
  3098       $user_info_cache[$user_id_or_name]['user_id'],   // $user_id = null,
  3098 			$user_info_cache[$user_id_or_name]['user_id'],   // $user_id = null,
  3099       $user_info_cache[$user_id_or_name]['groups'],    // $groups = null,
  3099 			$user_info_cache[$user_id_or_name]['groups'],    // $groups = null,
  3100       $base_cache[$user_id_or_name]['__resolve_table'] // $resolve_table = array()
  3100 			$base_cache[$user_id_or_name]['__resolve_table'] // $resolve_table = array()
  3101     );
  3101 		);
  3102     $object =& $objcache[$namespace][$page_id][$user_id_or_name];
  3102 		$object =& $objcache[$namespace][$page_id][$user_id_or_name];
  3103     
  3103 		
  3104     return $object;
  3104 		return $object;
  3105   }
  3105 	}
  3106   
  3106 	
  3107   /**
  3107 	/**
  3108    * Checks if the given ACL rule type applies to a namespace.
  3108  	* Checks if the given ACL rule type applies to a namespace.
  3109    * @param string ACL rule type
  3109  	* @param string ACL rule type
  3110    * @param string Namespace
  3110  	* @param string Namespace
  3111    * @return bool
  3111  	* @return bool
  3112    */
  3112  	*/
  3113   
  3113 	
  3114   function check_acl_scope($acl_rule, $namespace)
  3114 	function check_acl_scope($acl_rule, $namespace)
  3115   {
  3115 	{
  3116     if ( !isset($this->acl_scope[$acl_rule]) )
  3116 		if ( !isset($this->acl_scope[$acl_rule]) )
  3117       return false;
  3117 			return false;
  3118     if ( $this->acl_scope[$acl_rule] === array('All') )
  3118 		if ( $this->acl_scope[$acl_rule] === array('All') )
  3119       return true;
  3119 			return true;
  3120     return ( in_array($namespace, $this->acl_scope[$acl_rule]) ) ? true : false;
  3120 		return ( in_array($namespace, $this->acl_scope[$acl_rule]) ) ? true : false;
  3121   }
  3121 	}
  3122   
  3122 	
  3123   /**
  3123 	/**
  3124    * Read all of our permissions from the database and process/apply them. This should be called after the page is determined.
  3124  	* Read all of our permissions from the database and process/apply them. This should be called after the page is determined.
  3125    * @access private
  3125  	* @access private
  3126    */
  3126  	*/
  3127   
  3127 	
  3128   function init_permissions()
  3128 	function init_permissions()
  3129   {
  3129 	{
  3130     global $db, $session, $paths, $template, $plugins; // Common objects
  3130 		global $db, $session, $paths, $template, $plugins; // Common objects
  3131     // Initialize the permissions list with some defaults
  3131 		// Initialize the permissions list with some defaults
  3132     $this->perms = $this->acl_types;
  3132 		$this->perms = $this->acl_types;
  3133     $this->acl_defaults_used = $this->perms;
  3133 		$this->acl_defaults_used = $this->perms;
  3134     
  3134 		
  3135     // Fetch sitewide defaults from the permissions table
  3135 		// Fetch sitewide defaults from the permissions table
  3136     $bs = 'SELECT rules, target_type, target_id FROM '.table_prefix.'acl' . "\n"
  3136 		$bs = 'SELECT rules, target_type, target_id FROM '.table_prefix.'acl' . "\n"
  3137              . '  WHERE page_id IS NULL AND namespace IS NULL AND' . "\n"
  3137  						. '  WHERE page_id IS NULL AND namespace IS NULL AND' . "\n"
  3138              . '  ( ';
  3138  						. '  ( ';
  3139     
  3139 		
  3140     $q = Array();
  3140 		$q = Array();
  3141     $q[] = '( target_type='.ACL_TYPE_USER.' AND target_id='.$this->user_id.' )';
  3141 		$q[] = '( target_type='.ACL_TYPE_USER.' AND target_id='.$this->user_id.' )';
  3142     if(count($this->groups) > 0)
  3142 		if(count($this->groups) > 0)
  3143     {
  3143 		{
  3144       foreach($this->groups as $g_id => $g_name)
  3144 			foreach($this->groups as $g_id => $g_name)
  3145       {
  3145 			{
  3146         $q[] = '( target_type='.ACL_TYPE_GROUP.' AND target_id='.intval($g_id).' )';
  3146 				$q[] = '( target_type='.ACL_TYPE_GROUP.' AND target_id='.intval($g_id).' )';
  3147       }
  3147 			}
  3148     }
  3148 		}
  3149     $bs .= implode(" OR \n    ", $q) . " ) \n  ORDER BY target_type ASC, target_id ASC;";
  3149 		$bs .= implode(" OR \n    ", $q) . " ) \n  ORDER BY target_type ASC, target_id ASC;";
  3150     $q = $this->sql($bs);
  3150 		$q = $this->sql($bs);
  3151     if ( $row = $db->fetchrow() )
  3151 		if ( $row = $db->fetchrow() )
  3152     {
  3152 		{
  3153       do {
  3153 			do {
  3154         $rules = $this->string_to_perm($row['rules']);
  3154 				$rules = $this->string_to_perm($row['rules']);
  3155         $is_everyone = ( $row['target_type'] == ACL_TYPE_GROUP && $row['target_id'] == 1 );
  3155 				$is_everyone = ( $row['target_type'] == ACL_TYPE_GROUP && $row['target_id'] == 1 );
  3156         $this->acl_merge_with_current($rules, $is_everyone);
  3156 				$this->acl_merge_with_current($rules, $is_everyone);
  3157       } while ( $row = $db->fetchrow() );
  3157 			} while ( $row = $db->fetchrow() );
  3158     }
  3158 		}
  3159     
  3159 		
  3160     // Cache the sitewide permissions for later use
  3160 		// Cache the sitewide permissions for later use
  3161     $this->acl_base_cache = $this->perms;
  3161 		$this->acl_base_cache = $this->perms;
  3162     
  3162 		
  3163     profiler_log('session: base ACL set calculated');
  3163 		profiler_log('session: base ACL set calculated');
  3164     
  3164 		
  3165     // Load and calculate permissions for the current page
  3165 		// Load and calculate permissions for the current page
  3166     $page_acl = $this->fetch_page_acl($paths->page_id, $paths->namespace);
  3166 		$page_acl = $this->fetch_page_acl($paths->page_id, $paths->namespace);
  3167     $this->perms = $page_acl->perms;
  3167 		$this->perms = $page_acl->perms;
  3168     $this->acl_defaults_used = $page_acl->acl_defaults_used;
  3168 		$this->acl_defaults_used = $page_acl->acl_defaults_used;
  3169   }
  3169 	}
  3170   
  3170 	
  3171   /**
  3171 	/**
  3172    * Extends the scope of a permission type.
  3172  	* Extends the scope of a permission type.
  3173    * @param string The name of the permission type
  3173  	* @param string The name of the permission type
  3174    * @param string The namespace(s) that should be covered. This can be either one namespace ID or a pipe-delimited list.
  3174  	* @param string The namespace(s) that should be covered. This can be either one namespace ID or a pipe-delimited list.
  3175    * @param object Optional - the current $paths object, in case we're doing this from the acl_rule_init hook
  3175  	* @param object Optional - the current $paths object, in case we're doing this from the acl_rule_init hook
  3176    */
  3176  	*/
  3177    
  3177  	
  3178   function acl_extend_scope($perm_type, $namespaces, &$p_in)
  3178 	function acl_extend_scope($perm_type, $namespaces, &$p_in)
  3179   {
  3179 	{
  3180     global $db, $session, $paths, $template, $plugins; // Common objects
  3180 		global $db, $session, $paths, $template, $plugins; // Common objects
  3181     $p_obj = ( is_object($p_in) ) ? $p_in : $paths;
  3181 		$p_obj = ( is_object($p_in) ) ? $p_in : $paths;
  3182     $nslist = explode('|', $namespaces);
  3182 		$nslist = explode('|', $namespaces);
  3183     foreach ( $nslist as $i => $ns )
  3183 		foreach ( $nslist as $i => $ns )
  3184     {
  3184 		{
  3185       if ( !isset($p_obj->nslist[$ns]) )
  3185 			if ( !isset($p_obj->nslist[$ns]) )
  3186       {
  3186 			{
  3187         unset($nslist[$i]);
  3187 				unset($nslist[$i]);
  3188       }
  3188 			}
  3189       else
  3189 			else
  3190       {
  3190 			{
  3191         if ( $this->acl_scope[$perm_type] !== array('All') )
  3191 				if ( $this->acl_scope[$perm_type] !== array('All') )
  3192           $this->acl_scope[$perm_type][] = $ns;
  3192 					$this->acl_scope[$perm_type][] = $ns;
  3193         if ( isset($this->acl_types[$perm_type]) && !isset($this->perms[$perm_type]) )
  3193 				if ( isset($this->acl_types[$perm_type]) && !isset($this->perms[$perm_type]) )
  3194         {
  3194 				{
  3195           $this->perms[$perm_type] = $this->acl_types[$perm_type];
  3195 					$this->perms[$perm_type] = $this->acl_types[$perm_type];
  3196         }
  3196 				}
  3197       }
  3197 			}
  3198     }
  3198 		}
  3199   }
  3199 	}
  3200   
  3200 	
  3201   /**
  3201 	/**
  3202    * Converts a permissions field into a string for database insertion. Similar in spirit to serialize().
  3202  	* Converts a permissions field into a string for database insertion. Similar in spirit to serialize().
  3203    * @param array $perms An associative array with only integers as values
  3203  	* @param array $perms An associative array with only integers as values
  3204    * @return string
  3204  	* @return string
  3205    */
  3205  	*/
  3206    
  3206  	
  3207   function perm_to_string($perms)
  3207 	function perm_to_string($perms)
  3208   {
  3208 	{
  3209     $s = '';
  3209 		$s = '';
  3210     foreach($perms as $perm => $ac)
  3210 		foreach($perms as $perm => $ac)
  3211     {
  3211 		{
  3212       if ( $ac == 'i' )
  3212 			if ( $ac == 'i' )
  3213         continue;
  3213 				continue;
  3214       $s .= "$perm=$ac;";
  3214 			$s .= "$perm=$ac;";
  3215     }
  3215 		}
  3216     return $s;
  3216 		return $s;
  3217   }
  3217 	}
  3218   
  3218 	
  3219   /**
  3219 	/**
  3220    * Converts a permissions string back to an array.
  3220  	* Converts a permissions string back to an array.
  3221    * @param string $perms The result from sessionManager::perm_to_string()
  3221  	* @param string $perms The result from sessionManager::perm_to_string()
  3222    * @return array
  3222  	* @return array
  3223    */
  3223  	*/
  3224    
  3224  	
  3225   function string_to_perm($perms)
  3225 	function string_to_perm($perms)
  3226   {
  3226 	{
  3227     $ret = Array();
  3227 		$ret = Array();
  3228     preg_match_all('#([a-z0-9_-]+)=([0-9]+);#i', $perms, $matches);
  3228 		preg_match_all('#([a-z0-9_-]+)=([0-9]+);#i', $perms, $matches);
  3229     foreach($matches[1] as $i => $t)
  3229 		foreach($matches[1] as $i => $t)
  3230     {
  3230 		{
  3231       $ret[$t] = intval($matches[2][$i]);
  3231 			$ret[$t] = intval($matches[2][$i]);
  3232     }
  3232 		}
  3233     return $ret;
  3233 		return $ret;
  3234   }
  3234 	}
  3235   
  3235 	
  3236   /**
  3236 	/**
  3237    * Merges two ACL arrays. Both parameters should be permission list arrays. The second group takes precedence over the first, but AUTH_DENY always prevails.
  3237  	* Merges two ACL arrays. Both parameters should be permission list arrays. The second group takes precedence over the first, but AUTH_DENY always prevails.
  3238    * @param array $perm1 The first set of permissions
  3238  	* @param array $perm1 The first set of permissions
  3239    * @param array $perm2 The second set of permissions
  3239  	* @param array $perm2 The second set of permissions
  3240    * @param bool $is_everyone If true, applies exceptions for "Everyone" group
  3240  	* @param bool $is_everyone If true, applies exceptions for "Everyone" group
  3241    * @param array|reference $defaults_used Array that will be filled with default usage data
  3241  	* @param array|reference $defaults_used Array that will be filled with default usage data
  3242    * @return array
  3242  	* @return array
  3243    */
  3243  	*/
  3244    
  3244  	
  3245   function acl_merge($perm1, $perm2, $is_everyone = false, &$defaults_used = array())
  3245 	function acl_merge($perm1, $perm2, $is_everyone = false, &$defaults_used = array())
  3246   {
  3246 	{
  3247     $ret = $perm1;
  3247 		$ret = $perm1;
  3248     if ( !is_array(@$defaults_used) )
  3248 		if ( !is_array(@$defaults_used) )
  3249     {
  3249 		{
  3250       $defaults_used = array();
  3250 			$defaults_used = array();
  3251     }
  3251 		}
  3252     foreach ( $perm1 as $i => $p )
  3252 		foreach ( $perm1 as $i => $p )
  3253     {
  3253 		{
  3254       if ( isset($perm2[$i]) )
  3254 			if ( isset($perm2[$i]) )
  3255       {
  3255 			{
  3256         if ( $is_everyone && isset($defaults_used[$i]) && $defaults_used[$i] === false )
  3256 				if ( $is_everyone && isset($defaults_used[$i]) && $defaults_used[$i] === false )
  3257           continue;
  3257 					continue;
  3258         // Decide precedence
  3258 				// Decide precedence
  3259         if ( isset($defaults_used[$i]) )
  3259 				if ( isset($defaults_used[$i]) )
  3260         {
  3260 				{
  3261           // echo "$i: default in use, overriding to: {$perm2[$i]}<br />";
  3261 					// echo "$i: default in use, overriding to: {$perm2[$i]}<br />";
  3262           // Defaults are in use, override
  3262 					// Defaults are in use, override
  3263           
  3263 					
  3264           // CHANGED - 1.1.4:
  3264 					// CHANGED - 1.1.4:
  3265           // For some time this has been intentionally relaxed so that the following
  3265 					// For some time this has been intentionally relaxed so that the following
  3266           // exception is available to Deny permissions:
  3266 					// exception is available to Deny permissions:
  3267           //   If the rule applies to the group "Everyone" on the entire site,
  3267 					//   If the rule applies to the group "Everyone" on the entire site,
  3268           //   Deny settings could be overriden.
  3268 					//   Deny settings could be overriden.
  3269           // This is documented at: http://docs.enanocms.org/Help:4.2
  3269 					// This is documented at: http://docs.enanocms.org/Help:4.2
  3270           if ( $perm1[$i] != AUTH_DENY )
  3270 					if ( $perm1[$i] != AUTH_DENY )
  3271           {
  3271 					{
  3272             $perm1[$i] = $perm2[$i];
  3272 						$perm1[$i] = $perm2[$i];
  3273             $defaults_used[$i] = ( $is_everyone );
  3273 						$defaults_used[$i] = ( $is_everyone );
  3274           }
  3274 					}
  3275         }
  3275 				}
  3276         else
  3276 				else
  3277         {
  3277 				{
  3278           // echo "$i: default NOT in use";
  3278 					// echo "$i: default NOT in use";
  3279           // Defaults are not in use, merge as normal
  3279 					// Defaults are not in use, merge as normal
  3280           if ( $perm1[$i] != AUTH_DENY )
  3280 					if ( $perm1[$i] != AUTH_DENY )
  3281           {
  3281 					{
  3282             // echo ", but overriding";
  3282 						// echo ", but overriding";
  3283             $perm1[$i] = $perm2[$i];
  3283 						$perm1[$i] = $perm2[$i];
  3284           }
  3284 					}
  3285           // echo "<br />";
  3285 					// echo "<br />";
  3286         }
  3286 				}
  3287       }
  3287 			}
  3288     }
  3288 		}
  3289     return $perm1;
  3289 		return $perm1;
  3290   }
  3290 	}
  3291   
  3291 	
  3292   /**
  3292 	/**
  3293    * Merges two ACL arrays, but instead of calculating inheritance for missing permission types, just returns 'i' for that type. Useful
  3293  	* Merges two ACL arrays, but instead of calculating inheritance for missing permission types, just returns 'i' for that type. Useful
  3294    * for explicitly requiring inheritance in ACL editing interfaces
  3294  	* for explicitly requiring inheritance in ACL editing interfaces
  3295    * @param array $perm1 The first set of permissions
  3295  	* @param array $perm1 The first set of permissions
  3296    * @param array $perm2 The second, authoritative set of permissions
  3296  	* @param array $perm2 The second, authoritative set of permissions
  3297    */
  3297  	*/
  3298   
  3298 	
  3299   function acl_merge_inherit($perm1, $perm2)
  3299 	function acl_merge_inherit($perm1, $perm2)
  3300   {
  3300 	{
  3301     foreach ( $perm1 as $type => $level )
  3301 		foreach ( $perm1 as $type => $level )
  3302     {
  3302 		{
  3303       $perm1[$type][$level] = 'i';
  3303 			$perm1[$type][$level] = 'i';
  3304     }
  3304 		}
  3305     $ret = $perm1;
  3305 		$ret = $perm1;
  3306     foreach ( $perm2 as $type => $level )
  3306 		foreach ( $perm2 as $type => $level )
  3307     {
  3307 		{
  3308       if ( isset( $ret[$type] ) )
  3308 			if ( isset( $ret[$type] ) )
  3309       {
  3309 			{
  3310         if ( $ret[$type] != AUTH_DENY )
  3310 				if ( $ret[$type] != AUTH_DENY )
  3311           $ret[$type] = $level;
  3311 					$ret[$type] = $level;
  3312       }
  3312 			}
  3313     }
  3313 		}
  3314     return $ret;
  3314 		return $ret;
  3315   }
  3315 	}
  3316   
  3316 	
  3317   /**
  3317 	/**
  3318    * Merges the ACL array sent with the current permissions table, deciding precedence based on whether defaults are in effect or not.
  3318  	* Merges the ACL array sent with the current permissions table, deciding precedence based on whether defaults are in effect or not.
  3319    * @param array The array to merge into the master ACL list
  3319  	* @param array The array to merge into the master ACL list
  3320    * @param bool If true, $perm is treated as the "new default"
  3320  	* @param bool If true, $perm is treated as the "new default"
  3321    * @param int 1 if this is a site-wide ACL, 2 if page-specific. Defaults to 2.
  3321  	* @param int 1 if this is a site-wide ACL, 2 if page-specific. Defaults to 2.
  3322    */
  3322  	*/
  3323   
  3323 	
  3324   function acl_merge_with_current($perm, $is_everyone = false, $scope = 2)
  3324 	function acl_merge_with_current($perm, $is_everyone = false, $scope = 2)
  3325   {
  3325 	{
  3326     $this->perms = $this->acl_merge($this->perms, $perm, $is_everyone, $this->acl_defaults_used);
  3326 		$this->perms = $this->acl_merge($this->perms, $perm, $is_everyone, $this->acl_defaults_used);
  3327   }
  3327 	}
  3328   
  3328 	
  3329   /**
  3329 	/**
  3330    * Merges two ACL arrays. Both parameters should be permission list arrays. The second group takes precedence
  3330  	* Merges two ACL arrays. Both parameters should be permission list arrays. The second group takes precedence
  3331    * over the first, without exceptions. This is used to merge the hardcoded defaults with admin-specified
  3331  	* over the first, without exceptions. This is used to merge the hardcoded defaults with admin-specified
  3332    * defaults, which take precedence.
  3332  	* defaults, which take precedence.
  3333    * @param array $perm1 The first set of permissions
  3333  	* @param array $perm1 The first set of permissions
  3334    * @param array $perm2 The second set of permissions
  3334  	* @param array $perm2 The second set of permissions
  3335    * @return array
  3335  	* @return array
  3336    */
  3336  	*/
  3337    
  3337  	
  3338   function acl_merge_complete($perm1, $perm2)
  3338 	function acl_merge_complete($perm1, $perm2)
  3339   {
  3339 	{
  3340     $ret = $perm1;
  3340 		$ret = $perm1;
  3341     foreach ( $perm2 as $type => $level )
  3341 		foreach ( $perm2 as $type => $level )
  3342     {
  3342 		{
  3343       $ret[$type] = $level;
  3343 			$ret[$type] = $level;
  3344     }
  3344 		}
  3345     return $ret;
  3345 		return $ret;
  3346   }
  3346 	}
  3347   
  3347 	
  3348   /**
  3348 	/**
  3349    * Tell us if the dependencies for a given permission are met.
  3349  	* Tell us if the dependencies for a given permission are met.
  3350    * @param string The ACL permission ID
  3350  	* @param string The ACL permission ID
  3351    * @return bool
  3351  	* @return bool
  3352    */
  3352  	*/
  3353    
  3353  	
  3354   function acl_check_deps($type, $debug = false)
  3354 	function acl_check_deps($type, $debug = false)
  3355   {
  3355 	{
  3356     global $paths;
  3356 		global $paths;
  3357     
  3357 		
  3358     // This will only happen if the permissions table is hacked or improperly accessed
  3358 		// This will only happen if the permissions table is hacked or improperly accessed
  3359     if(!isset($this->acl_deps[$type]))
  3359 		if(!isset($this->acl_deps[$type]))
  3360       return true;
  3360 			return true;
  3361     // Permission has no dependencies?
  3361 		// Permission has no dependencies?
  3362     if(sizeof($this->acl_deps[$type]) < 1)
  3362 		if(sizeof($this->acl_deps[$type]) < 1)
  3363       return true;
  3363 			return true;
  3364     // go through them all and build a flat list of dependencies
  3364 		// go through them all and build a flat list of dependencies
  3365     $deps = $this->acl_deps[$type];
  3365 		$deps = $this->acl_deps[$type];
  3366     while(true)
  3366 		while(true)
  3367     {
  3367 		{
  3368       $j = sizeof($deps);
  3368 			$j = sizeof($deps);
  3369       for ( $i = 0; $i < $j; $i++ )
  3369 			for ( $i = 0; $i < $j; $i++ )
  3370       {
  3370 			{
  3371         $b = $deps;
  3371 				$b = $deps;
  3372         if ( !$this->check_acl_scope($deps[$i], $paths->namespace) )
  3372 				if ( !$this->check_acl_scope($deps[$i], $paths->namespace) )
  3373         {
  3373 				{
  3374           // Action $type depends on action $deps[$i] which cannot be satisfied because $deps[$i] is out of scope.
  3374 					// Action $type depends on action $deps[$i] which cannot be satisfied because $deps[$i] is out of scope.
  3375           // echo '<pre>' . enano_debug_print_backtrace(true) . '</pre>';
  3375 					// echo '<pre>' . enano_debug_print_backtrace(true) . '</pre>';
  3376           trigger_error("acl_check_deps: $type depends on {$deps[$i]} which is not within scope of $paths->namespace; this indicats a bug in ACL rule specification", E_USER_WARNING);
  3376 					trigger_error("acl_check_deps: $type depends on {$deps[$i]} which is not within scope of $paths->namespace; this indicats a bug in ACL rule specification", E_USER_WARNING);
  3377           return false;
  3377 					return false;
  3378         }
  3378 				}
  3379         $deps = array_merge($deps, $this->acl_deps[$deps[$i]]);
  3379 				$deps = array_merge($deps, $this->acl_deps[$deps[$i]]);
  3380         if( $b == $deps )
  3380 				if( $b == $deps )
  3381         {
  3381 				{
  3382           break 2;
  3382 					break 2;
  3383         }
  3383 				}
  3384         $j = sizeof($deps);
  3384 				$j = sizeof($deps);
  3385       }
  3385 			}
  3386     }
  3386 		}
  3387     $debugdata = array();
  3387 		$debugdata = array();
  3388     foreach($deps as $d)
  3388 		foreach($deps as $d)
  3389     {
  3389 		{
  3390       // Our dependencies are fully resolved, so tell get_permissions() to not recursively call this function
  3390 			// Our dependencies are fully resolved, so tell get_permissions() to not recursively call this function
  3391       if ( !$this->get_permissions($d, true) )
  3391 			if ( !$this->get_permissions($d, true) )
  3392       {
  3392 			{
  3393         if ( $debug )
  3393 				if ( $debug )
  3394         {
  3394 				{
  3395           $debugdata[] = $d;
  3395 					$debugdata[] = $d;
  3396         }
  3396 				}
  3397         else
  3397 				else
  3398         {
  3398 				{
  3399           return false;
  3399 					return false;
  3400         }
  3400 				}
  3401       }
  3401 			}
  3402     }
  3402 		}
  3403     return $debug ? $debugdata : true;
  3403 		return $debug ? $debugdata : true;
  3404   }
  3404 	}
  3405   
  3405 	
  3406   /**
  3406 	/**
  3407    * Makes a CAPTCHA code and caches the code in the database
  3407  	* Makes a CAPTCHA code and caches the code in the database
  3408    * @param int $len The length of the code, in bytes
  3408  	* @param int $len The length of the code, in bytes
  3409    * @param string Optional, the hash to reuse
  3409  	* @param string Optional, the hash to reuse
  3410    * @return string A unique identifier assigned to the code. This hash should be passed to sessionManager::getCaptcha() to retrieve the code.
  3410  	* @return string A unique identifier assigned to the code. This hash should be passed to sessionManager::getCaptcha() to retrieve the code.
  3411    */
  3411  	*/
  3412   
  3412 	
  3413   function make_captcha($len = 7, $hash = '')
  3413 	function make_captcha($len = 7, $hash = '')
  3414   {
  3414 	{
  3415     global $db, $session, $paths, $template, $plugins; // Common objects
  3415 		global $db, $session, $paths, $template, $plugins; // Common objects
  3416     $code = $this->generate_captcha_code($len);
  3416 		$code = $this->generate_captcha_code($len);
  3417     if ( !preg_match('/^[a-f0-9]{32}([a-z0-9]{8})?$/', $hash) )
  3417 		if ( !preg_match('/^[a-f0-9]{32}([a-z0-9]{8})?$/', $hash) )
  3418       $hash = md5(microtime() . mt_rand());
  3418 			$hash = md5(microtime() . mt_rand());
  3419     $session_data = $db->escape(serialize(array()));
  3419 		$session_data = $db->escape(serialize(array()));
  3420     
  3420 		
  3421     // sanity check
  3421 		// sanity check
  3422     if ( !is_valid_ip(@$_SERVER['REMOTE_ADDR']) || !is_int($this->user_id) )
  3422 		if ( !is_valid_ip(@$_SERVER['REMOTE_ADDR']) || !is_int($this->user_id) )
  3423       return false;
  3423 			return false;
  3424     
  3424 		
  3425     $this->sql('DELETE FROM ' . table_prefix . "captcha WHERE session_id = '$hash';");
  3425 		$this->sql('DELETE FROM ' . table_prefix . "captcha WHERE session_id = '$hash';");
  3426     $this->sql('INSERT INTO ' . table_prefix . 'captcha(session_id, code, session_data, source_ip, user_id)' . " VALUES('$hash', '$code', '$session_data', '{$_SERVER['REMOTE_ADDR']}', {$this->user_id});");
  3426 		$this->sql('INSERT INTO ' . table_prefix . 'captcha(session_id, code, session_data, source_ip, user_id)' . " VALUES('$hash', '$code', '$session_data', '{$_SERVER['REMOTE_ADDR']}', {$this->user_id});");
  3427     return $hash;
  3427 		return $hash;
  3428   }
  3428 	}
  3429   
  3429 	
  3430   /**
  3430 	/**
  3431    * Generates a "pronouncable" or "human-friendly" word using various phonics rules
  3431  	* Generates a "pronouncable" or "human-friendly" word using various phonics rules
  3432    * @param int Optional. The length of the word.
  3432  	* @param int Optional. The length of the word.
  3433    * @return string
  3433  	* @return string
  3434    */
  3434  	*/
  3435   
  3435 	
  3436   function generate_captcha_code($len = 7)
  3436 	function generate_captcha_code($len = 7)
  3437   {
  3437 	{
  3438     // don't use k and x, they get mixed up a lot
  3438 		// don't use k and x, they get mixed up a lot
  3439     $consonants = 'bcdfghmnpqrsvwyz';
  3439 		$consonants = 'bcdfghmnpqrsvwyz';
  3440     $vowels = 'aeiou';
  3440 		$vowels = 'aeiou';
  3441     $prev = 'vowel';
  3441 		$prev = 'vowel';
  3442     $prev_l = '';
  3442 		$prev_l = '';
  3443     $word = '';
  3443 		$word = '';
  3444     $allow_next_vowel = true;
  3444 		$allow_next_vowel = true;
  3445     for ( $i = 0; $i < $len; $i++ )
  3445 		for ( $i = 0; $i < $len; $i++ )
  3446     {
  3446 		{
  3447       if ( $prev == 'vowel' )
  3447 			if ( $prev == 'vowel' )
  3448       {
  3448 			{
  3449         $allow_next_vowel = false;
  3449 				$allow_next_vowel = false;
  3450         if ( $prev_l == 'o' && mt_rand(0, 3) == 3 && $allow_next_vowel )
  3450 				if ( $prev_l == 'o' && mt_rand(0, 3) == 3 && $allow_next_vowel )
  3451           $word .= 'i';
  3451 					$word .= 'i';
  3452         else if ( $prev_l == 'q' && mt_rand(0, 3) != 1 && $allow_next_vowel )
  3452 				else if ( $prev_l == 'q' && mt_rand(0, 3) != 1 && $allow_next_vowel )
  3453           $word .= 'u';
  3453 					$word .= 'u';
  3454         else if ( $prev_l == 'o' && mt_rand(0, 3) == 2 && $allow_next_vowel )
  3454 				else if ( $prev_l == 'o' && mt_rand(0, 3) == 2 && $allow_next_vowel )
  3455           $word .= 'u';
  3455 					$word .= 'u';
  3456         else if ( $prev_l == 'a' && mt_rand(0, 3) == 3 && $allow_next_vowel )
  3456 				else if ( $prev_l == 'a' && mt_rand(0, 3) == 3 && $allow_next_vowel )
  3457           $word .= 'i';
  3457 					$word .= 'i';
  3458         else if ( $prev_l == 'a' && mt_rand(0, 10) == 7 && $allow_next_vowel )
  3458 				else if ( $prev_l == 'a' && mt_rand(0, 10) == 7 && $allow_next_vowel )
  3459           $word .= 'o';
  3459 					$word .= 'o';
  3460         else if ( $prev_l == 'a' && mt_rand(0, 7) == 2 && $allow_next_vowel )
  3460 				else if ( $prev_l == 'a' && mt_rand(0, 7) == 2 && $allow_next_vowel )
  3461           $word .= 'u';
  3461 					$word .= 'u';
  3462         else
  3462 				else
  3463         {
  3463 				{
  3464           $allow_next_vowel = true;
  3464 					$allow_next_vowel = true;
  3465           $word .= $consonants{mt_rand(0, (strlen($consonants)-1))};
  3465 					$word .= $consonants{mt_rand(0, (strlen($consonants)-1))};
  3466         }
  3466 				}
  3467       }
  3467 			}
  3468       else if ( $prev == 'consonant' )
  3468 			else if ( $prev == 'consonant' )
  3469       {
  3469 			{
  3470         if ( $prev_l == 'p' && mt_rand(0, 7) == 4 )
  3470 				if ( $prev_l == 'p' && mt_rand(0, 7) == 4 )
  3471           $word .= 't';
  3471 					$word .= 't';
  3472         else if ( $prev_l == 'p' && mt_rand(0, 5) == 1 )
  3472 				else if ( $prev_l == 'p' && mt_rand(0, 5) == 1 )
  3473           $word .= 'h';
  3473 					$word .= 'h';
  3474         else
  3474 				else
  3475           $word .= $vowels{mt_rand(0, (strlen($vowels)-1))};
  3475 					$word .= $vowels{mt_rand(0, (strlen($vowels)-1))};
  3476       }
  3476 			}
  3477       $prev_l = substr($word, -1);
  3477 			$prev_l = substr($word, -1);
  3478       $l = ( mt_rand(0, 1) == 1 ) ? strtoupper($prev_l) : strtolower($prev_l);
  3478 			$l = ( mt_rand(0, 1) == 1 ) ? strtoupper($prev_l) : strtolower($prev_l);
  3479       $word = substr($word, 0, -1) . $l;
  3479 			$word = substr($word, 0, -1) . $l;
  3480       if ( strstr('aeiou', $prev_l) )
  3480 			if ( strstr('aeiou', $prev_l) )
  3481         $prev = 'vowel';
  3481 				$prev = 'vowel';
  3482       else
  3482 			else
  3483         $prev = 'consonant';
  3483 				$prev = 'consonant';
  3484     }
  3484 		}
  3485     return $word;
  3485 		return $word;
  3486   }
  3486 	}
  3487   
  3487 	
  3488   /**
  3488 	/**
  3489    * For the given code ID, returns the correct CAPTCHA code, or false on failure
  3489  	* For the given code ID, returns the correct CAPTCHA code, or false on failure
  3490    * @param string $hash The unique ID assigned to the code
  3490  	* @param string $hash The unique ID assigned to the code
  3491    * @param bool If true, the code is NOT deleted from the database. Use with caution!
  3491  	* @param bool If true, the code is NOT deleted from the database. Use with caution!
  3492    * @return string The correct confirmation code
  3492  	* @return string The correct confirmation code
  3493    */
  3493  	*/
  3494   
  3494 	
  3495   function get_captcha($hash, $nodelete = false)
  3495 	function get_captcha($hash, $nodelete = false)
  3496   {
  3496 	{
  3497     global $db, $session, $paths, $template, $plugins; // Common objects
  3497 		global $db, $session, $paths, $template, $plugins; // Common objects
  3498     
  3498 		
  3499     if ( !preg_match('/^[a-f0-9]{32}([a-z0-9]{8})?$/', $hash) )
  3499 		if ( !preg_match('/^[a-f0-9]{32}([a-z0-9]{8})?$/', $hash) )
  3500     {
  3500 		{
  3501       die("session manager: bad captcha_hash $hash");
  3501 			die("session manager: bad captcha_hash $hash");
  3502       return false;
  3502 			return false;
  3503     }
  3503 		}
  3504     
  3504 		
  3505     // sanity check
  3505 		// sanity check
  3506     if ( !is_valid_ip(@$_SERVER['REMOTE_ADDR']) )
  3506 		if ( !is_valid_ip(@$_SERVER['REMOTE_ADDR']) )
  3507     {
  3507 		{
  3508       die("session manager insanity: bad REMOTE_ADDR or invalid UID");
  3508 			die("session manager insanity: bad REMOTE_ADDR or invalid UID");
  3509       return false;
  3509 			return false;
  3510     }
  3510 		}
  3511     
  3511 		
  3512     $q = $this->sql('SELECT code_id, code FROM ' . table_prefix . "captcha WHERE session_id = '$hash' AND source_ip = '{$_SERVER['REMOTE_ADDR']}';");
  3512 		$q = $this->sql('SELECT code_id, code FROM ' . table_prefix . "captcha WHERE session_id = '$hash' AND source_ip = '{$_SERVER['REMOTE_ADDR']}';");
  3513     if ( $db->numrows() < 1 )
  3513 		if ( $db->numrows() < 1 )
  3514     {
  3514 		{
  3515       return false;
  3515 			return false;
  3516     }
  3516 		}
  3517     
  3517 		
  3518     list($code_id, $code) = $db->fetchrow_num();
  3518 		list($code_id, $code) = $db->fetchrow_num();
  3519     
  3519 		
  3520     $db->free_result();
  3520 		$db->free_result();
  3521     
  3521 		
  3522     // delete it
  3522 		// delete it
  3523     if ( !$nodelete )
  3523 		if ( !$nodelete )
  3524       $this->sql('DELETE FROM ' . table_prefix . "captcha WHERE code_id = $code_id;");
  3524 			$this->sql('DELETE FROM ' . table_prefix . "captcha WHERE code_id = $code_id;");
  3525     
  3525 		
  3526     return $code;
  3526 		return $code;
  3527   }
  3527 	}
  3528   
  3528 	
  3529   /**
  3529 	/**
  3530    * (AS OF 1.0.2: Deprecated. Captcha codes are now killed on first fetch for security.) Deletes all CAPTCHA codes cached in the DB for this user.
  3530  	* (AS OF 1.0.2: Deprecated. Captcha codes are now killed on first fetch for security.) Deletes all CAPTCHA codes cached in the DB for this user.
  3531    */
  3531  	*/
  3532   
  3532 	
  3533   function kill_captcha()
  3533 	function kill_captcha()
  3534   {
  3534 	{
  3535     return true;
  3535 		return true;
  3536   }
  3536 	}
  3537   
  3537 	
  3538   /**
  3538 	/**
  3539    * Generates a random password.
  3539  	* Generates a random password.
  3540    * @param int $length Optional - length of password
  3540  	* @param int $length Optional - length of password
  3541    * @return string
  3541  	* @return string
  3542    */
  3542  	*/
  3543    
  3543  	
  3544   function random_pass($length = 10)
  3544 	function random_pass($length = 10)
  3545   {
  3545 	{
  3546     $valid_chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_+@#%&<>';
  3546 		$valid_chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_+@#%&<>';
  3547     $valid_chars = enano_str_split($valid_chars);
  3547 		$valid_chars = enano_str_split($valid_chars);
  3548     $ret = '';
  3548 		$ret = '';
  3549     for ( $i = 0; $i < $length; $i++ )
  3549 		for ( $i = 0; $i < $length; $i++ )
  3550     {
  3550 		{
  3551       $ret .= $valid_chars[mt_rand(0, count($valid_chars)-1)];
  3551 			$ret .= $valid_chars[mt_rand(0, count($valid_chars)-1)];
  3552     }
  3552 		}
  3553     return $ret;
  3553 		return $ret;
  3554   }
  3554 	}
  3555   
  3555 	
  3556   /**
  3556 	/**
  3557    * Generates some Javascript that calls the AES encryption library. Put this after your </form>.
  3557  	* Generates some Javascript that calls the AES encryption library. Put this after your </form>.
  3558    * @param string The name of the form
  3558  	* @param string The name of the form
  3559    * @param string The name of the password field
  3559  	* @param string The name of the password field
  3560    * @param string The name of the field that switches encryption on or off
  3560  	* @param string The name of the field that switches encryption on or off
  3561    * @param string The name of the field that contains the encryption key
  3561  	* @param string The name of the field that contains the encryption key
  3562    * @param string The name of the field that will contain the encrypted password
  3562  	* @param string The name of the field that will contain the encrypted password
  3563    * @param string The name of the field that handles MD5 challenge data
  3563  	* @param string The name of the field that handles MD5 challenge data
  3564    * @param string The name of the field that tells if the server supports DiffieHellman
  3564  	* @param string The name of the field that tells if the server supports DiffieHellman
  3565    * @param string The name of the field with the DiffieHellman public key
  3565  	* @param string The name of the field with the DiffieHellman public key
  3566    * @param string The name of the field that the client should populate with its public key
  3566  	* @param string The name of the field that the client should populate with its public key
  3567    * @return string
  3567  	* @return string
  3568    */
  3568  	*/
  3569    
  3569  	
  3570   function aes_javascript($form_name, $pw_field, $use_crypt = 'use_crypt', $crypt_key = 'crypt_key', $crypt_data = 'crypt_data', $challenge = 'challenge_data', $dh_supported = 'dh_supported', $dh_pubkey = 'dh_public_key', $dh_client_pubkey = 'dh_client_public_key')
  3570 	function aes_javascript($form_name, $pw_field, $use_crypt = 'use_crypt', $crypt_key = 'crypt_key', $crypt_data = 'crypt_data', $challenge = 'challenge_data', $dh_supported = 'dh_supported', $dh_pubkey = 'dh_public_key', $dh_client_pubkey = 'dh_client_public_key')
  3571   {
  3571 	{
  3572     $code = '
  3572 		$code = '
  3573       <script type="text/javascript">
  3573 			<script type="text/javascript">
  3574           
  3574 					
  3575           function runEncryption(nowhiteout)
  3575 					function runEncryption(nowhiteout)
  3576           {
  3576 					{
  3577             var frm = document.forms.'.$form_name.';
  3577 						var frm = document.forms.'.$form_name.';
  3578             if ( !nowhiteout )
  3578 						if ( !nowhiteout )
  3579               whiteOutForm(frm);
  3579 							whiteOutForm(frm);
  3580             
  3580 						
  3581             load_component(\'crypto\');
  3581 						load_component(\'crypto\');
  3582             var testpassed = ' . ( ( isset($_GET['use_crypt']) && $_GET['use_crypt']=='0') ? 'false; // CRYPTO-AUTH DISABLED ON USER REQUEST // ' : '' ) . '( aes_self_test() && md5_vm_test() );
  3582 						var testpassed = ' . ( ( isset($_GET['use_crypt']) && $_GET['use_crypt']=='0') ? 'false; // CRYPTO-AUTH DISABLED ON USER REQUEST // ' : '' ) . '( aes_self_test() && md5_vm_test() );
  3583             var use_diffiehellman = false;' . "\n";
  3583 						var use_diffiehellman = false;' . "\n";
  3584     if ( $dh_supported && $dh_pubkey )
  3584 		if ( $dh_supported && $dh_pubkey )
  3585     {
  3585 		{
  3586       $code .= <<<EOF
  3586 			$code .= <<<EOF
  3587             if ( frm.$dh_supported.value == 'true' && !is_iPhone )
  3587 						if ( frm.$dh_supported.value == 'true' && !is_iPhone )
  3588               use_diffiehellman = true;
  3588 							use_diffiehellman = true;
  3589 EOF;
  3589 EOF;
  3590     }
  3590 		}
  3591     $code .= '
  3591 		$code .= '
  3592     
  3592 		
  3593             if ( frm[\'' . $dh_supported . '\'] )
  3593 						if ( frm[\'' . $dh_supported . '\'] )
  3594             {
  3594 						{
  3595               frm[\'' . $dh_supported . '\'].value = ( use_diffiehellman ) ? "true" : "false";
  3595 							frm[\'' . $dh_supported . '\'].value = ( use_diffiehellman ) ? "true" : "false";
  3596             }
  3596 						}
  3597             
  3597 						
  3598             if ( frm["' . $pw_field . '_confirm"] )
  3598 						if ( frm["' . $pw_field . '_confirm"] )
  3599             {
  3599 						{
  3600               pass1 = frm.' . $pw_field . '.value;
  3600 							pass1 = frm.' . $pw_field . '.value;
  3601               pass2 = frm.' . $pw_field . '_confirm.value;
  3601 							pass2 = frm.' . $pw_field . '_confirm.value;
  3602               if ( pass1 != pass2 )
  3602 							if ( pass1 != pass2 )
  3603               {
  3603 							{
  3604                 load_component("l10n");
  3604 								load_component("l10n");
  3605                 alert($lang.get("userfuncs_passreset_err_no_match"));
  3605 								alert($lang.get("userfuncs_passreset_err_no_match"));
  3606                 return false;
  3606 								return false;
  3607               }
  3607 							}
  3608               if ( pass1.length < 6 )
  3608 							if ( pass1.length < 6 )
  3609               {
  3609 							{
  3610                 load_component("l10n");
  3610 								load_component("l10n");
  3611                 alert($lang.get("userfuncs_passreset_err_too_short"));
  3611 								alert($lang.get("userfuncs_passreset_err_too_short"));
  3612                 return false;
  3612 								return false;
  3613               }
  3613 							}
  3614               frm.' . $pw_field . '_confirm.value = "";
  3614 							frm.' . $pw_field . '_confirm.value = "";
  3615             }
  3615 						}
  3616             
  3616 						
  3617             if ( testpassed && use_diffiehellman )
  3617 						if ( testpassed && use_diffiehellman )
  3618             {
  3618 						{
  3619               // try to blank out the table to prevent double submits and what have you
  3619 							// try to blank out the table to prevent double submits and what have you
  3620               var el = frm.' . $pw_field . ';
  3620 							var el = frm.' . $pw_field . ';
  3621               while ( el.tagName != "BODY" && el.tagName != "TABLE" )
  3621 							while ( el.tagName != "BODY" && el.tagName != "TABLE" )
  3622               {
  3622 							{
  3623                 el = el.parentNode;
  3623 								el = el.parentNode;
  3624               }
  3624 							}
  3625               /*
  3625 							/*
  3626               if ( el.tagName == "TABLE" )
  3626 							if ( el.tagName == "TABLE" )
  3627               {
  3627 							{
  3628                 whiteOutElement(el);
  3628 								whiteOutElement(el);
  3629               }
  3629 							}
  3630               */
  3630 							*/
  3631               
  3631 							
  3632               frm.'.$use_crypt.'.value = \'yes_dh\';
  3632 							frm.'.$use_crypt.'.value = \'yes_dh\';
  3633               
  3633 							
  3634               // Perform Diffie Hellman stuff
  3634 							// Perform Diffie Hellman stuff
  3635               // console.info("DiffieHellman: started keygen process");
  3635 							// console.info("DiffieHellman: started keygen process");
  3636               var dh_priv = dh_gen_private();
  3636 							var dh_priv = dh_gen_private();
  3637               var dh_pub = dh_gen_public(dh_priv);
  3637 							var dh_pub = dh_gen_public(dh_priv);
  3638               var secret = dh_gen_shared_secret(dh_priv, frm.' . $dh_pubkey . '.value);
  3638 							var secret = dh_gen_shared_secret(dh_priv, frm.' . $dh_pubkey . '.value);
  3639               // console.info("DiffieHellman: finished keygen process");
  3639 							// console.info("DiffieHellman: finished keygen process");
  3640               
  3640 							
  3641               // secret_hash is used to verify that the server guesses the correct secret
  3641 							// secret_hash is used to verify that the server guesses the correct secret
  3642               var secret_hash = hex_sha1(secret);
  3642 							var secret_hash = hex_sha1(secret);
  3643               
  3643 							
  3644               // give the server our values
  3644 							// give the server our values
  3645               frm.' . $crypt_key . '.value = secret_hash;
  3645 							frm.' . $crypt_key . '.value = secret_hash;
  3646               ' . ( $dh_supported ? 'frm.' . $dh_client_pubkey . '.value = dh_pub;' : '' ) . '
  3646 							' . ( $dh_supported ? 'frm.' . $dh_client_pubkey . '.value = dh_pub;' : '' ) . '
  3647               
  3647 							
  3648               // console.info("DiffieHellman: set public values");
  3648 							// console.info("DiffieHellman: set public values");
  3649               
  3649 							
  3650               // crypt_key is the actual AES key
  3650 							// crypt_key is the actual AES key
  3651               var crypt_key = (hex_sha256(secret)).substr(0, (keySizeInBits / 4));
  3651 							var crypt_key = (hex_sha256(secret)).substr(0, (keySizeInBits / 4));
  3652               
  3652 							
  3653               // Perform encryption
  3653 							// Perform encryption
  3654               crypt_key = hexToByteArray(crypt_key);
  3654 							crypt_key = hexToByteArray(crypt_key);
  3655               var pass = frm.'.$pw_field.'.value;
  3655 							var pass = frm.'.$pw_field.'.value;
  3656               pass = stringToByteArray(pass);
  3656 							pass = stringToByteArray(pass);
  3657               var cryptstring = rijndaelEncrypt(pass, crypt_key, \'ECB\');
  3657 							var cryptstring = rijndaelEncrypt(pass, crypt_key, \'ECB\');
  3658               if(!cryptstring)
  3658 							if(!cryptstring)
  3659               {
  3659 							{
  3660                 return false;
  3660 								return false;
  3661               }
  3661 							}
  3662               cryptstring = byteArrayToHex(cryptstring);
  3662 							cryptstring = byteArrayToHex(cryptstring);
  3663               // console.info("DiffieHellman: finished AES");
  3663 							// console.info("DiffieHellman: finished AES");
  3664               frm.'.$crypt_data.'.value = cryptstring;
  3664 							frm.'.$crypt_data.'.value = cryptstring;
  3665               frm.'.$pw_field.'.value = \'\';
  3665 							frm.'.$pw_field.'.value = \'\';
  3666               // console.info("DiffieHellman: ready to submit");
  3666 							// console.info("DiffieHellman: ready to submit");
  3667             }
  3667 						}
  3668             else if ( testpassed && !use_diffiehellman )
  3668 						else if ( testpassed && !use_diffiehellman )
  3669             {
  3669 						{
  3670               frm.'.$use_crypt.'.value = \'yes\';
  3670 							frm.'.$use_crypt.'.value = \'yes\';
  3671               var cryptkey = frm.'.$crypt_key.'.value;
  3671 							var cryptkey = frm.'.$crypt_key.'.value;
  3672               frm.'.$crypt_key.'.value = hex_md5(cryptkey);
  3672 							frm.'.$crypt_key.'.value = hex_md5(cryptkey);
  3673               cryptkey = hexToByteArray(cryptkey);
  3673 							cryptkey = hexToByteArray(cryptkey);
  3674               if(!cryptkey || ( ( typeof cryptkey == \'string\' || typeof cryptkey == \'object\' ) ) && cryptkey.length != keySizeInBits / 8 )
  3674 							if(!cryptkey || ( ( typeof cryptkey == \'string\' || typeof cryptkey == \'object\' ) ) && cryptkey.length != keySizeInBits / 8 )
  3675               {
  3675 							{
  3676                 if ( frm._login ) frm._login.disabled = true;
  3676 								if ( frm._login ) frm._login.disabled = true;
  3677                 len = ( typeof cryptkey == \'string\' || typeof cryptkey == \'object\' ) ? \'\\nLen: \'+cryptkey.length : \'\';
  3677 								len = ( typeof cryptkey == \'string\' || typeof cryptkey == \'object\' ) ? \'\\nLen: \'+cryptkey.length : \'\';
  3678                 alert(\'The key is messed up\\nType: \'+typeof(cryptkey)+len);
  3678 								alert(\'The key is messed up\\nType: \'+typeof(cryptkey)+len);
  3679               }
  3679 							}
  3680               pass = frm.'.$pw_field.'.value;
  3680 							pass = frm.'.$pw_field.'.value;
  3681               chal = frm.'.$challenge.'.value;
  3681 							chal = frm.'.$challenge.'.value;
  3682               challenge = hex_md5(pass + chal) + chal;
  3682 							challenge = hex_md5(pass + chal) + chal;
  3683               frm.'.$challenge.'.value = challenge;
  3683 							frm.'.$challenge.'.value = challenge;
  3684               pass = stringToByteArray(pass);
  3684 							pass = stringToByteArray(pass);
  3685               cryptstring = rijndaelEncrypt(pass, cryptkey, \'ECB\');
  3685 							cryptstring = rijndaelEncrypt(pass, cryptkey, \'ECB\');
  3686               if(!cryptstring)
  3686 							if(!cryptstring)
  3687               {
  3687 							{
  3688                 return false;
  3688 								return false;
  3689               }
  3689 							}
  3690               cryptstring = byteArrayToHex(cryptstring);
  3690 							cryptstring = byteArrayToHex(cryptstring);
  3691               frm.'.$crypt_data.'.value = cryptstring;
  3691 							frm.'.$crypt_data.'.value = cryptstring;
  3692               frm.'.$pw_field.'.value = \'\';
  3692 							frm.'.$pw_field.'.value = \'\';
  3693             }
  3693 						}
  3694           }
  3694 					}
  3695         </script>
  3695 				</script>
  3696         ';
  3696 				';
  3697     return $code;
  3697 		return $code;
  3698   }
  3698 	}
  3699   
  3699 	
  3700   /**
  3700 	/**
  3701    * Generates the HTML form elements required for an encrypted logon experience.
  3701  	* Generates the HTML form elements required for an encrypted logon experience.
  3702    * @return string
  3702  	* @return string
  3703    */
  3703  	*/
  3704   
  3704 	
  3705   function generate_aes_form()
  3705 	function generate_aes_form()
  3706   {
  3706 	{
  3707     $return = '<input type="hidden" name="use_crypt" value="no" />';
  3707 		$return = '<input type="hidden" name="use_crypt" value="no" />';
  3708     $return .= '<input type="hidden" name="crypt_key" value="' . $this->rijndael_genkey() . '" />';
  3708 		$return .= '<input type="hidden" name="crypt_key" value="' . $this->rijndael_genkey() . '" />';
  3709     $return .= '<input type="hidden" name="crypt_data" value="" />';
  3709 		$return .= '<input type="hidden" name="crypt_data" value="" />';
  3710     $return .= '<input type="hidden" name="challenge_data" value="' . $this->dss_rand() . '" />';
  3710 		$return .= '<input type="hidden" name="challenge_data" value="' . $this->dss_rand() . '" />';
  3711     
  3711 		
  3712     require_once(ENANO_ROOT . '/includes/math.php');
  3712 		require_once(ENANO_ROOT . '/includes/math.php');
  3713     require_once(ENANO_ROOT . '/includes/diffiehellman.php');
  3713 		require_once(ENANO_ROOT . '/includes/diffiehellman.php');
  3714     
  3714 		
  3715     global $dh_supported, $_math;
  3715 		global $dh_supported, $_math;
  3716     if ( $dh_supported )
  3716 		if ( $dh_supported )
  3717     {
  3717 		{
  3718       $dh_key_priv = dh_gen_private();
  3718 			$dh_key_priv = dh_gen_private();
  3719       $dh_key_pub = dh_gen_public($dh_key_priv);
  3719 			$dh_key_pub = dh_gen_public($dh_key_priv);
  3720       $dh_key_priv = $_math->str($dh_key_priv);
  3720 			$dh_key_priv = $_math->str($dh_key_priv);
  3721       $dh_key_pub = $_math->str($dh_key_pub);
  3721 			$dh_key_pub = $_math->str($dh_key_pub);
  3722       // store the keys in the DB
  3722 			// store the keys in the DB
  3723       $this->sql('INSERT INTO ' . table_prefix . "diffiehellman( public_key, private_key ) VALUES ( '$dh_key_pub', '$dh_key_priv' );");
  3723 			$this->sql('INSERT INTO ' . table_prefix . "diffiehellman( public_key, private_key ) VALUES ( '$dh_key_pub', '$dh_key_priv' );");
  3724       
  3724 			
  3725       $return .=  "<input type=\"hidden\" name=\"dh_supported\" value=\"true\" />
  3725 			$return .=  "<input type=\"hidden\" name=\"dh_supported\" value=\"true\" />
  3726             <input type=\"hidden\" name=\"dh_public_key\" value=\"$dh_key_pub\" />
  3726 						<input type=\"hidden\" name=\"dh_public_key\" value=\"$dh_key_pub\" />
  3727             <input type=\"hidden\" name=\"dh_client_public_key\" value=\"\" />";
  3727 						<input type=\"hidden\" name=\"dh_client_public_key\" value=\"\" />";
  3728     }
  3728 		}
  3729     else
  3729 		else
  3730     {
  3730 		{
  3731       $return .=  "<input type=\"hidden\" name=\"dh_supported\" value=\"false\" />";
  3731 			$return .=  "<input type=\"hidden\" name=\"dh_supported\" value=\"false\" />";
  3732     }
  3732 		}
  3733     return $return;
  3733 		return $return;
  3734   }
  3734 	}
  3735   
  3735 	
  3736   /**
  3736 	/**
  3737    * If you used all the same form fields as the normal login interface, this will take care of DiffieHellman for you and return the key.
  3737  	* If you used all the same form fields as the normal login interface, this will take care of DiffieHellman for you and return the key.
  3738    * @param string Password field name (defaults to "password")
  3738  	* @param string Password field name (defaults to "password")
  3739    * @return string
  3739  	* @return string
  3740    */
  3740  	*/
  3741   
  3741 	
  3742   function get_aes_post($fieldname = 'password')
  3742 	function get_aes_post($fieldname = 'password')
  3743   {
  3743 	{
  3744     global $db, $session, $paths, $template, $plugins; // Common objects
  3744 		global $db, $session, $paths, $template, $plugins; // Common objects
  3745     
  3745 		
  3746     $aes = AESCrypt::singleton(AES_BITS, AES_BLOCKSIZE);
  3746 		$aes = AESCrypt::singleton(AES_BITS, AES_BLOCKSIZE);
  3747     if ( $_POST['use_crypt'] == 'yes' )
  3747 		if ( $_POST['use_crypt'] == 'yes' )
  3748     {
  3748 		{
  3749       $crypt_key = $this->fetch_public_key($_POST['crypt_key']);
  3749 			$crypt_key = $this->fetch_public_key($_POST['crypt_key']);
  3750       if ( !$crypt_key )
  3750 			if ( !$crypt_key )
  3751       {
  3751 			{
  3752         throw new Exception($lang->get('user_err_key_not_found'));
  3752 				throw new Exception($lang->get('user_err_key_not_found'));
  3753       }
  3753 			}
  3754       $crypt_key = hexdecode($crypt_key);
  3754 			$crypt_key = hexdecode($crypt_key);
  3755       $data = $aes->decrypt($_POST['crypt_data'], $crypt_key, ENC_HEX);
  3755 			$data = $aes->decrypt($_POST['crypt_data'], $crypt_key, ENC_HEX);
  3756     }
  3756 		}
  3757     else if ( $_POST['use_crypt'] == 'yes_dh' )
  3757 		else if ( $_POST['use_crypt'] == 'yes_dh' )
  3758     {
  3758 		{
  3759       require_once(ENANO_ROOT . '/includes/math.php');
  3759 			require_once(ENANO_ROOT . '/includes/math.php');
  3760       require_once(ENANO_ROOT . '/includes/diffiehellman.php');
  3760 			require_once(ENANO_ROOT . '/includes/diffiehellman.php');
  3761       
  3761 			
  3762       global $dh_supported, $_math;
  3762 			global $dh_supported, $_math;
  3763         
  3763 				
  3764       if ( !$dh_supported )
  3764 			if ( !$dh_supported )
  3765       {
  3765 			{
  3766         throw new Exception('Server does not support DiffieHellman, denying request');
  3766 				throw new Exception('Server does not support DiffieHellman, denying request');
  3767       }
  3767 			}
  3768       
  3768 			
  3769       // Fetch private key
  3769 			// Fetch private key
  3770       $dh_public = $_POST['dh_public_key'];
  3770 			$dh_public = $_POST['dh_public_key'];
  3771       if ( !ctype_digit($dh_public) )
  3771 			if ( !ctype_digit($dh_public) )
  3772       {
  3772 			{
  3773         throw new Exception('ERR_DH_KEY_NOT_INTEGER');
  3773 				throw new Exception('ERR_DH_KEY_NOT_INTEGER');
  3774       }
  3774 			}
  3775       $q = $db->sql_query('SELECT private_key, key_id FROM ' . table_prefix . "diffiehellman WHERE public_key = '$dh_public';");
  3775 			$q = $db->sql_query('SELECT private_key, key_id FROM ' . table_prefix . "diffiehellman WHERE public_key = '$dh_public';");
  3776       if ( !$q )
  3776 			if ( !$q )
  3777         $db->die_json();
  3777 				$db->die_json();
  3778       
  3778 			
  3779       if ( $db->numrows() < 1 )
  3779 			if ( $db->numrows() < 1 )
  3780       {
  3780 			{
  3781         throw new Exception('ERR_DH_KEY_NOT_FOUND');
  3781 				throw new Exception('ERR_DH_KEY_NOT_FOUND');
  3782       }
  3782 			}
  3783       
  3783 			
  3784       list($dh_private, $dh_key_id) = $db->fetchrow_num();
  3784 			list($dh_private, $dh_key_id) = $db->fetchrow_num();
  3785       $db->free_result();
  3785 			$db->free_result();
  3786       
  3786 			
  3787       // We have the private key, now delete the key pair, we no longer need it
  3787 			// We have the private key, now delete the key pair, we no longer need it
  3788       $q = $db->sql_query('DELETE FROM ' . table_prefix . "diffiehellman WHERE key_id = $dh_key_id;");
  3788 			$q = $db->sql_query('DELETE FROM ' . table_prefix . "diffiehellman WHERE key_id = $dh_key_id;");
  3789       if ( !$q )
  3789 			if ( !$q )
  3790         $db->die_json();
  3790 				$db->die_json();
  3791       
  3791 			
  3792       // Generate the shared secret
  3792 			// Generate the shared secret
  3793       $dh_secret = dh_gen_shared_secret($dh_private, $_POST['dh_client_public_key']);
  3793 			$dh_secret = dh_gen_shared_secret($dh_private, $_POST['dh_client_public_key']);
  3794       $dh_secret = $_math->str($dh_secret);
  3794 			$dh_secret = $_math->str($dh_secret);
  3795       
  3795 			
  3796       // Did we get all our math right?
  3796 			// Did we get all our math right?
  3797       $dh_secret_check = sha1($dh_secret);
  3797 			$dh_secret_check = sha1($dh_secret);
  3798       $dh_hash = $_POST['crypt_key'];
  3798 			$dh_hash = $_POST['crypt_key'];
  3799       if ( $dh_secret_check !== $dh_hash )
  3799 			if ( $dh_secret_check !== $dh_hash )
  3800       {
  3800 			{
  3801         throw new Exception('ERR_DH_HASH_NO_MATCH');
  3801 				throw new Exception('ERR_DH_HASH_NO_MATCH');
  3802       }
  3802 			}
  3803       
  3803 			
  3804       // All good! Generate the AES key
  3804 			// All good! Generate the AES key
  3805       $aes_key = substr(sha256($dh_secret), 0, ( AES_BITS / 4 ));
  3805 			$aes_key = substr(sha256($dh_secret), 0, ( AES_BITS / 4 ));
  3806       
  3806 			
  3807       // decrypt user info
  3807 			// decrypt user info
  3808       $aes_key = hexdecode($aes_key);
  3808 			$aes_key = hexdecode($aes_key);
  3809       $aes = AESCrypt::singleton(AES_BITS, AES_BLOCKSIZE);
  3809 			$aes = AESCrypt::singleton(AES_BITS, AES_BLOCKSIZE);
  3810       $data = $aes->decrypt($_POST['crypt_data'], $aes_key, ENC_HEX);
  3810 			$data = $aes->decrypt($_POST['crypt_data'], $aes_key, ENC_HEX);
  3811     }
  3811 		}
  3812     else
  3812 		else
  3813     {
  3813 		{
  3814       $data = $_POST[$fieldname];
  3814 			$data = $_POST[$fieldname];
  3815     }
  3815 		}
  3816     return $data;
  3816 		return $data;
  3817   }
  3817 	}
  3818   
  3818 	
  3819   /**
  3819 	/**
  3820    * Backend code for the JSON login interface. Basically a frontend to the session API that takes all parameters in one huge array.
  3820  	* Backend code for the JSON login interface. Basically a frontend to the session API that takes all parameters in one huge array.
  3821    * @param array LoginAPI request
  3821  	* @param array LoginAPI request
  3822    * @return array LoginAPI response
  3822  	* @return array LoginAPI response
  3823    */
  3823  	*/
  3824   
  3824 	
  3825   function process_login_request($req, $_dbgtmp = false)
  3825 	function process_login_request($req, $_dbgtmp = false)
  3826   {
  3826 	{
  3827     global $db, $session, $paths, $template, $plugins; // Common objects
  3827 		global $db, $session, $paths, $template, $plugins; // Common objects
  3828     
  3828 		
  3829     // Setup EnanoMath and Diffie-Hellman
  3829 		// Setup EnanoMath and Diffie-Hellman
  3830     global $dh_supported;
  3830 		global $dh_supported;
  3831     if ( !function_exists('dh_gen_private') )
  3831 		if ( !function_exists('dh_gen_private') )
  3832     {
  3832 		{
  3833       require_once(ENANO_ROOT.'/includes/math.php');
  3833 			require_once(ENANO_ROOT.'/includes/math.php');
  3834       
  3834 			
  3835       $dh_supported = true;
  3835 			$dh_supported = true;
  3836       try
  3836 			try
  3837       {
  3837 			{
  3838         require_once(ENANO_ROOT . '/includes/diffiehellman.php');
  3838 				require_once(ENANO_ROOT . '/includes/diffiehellman.php');
  3839       }
  3839 			}
  3840       catch ( Exception $e )
  3840 			catch ( Exception $e )
  3841       {
  3841 			{
  3842         $dh_supported = false;
  3842 				$dh_supported = false;
  3843       }
  3843 			}
  3844     }
  3844 		}
  3845     global $_math;
  3845 		global $_math;
  3846     
  3846 		
  3847     // Check for the mode
  3847 		// Check for the mode
  3848     if ( !isset($req['mode']) )
  3848 		if ( !isset($req['mode']) )
  3849     {
  3849 		{
  3850       return $this->get_login_response('api_error', 'ERR_JSON_NO_MODE');
  3850 			return $this->get_login_response('api_error', 'ERR_JSON_NO_MODE');
  3851     }
  3851 		}
  3852     
  3852 		
  3853     // Main processing switch
  3853 		// Main processing switch
  3854     switch ( $req['mode'] )
  3854 		switch ( $req['mode'] )
  3855     {
  3855 		{
  3856       default:
  3856 			default:
  3857         return $this->get_login_response('api_error', 'ERR_JSON_INVALID_MODE');
  3857 				return $this->get_login_response('api_error', 'ERR_JSON_INVALID_MODE');
  3858         break;
  3858 				break;
  3859       case 'getkey':
  3859 			case 'getkey':
  3860         
  3860 				
  3861         $this->start();
  3861 				$this->start();
  3862         
  3862 				
  3863         return $this->get_login_response('initial');
  3863 				return $this->get_login_response('initial');
  3864         break;
  3864 				break;
  3865       case 'login_dh':
  3865 			case 'login_dh':
  3866         // User is requesting a login and has sent Diffie-Hellman data.
  3866 				// User is requesting a login and has sent Diffie-Hellman data.
  3867         
  3867 				
  3868         //
  3868 				//
  3869         // KEY RECONSTRUCTION
  3869 				// KEY RECONSTRUCTION
  3870         //
  3870 				//
  3871         
  3871 				
  3872         $userinfo_crypt = $req['userinfo'];
  3872 				$userinfo_crypt = $req['userinfo'];
  3873         $dh_public = $req['dh_public_key'];
  3873 				$dh_public = $req['dh_public_key'];
  3874         $dh_hash = $req['dh_secret_hash'];
  3874 				$dh_hash = $req['dh_secret_hash'];
  3875         
  3875 				
  3876         // Check the key
  3876 				// Check the key
  3877         if ( !ctype_digit($dh_public) || !ctype_digit($req['dh_client_key']) )
  3877 				if ( !ctype_digit($dh_public) || !ctype_digit($req['dh_client_key']) )
  3878         {
  3878 				{
  3879           return $this->get_login_response('api_error', 'ERR_DH_KEY_NOT_NUMERIC');
  3879 					return $this->get_login_response('api_error', 'ERR_DH_KEY_NOT_NUMERIC');
  3880         }
  3880 				}
  3881         
  3881 				
  3882         // Fetch private key
  3882 				// Fetch private key
  3883         $q = $db->sql_query('SELECT private_key, key_id FROM ' . table_prefix . "diffiehellman WHERE public_key = '$dh_public';");
  3883 				$q = $db->sql_query('SELECT private_key, key_id FROM ' . table_prefix . "diffiehellman WHERE public_key = '$dh_public';");
  3884         if ( !$q )
  3884 				if ( !$q )
  3885           $db->die_json();
  3885 					$db->die_json();
  3886         
  3886 				
  3887         if ( $db->numrows() < 1 )
  3887 				if ( $db->numrows() < 1 )
  3888         {
  3888 				{
  3889           return $this->get_login_response('api_error', 'ERR_DH_KEY_NOT_FOUND');
  3889 					return $this->get_login_response('api_error', 'ERR_DH_KEY_NOT_FOUND');
  3890         }
  3890 				}
  3891         
  3891 				
  3892         list($dh_private, $dh_key_id) = $db->fetchrow_num();
  3892 				list($dh_private, $dh_key_id) = $db->fetchrow_num();
  3893         $db->free_result();
  3893 				$db->free_result();
  3894         
  3894 				
  3895         // We have the private key, now delete the key pair, we no longer need it
  3895 				// We have the private key, now delete the key pair, we no longer need it
  3896         $q = $db->sql_query('DELETE FROM ' . table_prefix . "diffiehellman WHERE key_id = $dh_key_id;");
  3896 				$q = $db->sql_query('DELETE FROM ' . table_prefix . "diffiehellman WHERE key_id = $dh_key_id;");
  3897         if ( !$q )
  3897 				if ( !$q )
  3898           $db->die_json();
  3898 					$db->die_json();
  3899         
  3899 				
  3900         // Generate the shared secret
  3900 				// Generate the shared secret
  3901         $dh_secret = dh_gen_shared_secret($dh_private, $req['dh_client_key']);
  3901 				$dh_secret = dh_gen_shared_secret($dh_private, $req['dh_client_key']);
  3902         $dh_secret = $_math->str($dh_secret);
  3902 				$dh_secret = $_math->str($dh_secret);
  3903         
  3903 				
  3904         // Did we get all our math right?
  3904 				// Did we get all our math right?
  3905         $dh_secret_check = sha1($dh_secret);
  3905 				$dh_secret_check = sha1($dh_secret);
  3906         if ( $dh_secret_check !== $dh_hash )
  3906 				if ( $dh_secret_check !== $dh_hash )
  3907         {
  3907 				{
  3908           return $this->get_login_response('api_error', 'ERR_DH_HASH_NO_MATCH');
  3908 					return $this->get_login_response('api_error', 'ERR_DH_HASH_NO_MATCH');
  3909         }
  3909 				}
  3910         
  3910 				
  3911         // All good! Generate the AES key
  3911 				// All good! Generate the AES key
  3912         $aes_key = substr(sha256($dh_secret), 0, ( AES_BITS / 4 ));
  3912 				$aes_key = substr(sha256($dh_secret), 0, ( AES_BITS / 4 ));
  3913       case 'login_aes':
  3913 			case 'login_aes':
  3914         if ( $req['mode'] == 'login_aes' )
  3914 				if ( $req['mode'] == 'login_aes' )
  3915         {
  3915 				{
  3916           // login_aes-specific code
  3916 					// login_aes-specific code
  3917           $aes_key = $this->fetch_public_key($req['key_aes']);
  3917 					$aes_key = $this->fetch_public_key($req['key_aes']);
  3918           if ( !$aes_key )
  3918 					if ( !$aes_key )
  3919           {
  3919 					{
  3920             return $this->get_login_response('api_error', 'ERR_AES_LOOKUP_FAILED');
  3920 						return $this->get_login_response('api_error', 'ERR_AES_LOOKUP_FAILED');
  3921           }
  3921 					}
  3922           $userinfo_crypt = $req['userinfo'];
  3922 					$userinfo_crypt = $req['userinfo'];
  3923         }
  3923 				}
  3924         // shared between the two systems from here on out
  3924 				// shared between the two systems from here on out
  3925         
  3925 				
  3926         // decrypt user info
  3926 				// decrypt user info
  3927         $aes_key = hexdecode($aes_key);
  3927 				$aes_key = hexdecode($aes_key);
  3928         $aes = AESCrypt::singleton(AES_BITS, AES_BLOCKSIZE);
  3928 				$aes = AESCrypt::singleton(AES_BITS, AES_BLOCKSIZE);
  3929         // using "true" here disables caching of the decrypted login info (which includes the password)
  3929 				// using "true" here disables caching of the decrypted login info (which includes the password)
  3930         $userinfo_json = $aes->decrypt($userinfo_crypt, $aes_key, ENC_HEX, true);
  3930 				$userinfo_json = $aes->decrypt($userinfo_crypt, $aes_key, ENC_HEX, true);
  3931         if ( !$userinfo_json )
  3931 				if ( !$userinfo_json )
  3932         {
  3932 				{
  3933           return $this->get_login_response('api_error', 'ERR_AES_DECRYPT_FAILED');
  3933 					return $this->get_login_response('api_error', 'ERR_AES_DECRYPT_FAILED');
  3934         }
  3934 				}
  3935         // de-JSON user info
  3935 				// de-JSON user info
  3936         try
  3936 				try
  3937         {
  3937 				{
  3938           $userinfo = enano_json_decode($userinfo_json);
  3938 					$userinfo = enano_json_decode($userinfo_json);
  3939         }
  3939 				}
  3940         catch ( Exception $e )
  3940 				catch ( Exception $e )
  3941         {
  3941 				{
  3942           return $this->get_login_response('api_error', 'ERR_USERINFO_DECODE_FAILED');
  3942 					return $this->get_login_response('api_error', 'ERR_USERINFO_DECODE_FAILED');
  3943         }
  3943 				}
  3944         
  3944 				
  3945       case 'login_pt':
  3945 			case 'login_pt':
  3946         // plaintext login
  3946 				// plaintext login
  3947         if ( $req['mode'] == 'login_pt' )
  3947 				if ( $req['mode'] == 'login_pt' )
  3948         {
  3948 				{
  3949           $userinfo = isset($req['userinfo']) ? $req['userinfo'] : array();
  3949 					$userinfo = isset($req['userinfo']) ? $req['userinfo'] : array();
  3950         }
  3950 				}
  3951         
  3951 				
  3952         if ( !isset($userinfo['username']) || !isset($userinfo['password']) )
  3952 				if ( !isset($userinfo['username']) || !isset($userinfo['password']) )
  3953         {
  3953 				{
  3954           return $this->get_login_response('api_error', 'ERR_USERINFO_MISSING_VALUES');
  3954 					return $this->get_login_response('api_error', 'ERR_USERINFO_MISSING_VALUES');
  3955         }
  3955 				}
  3956         
  3956 				
  3957         $username =& $userinfo['username'];
  3957 				$username =& $userinfo['username'];
  3958         $password =& $userinfo['password'];
  3958 				$password =& $userinfo['password'];
  3959         
  3959 				
  3960         // locked out? check captcha
  3960 				// locked out? check captcha
  3961         $lockout_data = $this->get_lockout_info();
  3961 				$lockout_data = $this->get_lockout_info();
  3962         if ( $lockout_data['policy'] == 'captcha' && $lockout_data['active'] )
  3962 				if ( $lockout_data['policy'] == 'captcha' && $lockout_data['active'] )
  3963         {
  3963 				{
  3964           // policy is captcha -- check if it's correct, and if so, bypass lockout check
  3964 					// policy is captcha -- check if it's correct, and if so, bypass lockout check
  3965           $real_code = $this->get_captcha($req['captcha_hash']);
  3965 					$real_code = $this->get_captcha($req['captcha_hash']);
  3966           if ( strtolower($real_code) !== strtolower($req['captcha_code']) )
  3966 					if ( strtolower($real_code) !== strtolower($req['captcha_code']) )
  3967           {
  3967 					{
  3968             // captcha is bad
  3968 						// captcha is bad
  3969             return $this->get_login_response('login_failure', 'lockout_bad_captcha');
  3969 						return $this->get_login_response('login_failure', 'lockout_bad_captcha');
  3970           }
  3970 					}
  3971         }
  3971 				}
  3972         else if ( $lockout_data['policy'] == 'lockout' && $lockout_data['active'] )
  3972 				else if ( $lockout_data['policy'] == 'lockout' && $lockout_data['active'] )
  3973         {
  3973 				{
  3974           // we're fully locked out
  3974 					// we're fully locked out
  3975           return $this->get_login_response('login_failure', 'lockout_request_denied');
  3975 					return $this->get_login_response('login_failure', 'lockout_request_denied');
  3976         }
  3976 				}
  3977         
  3977 				
  3978         // At this point if any extra info was injected into the login data packet, we need to let plugins process it
  3978 				// At this point if any extra info was injected into the login data packet, we need to let plugins process it
  3979         /**
  3979 				/**
  3980          * Called upon processing an incoming login request. If you added anything to the userinfo object during the jshook
  3980  				* Called upon processing an incoming login request. If you added anything to the userinfo object during the jshook
  3981          * login_build_userinfo, that will be in the $userinfo array here. Expected return values are: true if your plugin has
  3981  				* login_build_userinfo, that will be in the $userinfo array here. Expected return values are: true if your plugin has
  3982          * not only succeeded but ALSO issued a session key (bypass the whole Enano builtin login process) and an associative array
  3982  				* not only succeeded but ALSO issued a session key (bypass the whole Enano builtin login process) and an associative array
  3983          * with "mode" set to "error" and an error string in "error" to send an error back to the client. Any return value other
  3983  				* with "mode" set to "error" and an error string in "error" to send an error back to the client. Any return value other
  3984          * than these will be treated as a pass-through, and the user's password will be validated through Enano's standard process.
  3984  				* than these will be treated as a pass-through, and the user's password will be validated through Enano's standard process.
  3985          * @hook login_process_userdata_json
  3985  				* @hook login_process_userdata_json
  3986          */
  3986  				*/
  3987         
  3987 				
  3988         $code = $plugins->setHook('login_process_userdata_json', true);
  3988 				$code = $plugins->setHook('login_process_userdata_json', true);
  3989         
  3989 				
  3990         foreach ( $code as $cmd )
  3990 				foreach ( $code as $cmd )
  3991         {
  3991 				{
  3992           $result = eval($cmd);
  3992 					$result = eval($cmd);
  3993           if ( $result === true )
  3993 					if ( $result === true )
  3994           {
  3994 					{
  3995             return $this->get_login_response('login_success', false, array(
  3995 						return $this->get_login_response('login_success', false, array(
  3996                 'key' => $this->sid_super,
  3996 								'key' => $this->sid_super,
  3997                 'user_id' => $this->user_id,
  3997 								'user_id' => $this->user_id,
  3998                 'user_level' => $this->user_level,
  3998 								'user_level' => $this->user_level,
  3999                 'reset' => false
  3999 								'reset' => false
  4000               ));
  4000 							));
  4001           }
  4001 					}
  4002           else if ( is_array($result) )
  4002 					else if ( is_array($result) )
  4003           {
  4003 					{
  4004             if ( isset($result['mode']) && $result['mode'] === 'error' && isset($result['error']) )
  4004 						if ( isset($result['mode']) && $result['mode'] === 'error' && isset($result['error']) )
  4005             {
  4005 						{
  4006               // Pass back any additional information from the error response
  4006 							// Pass back any additional information from the error response
  4007               $append = $result;
  4007 							$append = $result;
  4008               unset($append['mode'], $append['error']);
  4008 							unset($append['mode'], $append['error']);
  4009               $append['from_plugin'] = true;
  4009 							$append['from_plugin'] = true;
  4010               
  4010 							
  4011               return $this->get_login_response('login_failure', $result['error'], $append);
  4011 							return $this->get_login_response('login_failure', $result['error'], $append);
  4012             }
  4012 						}
  4013           }
  4013 					}
  4014         }
  4014 				}
  4015         
  4015 				
  4016         // attempt the login
  4016 				// attempt the login
  4017         $login_result = $this->login_without_crypto($username, $password, false, intval($req['level']), @$req['remember']);
  4017 				$login_result = $this->login_without_crypto($username, $password, false, intval($req['level']), @$req['remember']);
  4018         
  4018 				
  4019         if ( $login_result['success'] )
  4019 				if ( $login_result['success'] )
  4020         {
  4020 				{
  4021           return $this->get_login_response('login_success', false, array(
  4021 					return $this->get_login_response('login_success', false, array(
  4022               'key' => $this->sid_super,
  4022 							'key' => $this->sid_super,
  4023               'user_id' => $this->user_id,
  4023 							'user_id' => $this->user_id,
  4024               'user_level' => $this->user_level,
  4024 							'user_level' => $this->user_level,
  4025             ));
  4025 						));
  4026         }
  4026 				}
  4027         else if ( !$login_result['success'] && $login_result['error'] === 'valid_reset' )
  4027 				else if ( !$login_result['success'] && $login_result['error'] === 'valid_reset' )
  4028         {
  4028 				{
  4029           return $this->get_login_response('reset_pass_used', false, array(
  4029 					return $this->get_login_response('reset_pass_used', false, array(
  4030               'redirect_url' => $login_result['redirect_url']
  4030 							'redirect_url' => $login_result['redirect_url']
  4031             ));
  4031 						));
  4032         }
  4032 				}
  4033         else
  4033 				else
  4034         {
  4034 				{
  4035           return $this->get_login_response('login_failure', 'invalid_credentials');
  4035 					return $this->get_login_response('login_failure', 'invalid_credentials');
  4036         }
  4036 				}
  4037         
  4037 				
  4038         break;
  4038 				break;
  4039       case 'clean_key':
  4039 			case 'clean_key':
  4040         // Clean out a key, since it won't be used.
  4040 				// Clean out a key, since it won't be used.
  4041         // This is called when the user clicks Cancel in the AJAX login interface.
  4041 				// This is called when the user clicks Cancel in the AJAX login interface.
  4042         if ( !empty($req['key_aes']) )
  4042 				if ( !empty($req['key_aes']) )
  4043         {
  4043 				{
  4044           $this->fetch_public_key($req['key_aes']);
  4044 					$this->fetch_public_key($req['key_aes']);
  4045         }
  4045 				}
  4046         if ( !empty($req['key_dh']) )
  4046 				if ( !empty($req['key_dh']) )
  4047         {
  4047 				{
  4048           $pk = $db->escape($req['key_dh']);
  4048 					$pk = $db->escape($req['key_dh']);
  4049           $q = $db->sql_query('DELETE FROM ' . table_prefix . "diffiehellman WHERE public_key = '$pk';");
  4049 					$q = $db->sql_query('DELETE FROM ' . table_prefix . "diffiehellman WHERE public_key = '$pk';");
  4050           if ( !$q )
  4050 					if ( !$q )
  4051             $db->die_json();
  4051 						$db->die_json();
  4052         }
  4052 				}
  4053         return array(
  4053 				return array(
  4054             'mode' => 'noop'
  4054 						'mode' => 'noop'
  4055           );
  4055 					);
  4056         break;
  4056 				break;
  4057       case 'respond_password_reset':
  4057 			case 'respond_password_reset':
  4058         die(enano_json_encode(array(
  4058 				die(enano_json_encode(array(
  4059             'mode' => 'login_success_reset',
  4059 						'mode' => 'login_success_reset',
  4060             'user_id' => $req['user_id'],
  4060 						'user_id' => $req['user_id'],
  4061             'temp_password' => $req['temp_password'],
  4061 						'temp_password' => $req['temp_password'],
  4062             'respawn_info' => $this->process_login_request(array('mode' => 'getkey'))
  4062 						'respawn_info' => $this->process_login_request(array('mode' => 'getkey'))
  4063           )));
  4063 					)));
  4064         break;
  4064 				break;
  4065       case 'logout':
  4065 			case 'logout':
  4066         if ( !$this->started )
  4066 				if ( !$this->started )
  4067           $this->start();
  4067 					$this->start();
  4068         if ( !isset($req['csrf_token']) )
  4068 				if ( !isset($req['csrf_token']) )
  4069           return array(
  4069 					return array(
  4070               'mode' => 'error',
  4070 							'mode' => 'error',
  4071               'error' => 'Invalid CSRF token'
  4071 							'error' => 'Invalid CSRF token'
  4072             );
  4072 						);
  4073         
  4073 				
  4074         if ( $req['csrf_token'] !== $this->csrf_token )
  4074 				if ( $req['csrf_token'] !== $this->csrf_token )
  4075           return array(
  4075 					return array(
  4076               'mode' => 'error',
  4076 							'mode' => 'error',
  4077               'error' => 'Invalid CSRF token'
  4077 							'error' => 'Invalid CSRF token'
  4078             );
  4078 						);
  4079         $level = isset($req['level']) && is_int($req['level']) ? $req['level'] : USER_LEVEL_MEMBER;
  4079 				$level = isset($req['level']) && is_int($req['level']) ? $req['level'] : USER_LEVEL_MEMBER;
  4080         if ( ($result = $this->logout($level)) === 'success' )
  4080 				if ( ($result = $this->logout($level)) === 'success' )
  4081         {
  4081 				{
  4082           return array(
  4082 					return array(
  4083             'mode' => 'logout_success'
  4083 						'mode' => 'logout_success'
  4084           );
  4084 					);
  4085         }
  4085 				}
  4086         else
  4086 				else
  4087         {
  4087 				{
  4088           return array(
  4088 					return array(
  4089             'mode' => 'error',
  4089 						'mode' => 'error',
  4090             'error' => $result
  4090 						'error' => $result
  4091           );
  4091 					);
  4092         }
  4092 				}
  4093         break;
  4093 				break;
  4094     }
  4094 		}
  4095     
  4095 		
  4096   }
  4096 	}
  4097   
  4097 	
  4098   /**
  4098 	/**
  4099    * Generate a packet to send to the client for logins.
  4099  	* Generate a packet to send to the client for logins.
  4100    * @param string mode
  4100  	* @param string mode
  4101    * @param array 
  4101  	* @param array 
  4102    * @return array
  4102  	* @return array
  4103    */
  4103  	*/
  4104   
  4104 	
  4105   function get_login_response($mode, $error = false, $base = array())
  4105 	function get_login_response($mode, $error = false, $base = array())
  4106   {
  4106 	{
  4107     $this->start();
  4107 		$this->start();
  4108     
  4108 		
  4109     // init
  4109 		// init
  4110     $response = $base;
  4110 		$response = $base;
  4111     // modules in the packet
  4111 		// modules in the packet
  4112     $response['mode'] = $mode;
  4112 		$response['mode'] = $mode;
  4113     $response['error'] = $error;
  4113 		$response['error'] = $error;
  4114     $response['crypto'] = $mode !== 'login_success' ? $this->get_login_crypto_packet() : false;
  4114 		$response['crypto'] = $mode !== 'login_success' ? $this->get_login_crypto_packet() : false;
  4115     $response['lockout'] = $mode !== 'login_success' ? $this->get_lockout_info() : false;
  4115 		$response['lockout'] = $mode !== 'login_success' ? $this->get_lockout_info() : false;
  4116     $response['extended_time'] = intval(getConfig('session_remember_time', '30'));
  4116 		$response['extended_time'] = intval(getConfig('session_remember_time', '30'));
  4117     $response['username'] = $this->user_logged_in ? $this->username : false;
  4117 		$response['username'] = $this->user_logged_in ? $this->username : false;
  4118     return $response;
  4118 		return $response;
  4119   }
  4119 	}
  4120   
  4120 	
  4121   /**
  4121 	/**
  4122    * Get a packet of crypto flags for login.
  4122  	* Get a packet of crypto flags for login.
  4123    * @return array
  4123  	* @return array
  4124    */
  4124  	*/
  4125   
  4125 	
  4126   function get_login_crypto_packet()
  4126 	function get_login_crypto_packet()
  4127   {
  4127 	{
  4128     global $dh_supported, $_math;
  4128 		global $dh_supported, $_math;
  4129     
  4129 		
  4130     $response = array();
  4130 		$response = array();
  4131     
  4131 		
  4132     $response['dh_enable'] = $dh_supported;
  4132 		$response['dh_enable'] = $dh_supported;
  4133     $response['aes_key'] = $this->rijndael_genkey();
  4133 		$response['aes_key'] = $this->rijndael_genkey();
  4134     
  4134 		
  4135     // Can we do Diffie-Hellman? If so, generate and stash a public/private key pair.
  4135 		// Can we do Diffie-Hellman? If so, generate and stash a public/private key pair.
  4136     if ( $dh_supported )
  4136 		if ( $dh_supported )
  4137     {
  4137 		{
  4138       $dh_key_priv = dh_gen_private();
  4138 			$dh_key_priv = dh_gen_private();
  4139       $dh_key_pub = dh_gen_public($dh_key_priv);
  4139 			$dh_key_pub = dh_gen_public($dh_key_priv);
  4140       $dh_key_priv = $_math->str($dh_key_priv);
  4140 			$dh_key_priv = $_math->str($dh_key_priv);
  4141       $dh_key_pub = $_math->str($dh_key_pub);
  4141 			$dh_key_pub = $_math->str($dh_key_pub);
  4142       $response['dh_public_key'] = $dh_key_pub;
  4142 			$response['dh_public_key'] = $dh_key_pub;
  4143       // store the keys in the DB
  4143 			// store the keys in the DB
  4144       $this->sql('INSERT INTO ' . table_prefix . "diffiehellman( public_key, private_key ) VALUES ( '$dh_key_pub', '$dh_key_priv' );");
  4144 			$this->sql('INSERT INTO ' . table_prefix . "diffiehellman( public_key, private_key ) VALUES ( '$dh_key_pub', '$dh_key_priv' );");
  4145     }
  4145 		}
  4146     
  4146 		
  4147     return $response;
  4147 		return $response;
  4148   }
  4148 	}
  4149   
  4149 	
  4150 }
  4150 }
  4151 
  4151 
  4152 /**
  4152 /**
  4153  * Class used to fetch permissions for a specific page. Used internally by SessionManager.
  4153  * Class used to fetch permissions for a specific page. Used internally by SessionManager.
  4154  * @package Enano
  4154  * @package Enano
  4156  * @license http://www.gnu.org/copyleft/gpl.html
  4156  * @license http://www.gnu.org/copyleft/gpl.html
  4157  * @access private
  4157  * @access private
  4158  */
  4158  */
  4159  
  4159  
  4160 class Session_ACLPageInfo {
  4160 class Session_ACLPageInfo {
  4161   
  4161 	
  4162   /**
  4162 	/**
  4163    * The page ID of this ACL info package
  4163  	* The page ID of this ACL info package
  4164    * @var string
  4164  	* @var string
  4165    */
  4165  	*/
  4166    
  4166  	
  4167   var $page_id;
  4167 	var $page_id;
  4168   
  4168 	
  4169   /**
  4169 	/**
  4170    * The namespace of the page being checked
  4170  	* The namespace of the page being checked
  4171    * @var string
  4171  	* @var string
  4172    */
  4172  	*/
  4173    
  4173  	
  4174   var $namespace;
  4174 	var $namespace;
  4175   
  4175 	
  4176   /**
  4176 	/**
  4177    * Our list of permission types.
  4177  	* Our list of permission types.
  4178    * @access private
  4178  	* @access private
  4179    * @var array
  4179  	* @var array
  4180    */
  4180  	*/
  4181    
  4181  	
  4182   var $acl_types = Array();
  4182 	var $acl_types = Array();
  4183   
  4183 	
  4184   /**
  4184 	/**
  4185    * The list of descriptions for the permission types
  4185  	* The list of descriptions for the permission types
  4186    * @var array
  4186  	* @var array
  4187    */
  4187  	*/
  4188    
  4188  	
  4189   var $acl_descs = Array();
  4189 	var $acl_descs = Array();
  4190   
  4190 	
  4191   /**
  4191 	/**
  4192    * A list of dependencies for ACL types.
  4192  	* A list of dependencies for ACL types.
  4193    * @var array
  4193  	* @var array
  4194    */
  4194  	*/
  4195    
  4195  	
  4196   var $acl_deps = Array();
  4196 	var $acl_deps = Array();
  4197   
  4197 	
  4198   /**
  4198 	/**
  4199    * Our tell-all list of permissions. Do not even try to change this.
  4199  	* Our tell-all list of permissions. Do not even try to change this.
  4200    * @access private
  4200  	* @access private
  4201    * @var array
  4201  	* @var array
  4202    */
  4202  	*/
  4203    
  4203  	
  4204   var $perms = Array();
  4204 	var $perms = Array();
  4205   
  4205 	
  4206   /**
  4206 	/**
  4207    * Array to track which default permissions are being used
  4207  	* Array to track which default permissions are being used
  4208    * @var array
  4208  	* @var array
  4209    * @access private
  4209  	* @access private
  4210    */
  4210  	*/
  4211    
  4211  	
  4212   var $acl_defaults_used = Array();
  4212 	var $acl_defaults_used = Array();
  4213   
  4213 	
  4214   /**
  4214 	/**
  4215    * Tracks whether Wiki Mode is on for the page we're operating on.
  4215  	* Tracks whether Wiki Mode is on for the page we're operating on.
  4216    * @var bool
  4216  	* @var bool
  4217    */
  4217  	*/
  4218   
  4218 	
  4219   var $wiki_mode = false;
  4219 	var $wiki_mode = false;
  4220   
  4220 	
  4221   /**
  4221 	/**
  4222    * Tracks where permissions were calculated using the ACL_INHERIT_* constants. Layout:
  4222  	* Tracks where permissions were calculated using the ACL_INHERIT_* constants. Layout:
  4223    * array(
  4223  	* array(
  4224    *   [permission_name] => array(
  4224  	*   [permission_name] => array(
  4225    *       [src] => ACL_INHERIT_*
  4225  	*       [src] => ACL_INHERIT_*
  4226    *       [rule_id] => integer
  4226  	*       [rule_id] => integer
  4227    *     ),
  4227  	*     ),
  4228    *   ...
  4228  	*   ...
  4229    * )
  4229  	* )
  4230    *
  4230  	*
  4231    * @var array
  4231  	* @var array
  4232    */
  4232  	*/
  4233   
  4233 	
  4234   var $perm_resolve_table = array();
  4234 	var $perm_resolve_table = array();
  4235   
  4235 	
  4236   #
  4236 	#
  4237   # USER PARAMETERS
  4237 	# USER PARAMETERS
  4238   #
  4238 	#
  4239   
  4239 	
  4240   /**
  4240 	/**
  4241    * User ID
  4241  	* User ID
  4242    * @var int
  4242  	* @var int
  4243    */
  4243  	*/
  4244   
  4244 	
  4245   var $user_id = 1;
  4245 	var $user_id = 1;
  4246   
  4246 	
  4247   /**
  4247 	/**
  4248    * Group membership associative array (group_id => group_name)
  4248  	* Group membership associative array (group_id => group_name)
  4249    * @var array
  4249  	* @var array
  4250    */
  4250  	*/
  4251   
  4251 	
  4252   var $groups = array();
  4252 	var $groups = array();
  4253   
  4253 	
  4254   /**
  4254 	/**
  4255    * Constructor.
  4255  	* Constructor.
  4256    * @param string $page_id The ID of the page to check
  4256  	* @param string $page_id The ID of the page to check
  4257    * @param string $namespace The namespace of the page to check.
  4257  	* @param string $namespace The namespace of the page to check.
  4258    * @param array $acl_types List of ACL types
  4258  	* @param array $acl_types List of ACL types
  4259    * @param array $acl_descs List of human-readable descriptions for permissions (associative)
  4259  	* @param array $acl_descs List of human-readable descriptions for permissions (associative)
  4260    * @param array $acl_deps List of dependencies for permissions. For example, viewing history/diffs depends on the ability to read the page.
  4260  	* @param array $acl_deps List of dependencies for permissions. For example, viewing history/diffs depends on the ability to read the page.
  4261    * @param array $base What to start with - this is an attempt to reduce the number of SQL queries.
  4261  	* @param array $base What to start with - this is an attempt to reduce the number of SQL queries.
  4262    * @param int|string $user_id_or_name Username or ID to search for, defaults to current user
  4262  	* @param int|string $user_id_or_name Username or ID to search for, defaults to current user
  4263    * @param array $resolve_table Debugging info for tracking where rules came from, defaults to a blank array.
  4263  	* @param array $resolve_table Debugging info for tracking where rules came from, defaults to a blank array.
  4264    */
  4264  	*/
  4265    
  4265  	
  4266   function __construct($page_id, $namespace, $acl_types, $acl_descs, $acl_deps, $base, $user_id = null, $groups = null, $resolve_table = array())
  4266 	function __construct($page_id, $namespace, $acl_types, $acl_descs, $acl_deps, $base, $user_id = null, $groups = null, $resolve_table = array())
  4267   {
  4267 	{
  4268     global $db, $session, $paths, $template, $plugins; // Common objects
  4268 		global $db, $session, $paths, $template, $plugins; // Common objects
  4269     
  4269 		
  4270     // hack
  4270 		// hack
  4271     if ( isset($base['__resolve_table']) )
  4271 		if ( isset($base['__resolve_table']) )
  4272     {
  4272 		{
  4273       unset($base['__resolve_table']);
  4273 			unset($base['__resolve_table']);
  4274     }
  4274 		}
  4275     
  4275 		
  4276     foreach ( $acl_types as $perm_type => $_ )
  4276 		foreach ( $acl_types as $perm_type => $_ )
  4277     {
  4277 		{
  4278       if ( !$session->check_acl_scope($perm_type, $namespace) )
  4278 			if ( !$session->check_acl_scope($perm_type, $namespace) )
  4279       {
  4279 			{
  4280         unset($acl_types[$perm_type]);
  4280 				unset($acl_types[$perm_type]);
  4281         unset($acl_deps[$perm_type]);
  4281 				unset($acl_deps[$perm_type]);
  4282         unset($acl_descs[$perm_type]);
  4282 				unset($acl_descs[$perm_type]);
  4283         unset($base[$perm_type]);
  4283 				unset($base[$perm_type]);
  4284       }
  4284 			}
  4285     }
  4285 		}
  4286     
  4286 		
  4287     $this->acl_deps = $acl_deps;
  4287 		$this->acl_deps = $acl_deps;
  4288     $this->acl_types = $acl_types;
  4288 		$this->acl_types = $acl_types;
  4289     $this->acl_descs = $acl_descs;
  4289 		$this->acl_descs = $acl_descs;
  4290     
  4290 		
  4291     $this->perms = $acl_types;
  4291 		$this->perms = $acl_types;
  4292     $this->perms = $session->acl_merge_complete($this->perms, $base);
  4292 		$this->perms = $session->acl_merge_complete($this->perms, $base);
  4293     
  4293 		
  4294     $this->perm_resolve_table = array();
  4294 		$this->perm_resolve_table = array();
  4295     if ( is_array($resolve_table) )
  4295 		if ( is_array($resolve_table) )
  4296       $this->perm_resolve_table = $resolve_table;
  4296 			$this->perm_resolve_table = $resolve_table;
  4297     
  4297 		
  4298     if ( is_int($user_id) && is_array($groups) )
  4298 		if ( is_int($user_id) && is_array($groups) )
  4299     {
  4299 		{
  4300       $this->user_id = $user_id;
  4300 			$this->user_id = $user_id;
  4301       $this->groups = $groups;
  4301 			$this->groups = $groups;
  4302     }
  4302 		}
  4303     else
  4303 		else
  4304     {
  4304 		{
  4305       $this->user_id = $session->user_id;
  4305 			$this->user_id = $session->user_id;
  4306       $this->groups = $session->groups;
  4306 			$this->groups = $session->groups;
  4307     }
  4307 		}
  4308     
  4308 		
  4309     $this->page_id = $page_id;
  4309 		$this->page_id = $page_id;
  4310     $this->namespace = $namespace;
  4310 		$this->namespace = $namespace;
  4311     
  4311 		
  4312     $this->__calculate();
  4312 		$this->__calculate();
  4313   }
  4313 	}
  4314   
  4314 	
  4315   /**
  4315 	/**
  4316    * Performs the actual permission calculation.
  4316  	* Performs the actual permission calculation.
  4317    * @access private
  4317  	* @access private
  4318    */
  4318  	*/
  4319   
  4319 	
  4320   private function __calculate()
  4320 	private function __calculate()
  4321   {
  4321 	{
  4322     global $db, $session, $paths, $template, $plugins; // Common objects
  4322 		global $db, $session, $paths, $template, $plugins; // Common objects
  4323     
  4323 		
  4324     $page_id =& $this->page_id;
  4324 		$page_id =& $this->page_id;
  4325     $namespace =& $this->namespace;
  4325 		$namespace =& $this->namespace;
  4326     
  4326 		
  4327     // PAGE group info
  4327 		// PAGE group info
  4328     $pg_list = $paths->get_page_groups($page_id, $namespace);
  4328 		$pg_list = $paths->get_page_groups($page_id, $namespace);
  4329     $pg_info = '';
  4329 		$pg_info = '';
  4330     foreach ( $pg_list as $g_id )
  4330 		foreach ( $pg_list as $g_id )
  4331     {
  4331 		{
  4332       $pg_info .= ' ( page_id=\'' . $g_id . '\' AND namespace=\'__PageGroup\' ) OR';
  4332 			$pg_info .= ' ( page_id=\'' . $g_id . '\' AND namespace=\'__PageGroup\' ) OR';
  4333     }
  4333 		}
  4334     
  4334 		
  4335     // Build a query to grab ACL info
  4335 		// Build a query to grab ACL info
  4336     $bs = 'SELECT rules,target_type,target_id,page_id,namespace,rule_id,pg.pg_name,g.group_name FROM '.table_prefix."acl AS a\n"
  4336 		$bs = 'SELECT rules,target_type,target_id,page_id,namespace,rule_id,pg.pg_name,g.group_name FROM '.table_prefix."acl AS a\n"
  4337         . "  LEFT JOIN " . table_prefix . "page_groups AS pg\n"
  4337 				. "  LEFT JOIN " . table_prefix . "page_groups AS pg\n"
  4338         . "    ON ( ( a.page_id = CAST(pg.pg_id AS char) AND a.namespace = '__PageGroup' ) OR ( a.namespace != '__PageGroup' ) )\n"
  4338 				. "    ON ( ( a.page_id = CAST(pg.pg_id AS char) AND a.namespace = '__PageGroup' ) OR ( a.namespace != '__PageGroup' ) )\n"
  4339         . "  LEFT JOIN " . table_prefix . "groups AS g\n"
  4339 				. "  LEFT JOIN " . table_prefix . "groups AS g\n"
  4340         . "    ON ( ( a.target_type = " . ACL_TYPE_GROUP . " AND a.target_id = g.group_id ) OR ( a.target_type != " . ACL_TYPE_GROUP . " ) )\n";
  4340 				. "    ON ( ( a.target_type = " . ACL_TYPE_GROUP . " AND a.target_id = g.group_id ) OR ( a.target_type != " . ACL_TYPE_GROUP . " ) )\n";
  4341     
  4341 		
  4342     $bs .= '  WHERE ' . "\n"
  4342 		$bs .= '  WHERE ' . "\n"
  4343           . '  ( ';
  4343 					. '  ( ';
  4344     $q = Array();
  4344 		$q = Array();
  4345     $q[] = '( target_type='.ACL_TYPE_USER.' AND target_id='.$this->user_id.' )';
  4345 		$q[] = '( target_type='.ACL_TYPE_USER.' AND target_id='.$this->user_id.' )';
  4346     if(count($this->groups) > 0)
  4346 		if(count($this->groups) > 0)
  4347     {
  4347 		{
  4348       foreach($this->groups as $g_id => $g_name)
  4348 			foreach($this->groups as $g_id => $g_name)
  4349       {
  4349 			{
  4350         $q[] = '( target_type='.ACL_TYPE_GROUP.' AND target_id='.intval($g_id).' )';
  4350 				$q[] = '( target_type='.ACL_TYPE_GROUP.' AND target_id='.intval($g_id).' )';
  4351       }
  4351 			}
  4352     }
  4352 		}
  4353     // The reason we're using an ORDER BY statement here is because ACL_TYPE_GROUP is less than ACL_TYPE_USER, causing the user's individual
  4353 		// The reason we're using an ORDER BY statement here is because ACL_TYPE_GROUP is less than ACL_TYPE_USER, causing the user's individual
  4354     // permissions to override group permissions.
  4354 		// permissions to override group permissions.
  4355     $bs .= implode(" OR\n    ", $q) . ' ) AND (' . $pg_info . ' ( page_id=\''.$db->escape($page_id).'\' AND namespace=\''.$db->escape($namespace).'\' ) )     
  4355 		$bs .= implode(" OR\n    ", $q) . ' ) AND (' . $pg_info . ' ( page_id=\''.$db->escape($page_id).'\' AND namespace=\''.$db->escape($namespace).'\' ) )     
  4356       ORDER BY target_type ASC, page_id ASC, namespace ASC;';
  4356 			ORDER BY target_type ASC, page_id ASC, namespace ASC;';
  4357       
  4357 			
  4358     $q = $session->sql($bs);
  4358 		$q = $session->sql($bs);
  4359     if ( $row = $db->fetchrow($q, true) )
  4359 		if ( $row = $db->fetchrow($q, true) )
  4360     {
  4360 		{
  4361       do
  4361 			do
  4362       {
  4362 			{
  4363         $rules = $session->string_to_perm($row['rules']);
  4363 				$rules = $session->string_to_perm($row['rules']);
  4364         $is_everyone = ( $row['target_type'] == ACL_TYPE_GROUP && $row['target_id'] == 1 );
  4364 				$is_everyone = ( $row['target_type'] == ACL_TYPE_GROUP && $row['target_id'] == 1 );
  4365         // log where this comes from
  4365 				// log where this comes from
  4366         if ( $row['namespace'] == '__PageGroup' )
  4366 				if ( $row['namespace'] == '__PageGroup' )
  4367         {
  4367 				{
  4368           $src = ( $is_everyone ) ? ACL_INHERIT_PG_EVERYONE : ( $row['target_type'] == ACL_TYPE_GROUP ? ACL_INHERIT_PG_GROUP : ACL_INHERIT_PG_USER );
  4368 					$src = ( $is_everyone ) ? ACL_INHERIT_PG_EVERYONE : ( $row['target_type'] == ACL_TYPE_GROUP ? ACL_INHERIT_PG_GROUP : ACL_INHERIT_PG_USER );
  4369           $pg_name = $row['pg_name'];
  4369 					$pg_name = $row['pg_name'];
  4370         }
  4370 				}
  4371         else
  4371 				else
  4372         {
  4372 				{
  4373           $src = ( $is_everyone ) ? ACL_INHERIT_LOCAL_EVERYONE : ( $row['target_type'] == ACL_TYPE_GROUP ? ACL_INHERIT_LOCAL_GROUP : ACL_INHERIT_LOCAL_USER );
  4373 					$src = ( $is_everyone ) ? ACL_INHERIT_LOCAL_EVERYONE : ( $row['target_type'] == ACL_TYPE_GROUP ? ACL_INHERIT_LOCAL_GROUP : ACL_INHERIT_LOCAL_USER );
  4374         }
  4374 				}
  4375         if ( $row['group_name'] )
  4375 				if ( $row['group_name'] )
  4376         {
  4376 				{
  4377           $group_name = $row['group_name'];
  4377 					$group_name = $row['group_name'];
  4378         }
  4378 				}
  4379         foreach ( $rules as $perm_type => $perm_value )
  4379 				foreach ( $rules as $perm_type => $perm_value )
  4380         {
  4380 				{
  4381           if ( !isset($this->perms[$perm_type]) )
  4381 					if ( !isset($this->perms[$perm_type]) )
  4382             continue;
  4382 						continue;
  4383           
  4383 					
  4384           if ( $this->perms[$perm_type] == AUTH_DENY )
  4384 					if ( $this->perms[$perm_type] == AUTH_DENY )
  4385             continue;
  4385 						continue;
  4386           
  4386 					
  4387           if ( !$session->check_acl_scope($perm_type, $this->namespace) )
  4387 					if ( !$session->check_acl_scope($perm_type, $this->namespace) )
  4388             continue;
  4388 						continue;
  4389           
  4389 					
  4390           $this->perm_resolve_table[$perm_type] = array(
  4390 					$this->perm_resolve_table[$perm_type] = array(
  4391               'src' => $src,
  4391 							'src' => $src,
  4392               'rule_id' => $row['rule_id']
  4392 							'rule_id' => $row['rule_id']
  4393             );
  4393 						);
  4394           if ( isset($pg_name) )
  4394 					if ( isset($pg_name) )
  4395           {
  4395 					{
  4396             $this->perm_resolve_table[$perm_type]['pg_name'] = $pg_name;
  4396 						$this->perm_resolve_table[$perm_type]['pg_name'] = $pg_name;
  4397           }
  4397 					}
  4398           if ( isset($group_name) )
  4398 					if ( isset($group_name) )
  4399           {
  4399 					{
  4400             $this->perm_resolve_table[$perm_type]['group_name'] = $group_name;
  4400 						$this->perm_resolve_table[$perm_type]['group_name'] = $group_name;
  4401           }
  4401 					}
  4402         }
  4402 				}
  4403         $this->acl_merge_with_current($rules, $is_everyone);
  4403 				$this->acl_merge_with_current($rules, $is_everyone);
  4404       } while ( $row = $db->fetchrow() );
  4404 			} while ( $row = $db->fetchrow() );
  4405     }
  4405 		}
  4406     
  4406 		
  4407     $this->page_id = $page_id;
  4407 		$this->page_id = $page_id;
  4408     $this->namespace = $namespace;
  4408 		$this->namespace = $namespace;
  4409     
  4409 		
  4410     $pathskey = $paths->nslist[$this->namespace].sanitize_page_id($this->page_id);
  4410 		$pathskey = $paths->nslist[$this->namespace].sanitize_page_id($this->page_id);
  4411     $ns = namespace_factory($this->page_id, $this->namespace);
  4411 		$ns = namespace_factory($this->page_id, $this->namespace);
  4412     $cdata = $ns->get_cdata();
  4412 		$cdata = $ns->get_cdata();
  4413     $ppwm = $cdata['wiki_mode'];
  4413 		$ppwm = $cdata['wiki_mode'];
  4414     unset($ns, $cdata);
  4414 		unset($ns, $cdata);
  4415     
  4415 		
  4416     if ( $ppwm == 1 && ( $session->user_logged_in || getConfig('wiki_mode_require_login') != '1' ) )
  4416 		if ( $ppwm == 1 && ( $session->user_logged_in || getConfig('wiki_mode_require_login') != '1' ) )
  4417       $this->wiki_mode = true;
  4417 			$this->wiki_mode = true;
  4418     else if ( $ppwm == 1 && !$session->user_logged_in && getConfig('wiki_mode_require_login') == '1' )
  4418 		else if ( $ppwm == 1 && !$session->user_logged_in && getConfig('wiki_mode_require_login') == '1' )
  4419       $this->wiki_mode = true;
  4419 			$this->wiki_mode = true;
  4420     else if ( $ppwm == 0 )
  4420 		else if ( $ppwm == 0 )
  4421       $this->wiki_mode = false;
  4421 			$this->wiki_mode = false;
  4422     else if ( $ppwm == 2 )
  4422 		else if ( $ppwm == 2 )
  4423     {
  4423 		{
  4424       if ( $this->user_id > 1 )
  4424 			if ( $this->user_id > 1 )
  4425       {
  4425 			{
  4426         $this->wiki_mode = ( getConfig('wiki_mode') == '1' );
  4426 				$this->wiki_mode = ( getConfig('wiki_mode') == '1' );
  4427       }
  4427 			}
  4428       else
  4428 			else
  4429       {
  4429 			{
  4430         $this->wiki_mode = ( getConfig('wiki_mode') == '1' && getConfig('wiki_mode_require_login') != '1' );
  4430 				$this->wiki_mode = ( getConfig('wiki_mode') == '1' && getConfig('wiki_mode_require_login') != '1' );
  4431       }
  4431 			}
  4432     }
  4432 		}
  4433     else
  4433 		else
  4434     {
  4434 		{
  4435       // Ech. Internal logic failure, this should never happen.
  4435 			// Ech. Internal logic failure, this should never happen.
  4436       return false;
  4436 			return false;
  4437     }
  4437 		}
  4438   }
  4438 	}
  4439   
  4439 	
  4440   /**
  4440 	/**
  4441    * Tells us whether permission $type is allowed or not based on the current rules.
  4441  	* Tells us whether permission $type is allowed or not based on the current rules.
  4442    * @param string $type The permission identifier ($acl_type passed to sessionManager::register_acl_type())
  4442  	* @param string $type The permission identifier ($acl_type passed to sessionManager::register_acl_type())
  4443    * @param bool $no_deps If true, disables dependency checking
  4443  	* @param bool $no_deps If true, disables dependency checking
  4444    * @return bool True if allowed, false if denied or if an error occured
  4444  	* @return bool True if allowed, false if denied or if an error occured
  4445    */
  4445  	*/
  4446    
  4446  	
  4447   function get_permissions($type, $no_deps = false)
  4447 	function get_permissions($type, $no_deps = false)
  4448   {
  4448 	{
  4449     // echo '<pre>' . print_r($this->perms, true) . '</pre>';
  4449 		// echo '<pre>' . print_r($this->perms, true) . '</pre>';
  4450     global $db, $session, $paths, $template, $plugins; // Common objects
  4450 		global $db, $session, $paths, $template, $plugins; // Common objects
  4451     
  4451 		
  4452     if ( isset( $this->perms[$type] ) )
  4452 		if ( isset( $this->perms[$type] ) )
  4453     {
  4453 		{
  4454       if ( $this->perms[$type] == AUTH_DENY )
  4454 			if ( $this->perms[$type] == AUTH_DENY )
  4455       {
  4455 			{
  4456         $ret = false;
  4456 				$ret = false;
  4457       }
  4457 			}
  4458       else if ( $this->perms[$type] == AUTH_WIKIMODE && $this->wiki_mode )
  4458 			else if ( $this->perms[$type] == AUTH_WIKIMODE && $this->wiki_mode )
  4459       {
  4459 			{
  4460         $ret = true;
  4460 				$ret = true;
  4461       }
  4461 			}
  4462       else if ( $this->perms[$type] == AUTH_WIKIMODE && !$this->wiki_mode )
  4462 			else if ( $this->perms[$type] == AUTH_WIKIMODE && !$this->wiki_mode )
  4463       {
  4463 			{
  4464         $ret = false;
  4464 				$ret = false;
  4465       }
  4465 			}
  4466       else if ( $this->perms[$type] == AUTH_ALLOW )
  4466 			else if ( $this->perms[$type] == AUTH_ALLOW )
  4467       {
  4467 			{
  4468         $ret = true;
  4468 				$ret = true;
  4469       }
  4469 			}
  4470       else if ( $this->perms[$type] == AUTH_DISALLOW )
  4470 			else if ( $this->perms[$type] == AUTH_DISALLOW )
  4471       {
  4471 			{
  4472         $ret = false;
  4472 				$ret = false;
  4473       }
  4473 			}
  4474     }
  4474 		}
  4475     else if(isset($this->acl_types[$type]))
  4475 		else if(isset($this->acl_types[$type]))
  4476     {
  4476 		{
  4477       if ( $this->acl_types[$type] == AUTH_DENY )
  4477 			if ( $this->acl_types[$type] == AUTH_DENY )
  4478         $ret = false;
  4478 				$ret = false;
  4479       else if ( $this->acl_types[$type] == AUTH_WIKIMODE && $paths->wiki_mode )
  4479 			else if ( $this->acl_types[$type] == AUTH_WIKIMODE && $paths->wiki_mode )
  4480         $ret = true;
  4480 				$ret = true;
  4481       else if ( $this->acl_types[$type] == AUTH_WIKIMODE && !$paths->wiki_mode )
  4481 			else if ( $this->acl_types[$type] == AUTH_WIKIMODE && !$paths->wiki_mode )
  4482         $ret = false;
  4482 				$ret = false;
  4483       else if ( $this->acl_types[$type] == AUTH_ALLOW )
  4483 			else if ( $this->acl_types[$type] == AUTH_ALLOW )
  4484         $ret = true;
  4484 				$ret = true;
  4485       else if ( $this->acl_types[$type] == AUTH_DISALLOW )
  4485 			else if ( $this->acl_types[$type] == AUTH_DISALLOW )
  4486         $ret = false;
  4486 				$ret = false;
  4487     }
  4487 		}
  4488     else
  4488 		else
  4489     {
  4489 		{
  4490       // ACL type is undefined
  4490 			// ACL type is undefined
  4491       $caller = 'unknown';
  4491 			$caller = 'unknown';
  4492       if ( function_exists('debug_backtrace') )
  4492 			if ( function_exists('debug_backtrace') )
  4493       {
  4493 			{
  4494         if ( $bt = @debug_backtrace() )
  4494 				if ( $bt = @debug_backtrace() )
  4495         {
  4495 				{
  4496           foreach ( $bt as $trace )
  4496 					foreach ( $bt as $trace )
  4497           {
  4497 					{
  4498             $file = basename($trace['file']);
  4498 						$file = basename($trace['file']);
  4499             if ( $file != 'sessions.php' )
  4499 						if ( $file != 'sessions.php' )
  4500             {
  4500 						{
  4501               $caller = $file . ':' . $trace['line'];
  4501 							$caller = $file . ':' . $trace['line'];
  4502               break;
  4502 							break;
  4503             }
  4503 						}
  4504           }
  4504 					}
  4505         }
  4505 				}
  4506       }
  4506 			}
  4507       trigger_error('Unknown access type "' . $type . '", called from ' . $caller . '', E_USER_WARNING);
  4507 			trigger_error('Unknown access type "' . $type . '", called from ' . $caller . '', E_USER_WARNING);
  4508       return false; // Be on the safe side and deny access
  4508 			return false; // Be on the safe side and deny access
  4509     }
  4509 		}
  4510     if ( !$no_deps )
  4510 		if ( !$no_deps )
  4511     {
  4511 		{
  4512       if ( !$this->acl_check_deps($type) )
  4512 			if ( !$this->acl_check_deps($type) )
  4513         return false;
  4513 				return false;
  4514     }
  4514 		}
  4515     return $ret;
  4515 		return $ret;
  4516   }
  4516 	}
  4517   
  4517 	
  4518   /**
  4518 	/**
  4519    * Tell us if the dependencies for a given permission are met.
  4519  	* Tell us if the dependencies for a given permission are met.
  4520    * @param string The ACL permission ID
  4520  	* @param string The ACL permission ID
  4521    * @param bool If true, does not return a boolean value, but instead returns array of dependencies that fail
  4521  	* @param bool If true, does not return a boolean value, but instead returns array of dependencies that fail
  4522    * @return bool
  4522  	* @return bool
  4523    */
  4523  	*/
  4524    
  4524  	
  4525   function acl_check_deps($type, $debug = false)
  4525 	function acl_check_deps($type, $debug = false)
  4526   {
  4526 	{
  4527     // This will only happen if the permissions table is hacked or improperly accessed
  4527 		// This will only happen if the permissions table is hacked or improperly accessed
  4528     if(!isset($this->acl_deps[$type]))
  4528 		if(!isset($this->acl_deps[$type]))
  4529       return $debug ? array() : true;
  4529 			return $debug ? array() : true;
  4530     // Permission has no dependencies?
  4530 		// Permission has no dependencies?
  4531     if(sizeof($this->acl_deps[$type]) < 1)
  4531 		if(sizeof($this->acl_deps[$type]) < 1)
  4532       return $debug ? array() : true;
  4532 			return $debug ? array() : true;
  4533     // go through them all and build a flat list of dependencies
  4533 		// go through them all and build a flat list of dependencies
  4534     $deps = $this->acl_deps[$type];
  4534 		$deps = $this->acl_deps[$type];
  4535     while(true)
  4535 		while(true)
  4536     {
  4536 		{
  4537       $j = sizeof($deps);
  4537 			$j = sizeof($deps);
  4538       for ( $i = 0; $i < $j; $i++ )
  4538 			for ( $i = 0; $i < $j; $i++ )
  4539       {
  4539 			{
  4540         $b = $deps;
  4540 				$b = $deps;
  4541         if ( !isset($this->acl_deps[$deps[$i]]) )
  4541 				if ( !isset($this->acl_deps[$deps[$i]]) )
  4542         {
  4542 				{
  4543           // Action $type depends on action $deps[$i] which cannot be satisfied because $deps[$i] is out of scope.
  4543 					// Action $type depends on action $deps[$i] which cannot be satisfied because $deps[$i] is out of scope.
  4544           trigger_error("acl_check_deps: $type depends on {$deps[$i]} which is not within scope of $this->namespace; this indicats a bug in ACL rule specification", E_USER_WARNING);
  4544 					trigger_error("acl_check_deps: $type depends on {$deps[$i]} which is not within scope of $this->namespace; this indicats a bug in ACL rule specification", E_USER_WARNING);
  4545           return false;
  4545 					return false;
  4546         }
  4546 				}
  4547         $deps = array_merge($deps, $this->acl_deps[$deps[$i]]);
  4547 				$deps = array_merge($deps, $this->acl_deps[$deps[$i]]);
  4548         if( $b == $deps )
  4548 				if( $b == $deps )
  4549         {
  4549 				{
  4550           break 2;
  4550 					break 2;
  4551         }
  4551 				}
  4552         $j = sizeof($deps);
  4552 				$j = sizeof($deps);
  4553       }
  4553 			}
  4554     }
  4554 		}
  4555     $debugdata = array();
  4555 		$debugdata = array();
  4556     foreach($deps as $d)
  4556 		foreach($deps as $d)
  4557     {
  4557 		{
  4558       // Our dependencies are fully resolved, so tell get_permissions() to not recursively call this function
  4558 			// Our dependencies are fully resolved, so tell get_permissions() to not recursively call this function
  4559       if ( !$this->get_permissions($d, true) )
  4559 			if ( !$this->get_permissions($d, true) )
  4560       {
  4560 			{
  4561         if ( $debug )
  4561 				if ( $debug )
  4562         {
  4562 				{
  4563           $debugdata[] = $d;
  4563 					$debugdata[] = $d;
  4564         }
  4564 				}
  4565         else
  4565 				else
  4566         {
  4566 				{
  4567           return false;
  4567 					return false;
  4568         }
  4568 				}
  4569       }
  4569 			}
  4570     }
  4570 		}
  4571     return $debug ? $debugdata : true;
  4571 		return $debug ? $debugdata : true;
  4572   }
  4572 	}
  4573   
  4573 	
  4574   /**
  4574 	/**
  4575    * Merges the ACL array sent with the current permissions table, deciding precedence based on whether defaults are in effect or not.
  4575  	* Merges the ACL array sent with the current permissions table, deciding precedence based on whether defaults are in effect or not.
  4576    * @param array The array to merge into the master ACL list
  4576  	* @param array The array to merge into the master ACL list
  4577    * @param bool If true, $perm is treated as the "new default"
  4577  	* @param bool If true, $perm is treated as the "new default"
  4578    * @param int 1 if this is a site-wide ACL, 2 if page-specific. Defaults to 2.
  4578  	* @param int 1 if this is a site-wide ACL, 2 if page-specific. Defaults to 2.
  4579    */
  4579  	*/
  4580   
  4580 	
  4581   function acl_merge_with_current($perm, $is_everyone = false, $scope = 2)
  4581 	function acl_merge_with_current($perm, $is_everyone = false, $scope = 2)
  4582   {
  4582 	{
  4583     foreach ( $this->perms as $i => $p )
  4583 		foreach ( $this->perms as $i => $p )
  4584     {
  4584 		{
  4585       if ( isset($perm[$i]) )
  4585 			if ( isset($perm[$i]) )
  4586       {
  4586 			{
  4587         if ( $is_everyone && !@$this->acl_defaults_used[$i] )
  4587 				if ( $is_everyone && !@$this->acl_defaults_used[$i] )
  4588           continue;
  4588 					continue;
  4589         // Decide precedence
  4589 				// Decide precedence
  4590         if ( isset($this->acl_defaults_used[$i]) )
  4590 				if ( isset($this->acl_defaults_used[$i]) )
  4591         {
  4591 				{
  4592           //echo "$i: default in use, overriding to: {$perm[$i]}<br />";
  4592 					//echo "$i: default in use, overriding to: {$perm[$i]}<br />";
  4593           // Defaults are in use, override
  4593 					// Defaults are in use, override
  4594           $this->perms[$i] = $perm[$i];
  4594 					$this->perms[$i] = $perm[$i];
  4595           $this->acl_defaults_used[$i] = ( $is_everyone );
  4595 					$this->acl_defaults_used[$i] = ( $is_everyone );
  4596         }
  4596 				}
  4597         else
  4597 				else
  4598         {
  4598 				{
  4599           //echo "$i: default NOT in use";
  4599 					//echo "$i: default NOT in use";
  4600           // Defaults are not in use, merge as normal
  4600 					// Defaults are not in use, merge as normal
  4601           if ( $this->perms[$i] != AUTH_DENY )
  4601 					if ( $this->perms[$i] != AUTH_DENY )
  4602           {
  4602 					{
  4603             //echo ", but overriding";
  4603 						//echo ", but overriding";
  4604             $this->perms[$i] = $perm[$i];
  4604 						$this->perms[$i] = $perm[$i];
  4605           }
  4605 					}
  4606           //echo "<br />";
  4606 					//echo "<br />";
  4607         }
  4607 				}
  4608       }
  4608 			}
  4609     }
  4609 		}
  4610   }
  4610 	}
  4611   
  4611 	
  4612 }
  4612 }
  4613 
  4613 
  4614 /**
  4614 /**
  4615  * Cron task - clears out the database of Diffie-Hellman keys
  4615  * Cron task - clears out the database of Diffie-Hellman keys
  4616  */
  4616  */
  4617 
  4617 
  4618 function cron_clean_login_cache()
  4618 function cron_clean_login_cache()
  4619 {
  4619 {
  4620   global $db, $session, $paths, $template, $plugins; // Common objects
  4620 	global $db, $session, $paths, $template, $plugins; // Common objects
  4621   
  4621 	
  4622   if ( !$db->sql_query('DELETE FROM ' . table_prefix . 'diffiehellman;') )
  4622 	if ( !$db->sql_query('DELETE FROM ' . table_prefix . 'diffiehellman;') )
  4623     $db->_die();
  4623 		$db->_die();
  4624   
  4624 	
  4625   setConfig('login_key_cache', '');
  4625 	setConfig('login_key_cache', '');
  4626 }
  4626 }
  4627 
  4627 
  4628 register_cron_task('cron_clean_login_cache', 72);
  4628 register_cron_task('cron_clean_login_cache', 72);
  4629 
  4629 
  4630 /**
  4630 /**
  4631  * Cron task - clears out outdated high-auth session keys
  4631  * Cron task - clears out outdated high-auth session keys
  4632  */
  4632  */
  4633 
  4633 
  4634 function cron_clean_old_admin_keys()
  4634 function cron_clean_old_admin_keys()
  4635 {
  4635 {
  4636   global $db, $session, $paths, $template, $plugins; // Common objects
  4636 	global $db, $session, $paths, $template, $plugins; // Common objects
  4637   
  4637 	
  4638   $threshold = time() - ( 15 * 60 );
  4638 	$threshold = time() - ( 15 * 60 );
  4639   $ul_member = USER_LEVEL_MEMBER;
  4639 	$ul_member = USER_LEVEL_MEMBER;
  4640   if ( !$db->sql_query('DELETE FROM ' . table_prefix . "session_keys WHERE time < $threshold AND auth_level > $ul_member;") )
  4640 	if ( !$db->sql_query('DELETE FROM ' . table_prefix . "session_keys WHERE time < $threshold AND auth_level > $ul_member;") )
  4641     $db->_die();
  4641 		$db->_die();
  4642 }
  4642 }
  4643 
  4643 
  4644 // Once a week
  4644 // Once a week
  4645 register_cron_task('cron_clean_old_admin_keys', 168);
  4645 register_cron_task('cron_clean_old_admin_keys', 168);
  4646 
  4646