includes/sessions.php
changeset 1240 2b6cdff92b09
parent 1231 4797a4a88533
child 1242 9aa09b0a7544
equal deleted inserted replaced
1239:e88534039a8d 1240:2b6cdff92b09
    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',
   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') )
   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);
   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);
   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;
   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
   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 		
   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();
   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;
   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
   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);
  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 		
  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'];
  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 		
  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 		
  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 		
  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 		}
  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 		}
  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;
  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;
  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 )
  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 		{
  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 	static 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))
  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 	static 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 		
  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 		
  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 		
  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 		
  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 		{
  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).'\';');
  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 		
  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 		
  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;
  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).'\'');
  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 		
  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
  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), '', '');
  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);
  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();
  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;
  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;
  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 		
  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
  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 		{
  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 )
  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 		
  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;
  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 		{
  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);
  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 		{
  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)
  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 		{
  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 		{
  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 		{
  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
  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);
  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';
  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 		
  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 = '';
  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 	static 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)
  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
  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. Result: " + cryptstring);
       
  3664 							console.debug(frm);
  3664 							frm.'.$crypt_data.'.value = cryptstring;
  3665 							frm.'.$crypt_data.'.value = cryptstring;
  3665 							frm.'.$pw_field.'.value = \'\';
  3666 							frm.'.$pw_field.'.value = \'\';
  3666 							// console.info("DiffieHellman: ready to submit");
  3667 							// console.info("DiffieHellman: ready to submit");
  3667 						}
  3668 						}
  3668 						else if ( testpassed && !use_diffiehellman )
  3669 						else if ( testpassed && !use_diffiehellman )
  3696 				';
  3697 				';
  3697 		return $code;
  3698 		return $code;
  3698 	}
  3699 	}
  3699 	
  3700 	
  3700 	/**
  3701 	/**
  3701  	* Generates the HTML form elements required for an encrypted logon experience.
  3702 	 * Generates the HTML form elements required for an encrypted logon experience.
  3702  	* @return string
  3703 	 * @param reference Optional variable to fill with the server's public and private key. If IN_ENANO_INSTALL is defined, storing and retrieving the key
  3703  	*/
  3704 	 *                  is YOUR responsibility.
  3704 	
  3705 	 * @return string
  3705 	function generate_aes_form()
  3706 	 */
  3706 	{
  3707 	
       
  3708 	static function generate_aes_form(&$dh_store = array())
       
  3709 	{
       
  3710 		$is_static = !( isset($this) && get_class($this) === __CLASS__ );
       
  3711 		if ( $is_static )
       
  3712 		{
       
  3713 			$aes = AESCrypt::singleton(AES_BITS, AES_BLOCKSIZE);
       
  3714 			$aes_key = $aes->gen_readymade_key();
       
  3715 		}
       
  3716 		else
       
  3717 		{
       
  3718 			$aes_key = self::rijndael_genkey();
       
  3719 		}
       
  3720 		$dh_store = array('aes' => $aes_key, 'public' => '', 'private' => '');
       
  3721 		
  3707 		$return = '<input type="hidden" name="use_crypt" value="no" />';
  3722 		$return = '<input type="hidden" name="use_crypt" value="no" />';
  3708 		$return .= '<input type="hidden" name="crypt_key" value="' . $this->rijndael_genkey() . '" />';
  3723 		$return .= '<input type="hidden" name="crypt_key" value="' . $aes_key . '" />';
  3709 		$return .= '<input type="hidden" name="crypt_data" value="" />';
  3724 		$return .= '<input type="hidden" name="crypt_data" value="" />';
  3710 		$return .= '<input type="hidden" name="challenge_data" value="' . $this->dss_rand() . '" />';
  3725 		$return .= '<input type="hidden" name="challenge_data" value="' . self::dss_rand() . '" />';
  3711 		
  3726 		
  3712 		require_once(ENANO_ROOT . '/includes/math.php');
  3727 		require_once(ENANO_ROOT . '/includes/math.php');
  3713 		require_once(ENANO_ROOT . '/includes/diffiehellman.php');
  3728 		require_once(ENANO_ROOT . '/includes/diffiehellman.php');
  3714 		
  3729 		
  3715 		global $dh_supported, $_math;
  3730 		global $dh_supported, $_math;
  3718 			$dh_key_priv = dh_gen_private();
  3733 			$dh_key_priv = dh_gen_private();
  3719 			$dh_key_pub = dh_gen_public($dh_key_priv);
  3734 			$dh_key_pub = dh_gen_public($dh_key_priv);
  3720 			$dh_key_priv = $_math->str($dh_key_priv);
  3735 			$dh_key_priv = $_math->str($dh_key_priv);
  3721 			$dh_key_pub = $_math->str($dh_key_pub);
  3736 			$dh_key_pub = $_math->str($dh_key_pub);
  3722 			// store the keys in the DB
  3737 			// 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' );");
  3738 			// this is doing a static call check to avoid using $this in a static call
       
  3739 			if ( !defined('IN_ENANO_INSTALL') && isset($this) && get_class($this) === __CLASS__ )
       
  3740 				$this->sql('INSERT INTO ' . table_prefix . "diffiehellman( public_key, private_key ) VALUES ( '$dh_key_pub', '$dh_key_priv' );");
       
  3741 			// also give the key to the calling function
       
  3742 			$dh_store['public'] = $dh_key_pub;
       
  3743 			$dh_store['private'] = $dh_key_priv;
  3724 			
  3744 			
  3725 			$return .=  "<input type=\"hidden\" name=\"dh_supported\" value=\"true\" />
  3745 			$return .=  "<input type=\"hidden\" name=\"dh_supported\" value=\"true\" />
  3726 						<input type=\"hidden\" name=\"dh_public_key\" value=\"$dh_key_pub\" />
  3746 						<input type=\"hidden\" name=\"dh_public_key\" value=\"$dh_key_pub\" />
  3727 						<input type=\"hidden\" name=\"dh_client_public_key\" value=\"\" />";
  3747 						<input type=\"hidden\" name=\"dh_client_public_key\" value=\"\" />";
  3728 		}
  3748 		}
  3732 		}
  3752 		}
  3733 		return $return;
  3753 		return $return;
  3734 	}
  3754 	}
  3735 	
  3755 	
  3736 	/**
  3756 	/**
  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.
  3757 	 * 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")
  3758 	 * @param string Password field name (defaults to "password")
  3739  	* @return string
  3759 	 * @param array Optional associative array with fields "public", "private", and "aes".
  3740  	*/
  3760 	 * @return string
  3741 	
  3761 	 */
  3742 	function get_aes_post($fieldname = 'password')
  3762 	
       
  3763 	static function get_aes_post($fieldname = 'password', $keys = false)
  3743 	{
  3764 	{
  3744 		global $db, $session, $paths, $template, $plugins; // Common objects
  3765 		global $db, $session, $paths, $template, $plugins; // Common objects
  3745 		
  3766 		
  3746 		$aes = AESCrypt::singleton(AES_BITS, AES_BLOCKSIZE);
  3767 		$aes = AESCrypt::singleton(AES_BITS, AES_BLOCKSIZE);
  3747 		if ( $_POST['use_crypt'] == 'yes' )
  3768 		if ( $_POST['use_crypt'] == 'yes' )
  3748 		{
  3769 		{
  3749 			$crypt_key = $this->fetch_public_key($_POST['crypt_key']);
  3770 			$is_static = !( isset($this) && get_class($this) === __CLASS__ );
       
  3771 			if ( $is_static && ( !is_array($keys) || (is_array($keys) && !isset($keys['aes'])) ) )
       
  3772 			{
       
  3773 				throw new Exception('ERR_STATIC_REQUIRES_KEY');
       
  3774 			}
       
  3775 			$crypt_key = $is_static ? $keys['aes'] : $this->fetch_public_key($_POST['crypt_key']);
  3750 			if ( !$crypt_key )
  3776 			if ( !$crypt_key )
  3751 			{
  3777 			{
  3752 				throw new Exception($lang->get('user_err_key_not_found'));
  3778 				throw new Exception($lang->get('user_err_key_not_found'));
  3753 			}
  3779 			}
  3754 			$crypt_key = hexdecode($crypt_key);
  3780 			$crypt_key = hexdecode($crypt_key);
  3770 			$dh_public = $_POST['dh_public_key'];
  3796 			$dh_public = $_POST['dh_public_key'];
  3771 			if ( !ctype_digit($dh_public) )
  3797 			if ( !ctype_digit($dh_public) )
  3772 			{
  3798 			{
  3773 				throw new Exception('ERR_DH_KEY_NOT_INTEGER');
  3799 				throw new Exception('ERR_DH_KEY_NOT_INTEGER');
  3774 			}
  3800 			}
  3775 			$q = $db->sql_query('SELECT private_key, key_id FROM ' . table_prefix . "diffiehellman WHERE public_key = '$dh_public';");
  3801 			if ( is_array($keys) && isset($keys['public']) && isset($keys['private']) )
  3776 			if ( !$q )
  3802 			{
  3777 				$db->die_json();
  3803 				if ( $keys['public'] !== $dh_public )
  3778 			
  3804 					throw new Exception('ERR_DH_KEY_NOT_FOUND');
  3779 			if ( $db->numrows() < 1 )
  3805 				$dh_private = $keys['private'];
  3780 			{
  3806 			}
  3781 				throw new Exception('ERR_DH_KEY_NOT_FOUND');
  3807 			else
  3782 			}
  3808 			{
  3783 			
  3809 				$q = $db->sql_query('SELECT private_key, key_id FROM ' . table_prefix . "diffiehellman WHERE public_key = '$dh_public';");
  3784 			list($dh_private, $dh_key_id) = $db->fetchrow_num();
  3810 				if ( !$q )
  3785 			$db->free_result();
  3811 					$db->die_json();
  3786 			
  3812 				
  3787 			// We have the private key, now delete the key pair, we no longer need it
  3813 				if ( $db->numrows() < 1 )
  3788 			$q = $db->sql_query('DELETE FROM ' . table_prefix . "diffiehellman WHERE key_id = $dh_key_id;");
  3814 				{
  3789 			if ( !$q )
  3815 					throw new Exception('ERR_DH_KEY_NOT_FOUND');
  3790 				$db->die_json();
  3816 				}
       
  3817 				
       
  3818 				list($dh_private, $dh_key_id) = $db->fetchrow_num();
       
  3819 				$db->free_result();
       
  3820 				
       
  3821 				// We have the private key, now delete the key pair, we no longer need it
       
  3822 				$q = $db->sql_query('DELETE FROM ' . table_prefix . "diffiehellman WHERE key_id = $dh_key_id;");
       
  3823 				if ( !$q )
       
  3824 					$db->die_json();
       
  3825 			}
  3791 			
  3826 			
  3792 			// Generate the shared secret
  3827 			// Generate the shared secret
  3793 			$dh_secret = dh_gen_shared_secret($dh_private, $_POST['dh_client_public_key']);
  3828 			$dh_secret = dh_gen_shared_secret($dh_private, $_POST['dh_client_public_key']);
  3794 			$dh_secret = $_math->str($dh_secret);
  3829 			$dh_secret = $_math->str($dh_secret);
  3795 			
  3830 			
  3815 		}
  3850 		}
  3816 		return $data;
  3851 		return $data;
  3817 	}
  3852 	}
  3818 	
  3853 	
  3819 	/**
  3854 	/**
  3820  	* Backend code for the JSON login interface. Basically a frontend to the session API that takes all parameters in one huge array.
  3855 	 * 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
  3856 	 * @param array LoginAPI request
  3822  	* @return array LoginAPI response
  3857 	 * @return array LoginAPI response
  3823  	*/
  3858 	 */
  3824 	
  3859 	
  3825 	function process_login_request($req)
  3860 	function process_login_request($req)
  3826 	{
  3861 	{
  3827 		global $db, $session, $paths, $template, $plugins; // Common objects
  3862 		global $db, $session, $paths, $template, $plugins; // Common objects
  3828 		
  3863 		
  3975 					return $this->get_login_response('login_failure', 'lockout_request_denied');
  4010 					return $this->get_login_response('login_failure', 'lockout_request_denied');
  3976 				}
  4011 				}
  3977 				
  4012 				
  3978 				// At this point if any extra info was injected into the login data packet, we need to let plugins process it
  4013 				// At this point if any extra info was injected into the login data packet, we need to let plugins process it
  3979 				/**
  4014 				/**
  3980  				* Called upon processing an incoming login request. If you added anything to the userinfo object during the jshook
  4015 	 			* 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
  4016 	 			* 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
  4017 	 			* 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
  4018 	 			* 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.
  4019 	 			* 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
  4020 	 			* @hook login_process_userdata_json
  3986  				*/
  4021 	 			*/
  3987 				
  4022 				
  3988 				$code = $plugins->setHook('login_process_userdata_json', true);
  4023 				$code = $plugins->setHook('login_process_userdata_json', true);
  3989 				
  4024 				
  3990 				foreach ( $code as $cmd )
  4025 				foreach ( $code as $cmd )
  3991 				{
  4026 				{
  4094 		}
  4129 		}
  4095 		
  4130 		
  4096 	}
  4131 	}
  4097 	
  4132 	
  4098 	/**
  4133 	/**
  4099  	* Generate a packet to send to the client for logins.
  4134 	 * Generate a packet to send to the client for logins.
  4100  	* @param string mode
  4135 	 * @param string mode
  4101  	* @param array 
  4136 	 * @param array 
  4102  	* @return array
  4137 	 * @return array
  4103  	*/
  4138 	 */
  4104 	
  4139 	
  4105 	function get_login_response($mode, $error = false, $base = array())
  4140 	function get_login_response($mode, $error = false, $base = array())
  4106 	{
  4141 	{
  4107 		$this->start();
  4142 		$this->start();
  4108 		
  4143 		
  4117 		$response['username'] = $this->user_logged_in ? $this->username : false;
  4152 		$response['username'] = $this->user_logged_in ? $this->username : false;
  4118 		return $response;
  4153 		return $response;
  4119 	}
  4154 	}
  4120 	
  4155 	
  4121 	/**
  4156 	/**
  4122  	* Get a packet of crypto flags for login.
  4157 	 * Get a packet of crypto flags for login.
  4123  	* @return array
  4158 	 * @return array
  4124  	*/
  4159 	 */
  4125 	
  4160 	
  4126 	function get_login_crypto_packet()
  4161 	function get_login_crypto_packet()
  4127 	{
  4162 	{
  4128 		global $dh_supported, $_math;
  4163 		global $dh_supported, $_math;
  4129 		
  4164 		
  4158  */
  4193  */
  4159  
  4194  
  4160 class Session_ACLPageInfo {
  4195 class Session_ACLPageInfo {
  4161 	
  4196 	
  4162 	/**
  4197 	/**
  4163  	* The page ID of this ACL info package
  4198 	 * The page ID of this ACL info package
  4164  	* @var string
  4199 	 * @var string
  4165  	*/
  4200 	 */
  4166  	
  4201 	 
  4167 	var $page_id;
  4202 	var $page_id;
  4168 	
  4203 	
  4169 	/**
  4204 	/**
  4170  	* The namespace of the page being checked
  4205 	 * The namespace of the page being checked
  4171  	* @var string
  4206 	 * @var string
  4172  	*/
  4207 	 */
  4173  	
  4208 	 
  4174 	var $namespace;
  4209 	var $namespace;
  4175 	
  4210 	
  4176 	/**
  4211 	/**
  4177  	* Our list of permission types.
  4212 	 * Our list of permission types.
  4178  	* @access private
  4213 	 * @access private
  4179  	* @var array
  4214 	 * @var array
  4180  	*/
  4215 	 */
  4181  	
  4216 	 
  4182 	var $acl_types = Array();
  4217 	var $acl_types = Array();
  4183 	
  4218 	
  4184 	/**
  4219 	/**
  4185  	* The list of descriptions for the permission types
  4220 	 * The list of descriptions for the permission types
  4186  	* @var array
  4221 	 * @var array
  4187  	*/
  4222 	 */
  4188  	
  4223 	 
  4189 	var $acl_descs = Array();
  4224 	var $acl_descs = Array();
  4190 	
  4225 	
  4191 	/**
  4226 	/**
  4192  	* A list of dependencies for ACL types.
  4227 	 * A list of dependencies for ACL types.
  4193  	* @var array
  4228 	 * @var array
  4194  	*/
  4229 	 */
  4195  	
  4230 	 
  4196 	var $acl_deps = Array();
  4231 	var $acl_deps = Array();
  4197 	
  4232 	
  4198 	/**
  4233 	/**
  4199  	* Our tell-all list of permissions. Do not even try to change this.
  4234 	 * Our tell-all list of permissions. Do not even try to change this.
  4200  	* @access private
  4235 	 * @access private
  4201  	* @var array
  4236 	 * @var array
  4202  	*/
  4237 	 */
  4203  	
  4238 	 
  4204 	var $perms = Array();
  4239 	var $perms = Array();
  4205 	
  4240 	
  4206 	/**
  4241 	/**
  4207  	* Array to track which default permissions are being used
  4242 	 * Array to track which default permissions are being used
  4208  	* @var array
  4243 	 * @var array
  4209  	* @access private
  4244 	 * @access private
  4210  	*/
  4245 	 */
  4211  	
  4246 	 
  4212 	var $acl_defaults_used = Array();
  4247 	var $acl_defaults_used = Array();
  4213 	
  4248 	
  4214 	/**
  4249 	/**
  4215  	* Tracks whether Wiki Mode is on for the page we're operating on.
  4250 	 * Tracks whether Wiki Mode is on for the page we're operating on.
  4216  	* @var bool
  4251 	 * @var bool
  4217  	*/
  4252 	 */
  4218 	
  4253 	
  4219 	var $wiki_mode = false;
  4254 	var $wiki_mode = false;
  4220 	
  4255 	
  4221 	/**
  4256 	/**
  4222  	* Tracks where permissions were calculated using the ACL_INHERIT_* constants. Layout:
  4257 	 * Tracks where permissions were calculated using the ACL_INHERIT_* constants. Layout:
  4223  	* array(
  4258 	 * array(
  4224  	*   [permission_name] => array(
  4259 	 *   [permission_name] => array(
  4225  	*       [src] => ACL_INHERIT_*
  4260 	 *       [src] => ACL_INHERIT_*
  4226  	*       [rule_id] => integer
  4261 	 *       [rule_id] => integer
  4227  	*     ),
  4262 	 *     ),
  4228  	*   ...
  4263 	 *   ...
  4229  	* )
  4264 	 * )
  4230  	*
  4265 	 *
  4231  	* @var array
  4266 	 * @var array
  4232  	*/
  4267 	 */
  4233 	
  4268 	
  4234 	var $perm_resolve_table = array();
  4269 	var $perm_resolve_table = array();
  4235 	
  4270 	
  4236 	#
  4271 	#
  4237 	# USER PARAMETERS
  4272 	# USER PARAMETERS
  4238 	#
  4273 	#
  4239 	
  4274 	
  4240 	/**
  4275 	/**
  4241  	* User ID
  4276 	 * User ID
  4242  	* @var int
  4277 	 * @var int
  4243  	*/
  4278 	 */
  4244 	
  4279 	
  4245 	var $user_id = 1;
  4280 	var $user_id = 1;
  4246 	
  4281 	
  4247 	/**
  4282 	/**
  4248  	* Group membership associative array (group_id => group_name)
  4283 	 * Group membership associative array (group_id => group_name)
  4249  	* @var array
  4284 	 * @var array
  4250  	*/
  4285 	 */
  4251 	
  4286 	
  4252 	var $groups = array();
  4287 	var $groups = array();
  4253 	
  4288 	
  4254 	/**
  4289 	/**
  4255  	* Constructor.
  4290 	 * Constructor.
  4256  	* @param string $page_id The ID of the page to check
  4291 	 * @param string $page_id The ID of the page to check
  4257  	* @param string $namespace The namespace of the page to check.
  4292 	 * @param string $namespace The namespace of the page to check.
  4258  	* @param array $acl_types List of ACL types
  4293 	 * @param array $acl_types List of ACL types
  4259  	* @param array $acl_descs List of human-readable descriptions for permissions (associative)
  4294 	 * @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.
  4295 	 * @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.
  4296 	 * @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
  4297 	 * @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.
  4298 	 * @param array $resolve_table Debugging info for tracking where rules came from, defaults to a blank array.
  4264  	*/
  4299 	 */
  4265  	
  4300 	 
  4266 	function __construct($page_id, $namespace, $acl_types, $acl_descs, $acl_deps, $base, $user_id = null, $groups = null, $resolve_table = array())
  4301 	function __construct($page_id, $namespace, $acl_types, $acl_descs, $acl_deps, $base, $user_id = null, $groups = null, $resolve_table = array())
  4267 	{
  4302 	{
  4268 		global $db, $session, $paths, $template, $plugins; // Common objects
  4303 		global $db, $session, $paths, $template, $plugins; // Common objects
  4269 		
  4304 		
  4270 		// hack
  4305 		// hack
  4311 		
  4346 		
  4312 		$this->__calculate();
  4347 		$this->__calculate();
  4313 	}
  4348 	}
  4314 	
  4349 	
  4315 	/**
  4350 	/**
  4316  	* Performs the actual permission calculation.
  4351 	 * Performs the actual permission calculation.
  4317  	* @access private
  4352 	 * @access private
  4318  	*/
  4353 	 */
  4319 	
  4354 	
  4320 	private function __calculate()
  4355 	private function __calculate()
  4321 	{
  4356 	{
  4322 		global $db, $session, $paths, $template, $plugins; // Common objects
  4357 		global $db, $session, $paths, $template, $plugins; // Common objects
  4323 		
  4358 		
  4436 			return false;
  4471 			return false;
  4437 		}
  4472 		}
  4438 	}
  4473 	}
  4439 	
  4474 	
  4440 	/**
  4475 	/**
  4441  	* Tells us whether permission $type is allowed or not based on the current rules.
  4476 	 * 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())
  4477 	 * @param string $type The permission identifier ($acl_type passed to sessionManager::register_acl_type())
  4443  	* @param bool $no_deps If true, disables dependency checking
  4478 	 * @param bool $no_deps If true, disables dependency checking
  4444  	* @return bool True if allowed, false if denied or if an error occured
  4479 	 * @return bool True if allowed, false if denied or if an error occured
  4445  	*/
  4480 	 */
  4446  	
  4481 	 
  4447 	function get_permissions($type, $no_deps = false)
  4482 	function get_permissions($type, $no_deps = false)
  4448 	{
  4483 	{
  4449 		// echo '<pre>' . print_r($this->perms, true) . '</pre>';
  4484 		// echo '<pre>' . print_r($this->perms, true) . '</pre>';
  4450 		global $db, $session, $paths, $template, $plugins; // Common objects
  4485 		global $db, $session, $paths, $template, $plugins; // Common objects
  4451 		
  4486 		
  4514 		}
  4549 		}
  4515 		return $ret;
  4550 		return $ret;
  4516 	}
  4551 	}
  4517 	
  4552 	
  4518 	/**
  4553 	/**
  4519  	* Tell us if the dependencies for a given permission are met.
  4554 	 * Tell us if the dependencies for a given permission are met.
  4520  	* @param string The ACL permission ID
  4555 	 * @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
  4556 	 * @param bool If true, does not return a boolean value, but instead returns array of dependencies that fail
  4522  	* @return bool
  4557 	 * @return bool
  4523  	*/
  4558 	 */
  4524  	
  4559 	 
  4525 	function acl_check_deps($type, $debug = false)
  4560 	function acl_check_deps($type, $debug = false)
  4526 	{
  4561 	{
  4527 		// This will only happen if the permissions table is hacked or improperly accessed
  4562 		// This will only happen if the permissions table is hacked or improperly accessed
  4528 		if(!isset($this->acl_deps[$type]))
  4563 		if(!isset($this->acl_deps[$type]))
  4529 			return $debug ? array() : true;
  4564 			return $debug ? array() : true;
  4570 		}
  4605 		}
  4571 		return $debug ? $debugdata : true;
  4606 		return $debug ? $debugdata : true;
  4572 	}
  4607 	}
  4573 	
  4608 	
  4574 	/**
  4609 	/**
  4575  	* Merges the ACL array sent with the current permissions table, deciding precedence based on whether defaults are in effect or not.
  4610 	 * 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
  4611 	 * @param array The array to merge into the master ACL list
  4577  	* @param bool If true, $perm is treated as the "new default"
  4612 	 * @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.
  4613 	 * @param int 1 if this is a site-wide ACL, 2 if page-specific. Defaults to 2.
  4579  	*/
  4614 	 */
  4580 	
  4615 	
  4581 	function acl_merge_with_current($perm, $is_everyone = false, $scope = 2)
  4616 	function acl_merge_with_current($perm, $is_everyone = false, $scope = 2)
  4582 	{
  4617 	{
  4583 		foreach ( $this->perms as $i => $p )
  4618 		foreach ( $this->perms as $i => $p )
  4584 		{
  4619 		{