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