phpBB
Statistics
| Revision:

root / branches / phpBB-3_0_0 / phpBB / includes / session.php

History | View | Annotate | Download (76.4 kB)

1
<?php
2
/**
3
*
4
* @package phpBB3
5
* @version $Id: session.php 11643 2011-12-25 14:15:11Z git-gate $
6
* @copyright (c) 2005 phpBB Group
7
* @license http://opensource.org/licenses/gpl-license.php GNU Public License
8
*
9
*/
10
11
/**
12
* @ignore
13
*/
14
if (!defined('IN_PHPBB'))
15
{
16
        exit;
17
}
18
19
/**
20
* Session class
21
* @package phpBB3
22
*/
23
class session
24
{
25
        var $cookie_data = array();
26
        var $page = array();
27
        var $data = array();
28
        var $browser = '';
29
        var $forwarded_for = '';
30
        var $host = '';
31
        var $session_id = '';
32
        var $ip = '';
33
        var $load = 0;
34
        var $time_now = 0;
35
        var $update_session_page = true;
36
37
        /**
38
        * Extract current session page
39
        *
40
        * @param string $root_path current root path (phpbb_root_path)
41
        */
42
        function extract_current_page($root_path)
43
        {
44
                $page_array = array();
45
46
                // First of all, get the request uri...
47
                $script_name = (!empty($_SERVER['PHP_SELF'])) ? $_SERVER['PHP_SELF'] : getenv('PHP_SELF');
48
                $args = (!empty($_SERVER['QUERY_STRING'])) ? explode('&', $_SERVER['QUERY_STRING']) : explode('&', getenv('QUERY_STRING'));
49
50
                // If we are unable to get the script name we use REQUEST_URI as a failover and note it within the page array for easier support...
51
                if (!$script_name)
52
                {
53
                        $script_name = (!empty($_SERVER['REQUEST_URI'])) ? $_SERVER['REQUEST_URI'] : getenv('REQUEST_URI');
54
                        $script_name = (($pos = strpos($script_name, '?')) !== false) ? substr($script_name, 0, $pos) : $script_name;
55
                        $page_array['failover'] = 1;
56
                }
57
58
                // Replace backslashes and doubled slashes (could happen on some proxy setups)
59
                $script_name = str_replace(array('\\', '//'), '/', $script_name);
60
61
                // Now, remove the sid and let us get a clean query string...
62
                $use_args = array();
63
64
                // Since some browser do not encode correctly we need to do this with some "special" characters...
65
                // " -> %22, ' => %27, < -> %3C, > -> %3E
66
                $find = array('"', "'", '<', '>');
67
                $replace = array('%22', '%27', '%3C', '%3E');
68
69
                foreach ($args as $key => $argument)
70
                {
71
                        if (strpos($argument, 'sid=') === 0)
72
                        {
73
                                continue;
74
                        }
75
76
                        $use_args[] = str_replace($find, $replace, $argument);
77
                }
78
                unset($args);
79
80
                // The following examples given are for an request uri of {path to the phpbb directory}/adm/index.php?i=10&b=2
81
82
                // The current query string
83
                $query_string = trim(implode('&', $use_args));
84
85
                // basenamed page name (for example: index.php)
86
                $page_name = (substr($script_name, -1, 1) == '/') ? '' : basename($script_name);
87
                $page_name = urlencode(htmlspecialchars($page_name));
88
89
                // current directory within the phpBB root (for example: adm)
90
                $root_dirs = explode('/', str_replace('\\', '/', phpbb_realpath($root_path)));
91
                $page_dirs = explode('/', str_replace('\\', '/', phpbb_realpath('./')));
92
                $intersection = array_intersect_assoc($root_dirs, $page_dirs);
93
94
                $root_dirs = array_diff_assoc($root_dirs, $intersection);
95
                $page_dirs = array_diff_assoc($page_dirs, $intersection);
96
97
                $page_dir = str_repeat('../', sizeof($root_dirs)) . implode('/', $page_dirs);
98
99
                if ($page_dir && substr($page_dir, -1, 1) == '/')
100
                {
101
                        $page_dir = substr($page_dir, 0, -1);
102
                }
103
104
                // Current page from phpBB root (for example: adm/index.php?i=10&b=2)
105
                $page = (($page_dir) ? $page_dir . '/' : '') . $page_name . (($query_string) ? "?$query_string" : '');
106
107
                // The script path from the webroot to the current directory (for example: /phpBB3/adm/) : always prefixed with / and ends in /
108
                $script_path = trim(str_replace('\\', '/', dirname($script_name)));
109
110
                // The script path from the webroot to the phpBB root (for example: /phpBB3/)
111
                $script_dirs = explode('/', $script_path);
112
                array_splice($script_dirs, -sizeof($page_dirs));
113
                $root_script_path = implode('/', $script_dirs) . (sizeof($root_dirs) ? '/' . implode('/', $root_dirs) : '');
114
115
                // We are on the base level (phpBB root == webroot), lets adjust the variables a bit...
116
                if (!$root_script_path)
117
                {
118
                        $root_script_path = ($page_dir) ? str_replace($page_dir, '', $script_path) : $script_path;
119
                }
120
121
                $script_path .= (substr($script_path, -1, 1) == '/') ? '' : '/';
122
                $root_script_path .= (substr($root_script_path, -1, 1) == '/') ? '' : '/';
123
124
                $page_array += array(
125
                        'page_name'                        => $page_name,
126
                        'page_dir'                        => $page_dir,
127
128
                        'query_string'                => $query_string,
129
                        'script_path'                => str_replace(' ', '%20', htmlspecialchars($script_path)),
130
                        'root_script_path'        => str_replace(' ', '%20', htmlspecialchars($root_script_path)),
131
132
                        'page'                                => $page,
133
                        'forum'                                => (isset($_REQUEST['f']) && $_REQUEST['f'] > 0) ? (int) $_REQUEST['f'] : 0,
134
                );
135
136
                return $page_array;
137
        }
138
139
        /**
140
        * Get valid hostname/port. HTTP_HOST is used, SERVER_NAME if HTTP_HOST not present.
141
        */
142
        function extract_current_hostname()
143
        {
144
                global $config;
145
146
                // Get hostname
147
                $host = (!empty($_SERVER['HTTP_HOST'])) ? $_SERVER['HTTP_HOST'] : ((!empty($_SERVER['SERVER_NAME'])) ? $_SERVER['SERVER_NAME'] : getenv('SERVER_NAME'));
148
149
                // Should be a string and lowered
150
                $host = (string) strtolower($host);
151
152
                // If host is equal the cookie domain or the server name (if config is set), then we assume it is valid
153
                if ((isset($config['cookie_domain']) && $host === $config['cookie_domain']) || (isset($config['server_name']) && $host === $config['server_name']))
154
                {
155
                        return $host;
156
                }
157
158
                // Is the host actually a IP? If so, we use the IP... (IPv4)
159
                if (long2ip(ip2long($host)) === $host)
160
                {
161
                        return $host;
162
                }
163
164
                // Now return the hostname (this also removes any port definition). The http:// is prepended to construct a valid URL, hosts never have a scheme assigned
165
                $host = @parse_url('http://' . $host);
166
                $host = (!empty($host['host'])) ? $host['host'] : '';
167
168
                // Remove any portions not removed by parse_url (#)
169
                $host = str_replace('#', '', $host);
170
171
                // If, by any means, the host is now empty, we will use a "best approach" way to guess one
172
                if (empty($host))
173
                {
174
                        if (!empty($config['server_name']))
175
                        {
176
                                $host = $config['server_name'];
177
                        }
178
                        else if (!empty($config['cookie_domain']))
179
                        {
180
                                $host = (strpos($config['cookie_domain'], '.') === 0) ? substr($config['cookie_domain'], 1) : $config['cookie_domain'];
181
                        }
182
                        else
183
                        {
184
                                // Set to OS hostname or localhost
185
                                $host = (function_exists('php_uname')) ? php_uname('n') : 'localhost';
186
                        }
187
                }
188
189
                // It may be still no valid host, but for sure only a hostname (we may further expand on the cookie domain... if set)
190
                return $host;
191
        }
192
193
        /**
194
        * Start session management
195
        *
196
        * This is where all session activity begins. We gather various pieces of
197
        * information from the client and server. We test to see if a session already
198
        * exists. If it does, fine and dandy. If it doesn't we'll go on to create a
199
        * new one ... pretty logical heh? We also examine the system load (if we're
200
        * running on a system which makes such information readily available) and
201
        * halt if it's above an admin definable limit.
202
        *
203
        * @param bool $update_session_page if true the session page gets updated.
204
        *                        This can be set to circumvent certain scripts to update the users last visited page.
205
        */
206
        function session_begin($update_session_page = true)
207
        {
208
                global $phpEx, $SID, $_SID, $_EXTRA_URL, $db, $config, $phpbb_root_path;
209
210
                // Give us some basic information
211
                $this->time_now                                = time();
212
                $this->cookie_data                        = array('u' => 0, 'k' => '');
213
                $this->update_session_page        = $update_session_page;
214
                $this->browser                                = (!empty($_SERVER['HTTP_USER_AGENT'])) ? htmlspecialchars((string) $_SERVER['HTTP_USER_AGENT']) : '';
215
                $this->referer                                = (!empty($_SERVER['HTTP_REFERER'])) ? htmlspecialchars((string) $_SERVER['HTTP_REFERER']) : '';
216
                $this->forwarded_for                = (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) ? htmlspecialchars((string) $_SERVER['HTTP_X_FORWARDED_FOR']) : '';
217
218
                $this->host                                        = $this->extract_current_hostname();
219
                $this->page                                        = $this->extract_current_page($phpbb_root_path);
220
221
                // if the forwarded for header shall be checked we have to validate its contents
222
                if ($config['forwarded_for_check'])
223
                {
224
                        $this->forwarded_for = preg_replace('# {2,}#', ' ', str_replace(',', ' ', $this->forwarded_for));
225
226
                        // split the list of IPs
227
                        $ips = explode(' ', $this->forwarded_for);
228
                        foreach ($ips as $ip)
229
                        {
230
                                // check IPv4 first, the IPv6 is hopefully only going to be used very seldomly
231
                                if (!empty($ip) && !preg_match(get_preg_expression('ipv4'), $ip) && !preg_match(get_preg_expression('ipv6'), $ip))
232
                                {
233
                                        // contains invalid data, don't use the forwarded for header
234
                                        $this->forwarded_for = '';
235
                                        break;
236
                                }
237
                        }
238
                }
239
                else
240
                {
241
                        $this->forwarded_for = '';
242
                }
243
244
                if (isset($_COOKIE[$config['cookie_name'] . '_sid']) || isset($_COOKIE[$config['cookie_name'] . '_u']))
245
                {
246
                        $this->cookie_data['u'] = request_var($config['cookie_name'] . '_u', 0, false, true);
247
                        $this->cookie_data['k'] = request_var($config['cookie_name'] . '_k', '', false, true);
248
                        $this->session_id                 = request_var($config['cookie_name'] . '_sid', '', false, true);
249
250
                        $SID = (defined('NEED_SID')) ? '?sid=' . $this->session_id : '?sid=';
251
                        $_SID = (defined('NEED_SID')) ? $this->session_id : '';
252
253
                        if (empty($this->session_id))
254
                        {
255
                                $this->session_id = $_SID = request_var('sid', '');
256
                                $SID = '?sid=' . $this->session_id;
257
                                $this->cookie_data = array('u' => 0, 'k' => '');
258
                        }
259
                }
260
                else
261
                {
262
                        $this->session_id = $_SID = request_var('sid', '');
263
                        $SID = '?sid=' . $this->session_id;
264
                }
265
266
                $_EXTRA_URL = array();
267
268
                // Why no forwarded_for et al? Well, too easily spoofed. With the results of my recent requests
269
                // it's pretty clear that in the majority of cases you'll at least be left with a proxy/cache ip.
270
                $this->ip = (!empty($_SERVER['REMOTE_ADDR'])) ? (string) $_SERVER['REMOTE_ADDR'] : '';
271
                $this->ip = preg_replace('# {2,}#', ' ', str_replace(',', ' ', $this->ip));
272
273
                // split the list of IPs
274
                $ips = explode(' ', trim($this->ip));
275
276
                // Default IP if REMOTE_ADDR is invalid
277
                $this->ip = '127.0.0.1';
278
279
                foreach ($ips as $ip)
280
                {
281
                        if (preg_match(get_preg_expression('ipv4'), $ip))
282
                        {
283
                                $this->ip = $ip;
284
                        }
285
                        else if (preg_match(get_preg_expression('ipv6'), $ip))
286
                        {
287
                                // Quick check for IPv4-mapped address in IPv6
288
                                if (stripos($ip, '::ffff:') === 0)
289
                                {
290
                                        $ipv4 = substr($ip, 7);
291
292
                                        if (preg_match(get_preg_expression('ipv4'), $ipv4))
293
                                        {
294
                                                $ip = $ipv4;
295
                                        }
296
                                }
297
298
                                $this->ip = $ip;
299
                        }
300
                        else
301
                        {
302
                                // We want to use the last valid address in the chain
303
                                // Leave foreach loop when address is invalid
304
                                break;
305
                        }
306
                }
307
308
                $this->load = false;
309
310
                // Load limit check (if applicable)
311
                if ($config['limit_load'] || $config['limit_search_load'])
312
                {
313
                        if ((function_exists('sys_getloadavg') && $load = sys_getloadavg()) || ($load = explode(' ', @file_get_contents('/proc/loadavg'))))
314
                        {
315
                                $this->load = array_slice($load, 0, 1);
316
                                $this->load = floatval($this->load[0]);
317
                        }
318
                        else
319
                        {
320
                                set_config('limit_load', '0');
321
                                set_config('limit_search_load', '0');
322
                        }
323
                }
324
325
                // Is session_id is set or session_id is set and matches the url param if required
326
                if (!empty($this->session_id) && (!defined('NEED_SID') || (isset($_GET['sid']) && $this->session_id === $_GET['sid'])))
327
                {
328
                        $sql = 'SELECT u.*, s.*
329
                                FROM ' . SESSIONS_TABLE . ' s, ' . USERS_TABLE . " u
330
                                WHERE s.session_id = '" . $db->sql_escape($this->session_id) . "'
331
                                        AND u.user_id = s.session_user_id";
332
                        $result = $db->sql_query($sql);
333
                        $this->data = $db->sql_fetchrow($result);
334
                        $db->sql_freeresult($result);
335
336
                        // Did the session exist in the DB?
337
                        if (isset($this->data['user_id']))
338
                        {
339
                                // Validate IP length according to admin ... enforces an IP
340
                                // check on bots if admin requires this
341
//                                $quadcheck = ($config['ip_check_bot'] && $this->data['user_type'] & USER_BOT) ? 4 : $config['ip_check'];
342
343
                                if (strpos($this->ip, ':') !== false && strpos($this->data['session_ip'], ':') !== false)
344
                                {
345
                                        $s_ip = short_ipv6($this->data['session_ip'], $config['ip_check']);
346
                                        $u_ip = short_ipv6($this->ip, $config['ip_check']);
347
                                }
348
                                else
349
                                {
350
                                        $s_ip = implode('.', array_slice(explode('.', $this->data['session_ip']), 0, $config['ip_check']));
351
                                        $u_ip = implode('.', array_slice(explode('.', $this->ip), 0, $config['ip_check']));
352
                                }
353
354
                                $s_browser = ($config['browser_check']) ? trim(strtolower(substr($this->data['session_browser'], 0, 149))) : '';
355
                                $u_browser = ($config['browser_check']) ? trim(strtolower(substr($this->browser, 0, 149))) : '';
356
357
                                $s_forwarded_for = ($config['forwarded_for_check']) ? substr($this->data['session_forwarded_for'], 0, 254) : '';
358
                                $u_forwarded_for = ($config['forwarded_for_check']) ? substr($this->forwarded_for, 0, 254) : '';
359
360
                                // referer checks
361
                                // The @ before $config['referer_validation'] suppresses notices present while running the updater
362
                                $check_referer_path = (@$config['referer_validation'] == REFERER_VALIDATE_PATH);
363
                                $referer_valid = true;
364
365
                                // we assume HEAD and TRACE to be foul play and thus only whitelist GET
366
                                if (@$config['referer_validation'] && isset($_SERVER['REQUEST_METHOD']) && strtolower($_SERVER['REQUEST_METHOD']) !== 'get')
367
                                {
368
                                        $referer_valid = $this->validate_referer($check_referer_path);
369
                                }
370
371
                                if ($u_ip === $s_ip && $s_browser === $u_browser && $s_forwarded_for === $u_forwarded_for && $referer_valid)
372
                                {
373
                                        $session_expired = false;
374
375
                                        // Check whether the session is still valid if we have one
376
                                        $method = basename(trim($config['auth_method']));
377
                                        include_once($phpbb_root_path . 'includes/auth/auth_' . $method . '.' . $phpEx);
378
379
                                        $method = 'validate_session_' . $method;
380
                                        if (function_exists($method))
381
                                        {
382
                                                if (!$method($this->data))
383
                                                {
384
                                                        $session_expired = true;
385
                                                }
386
                                        }
387
388
                                        if (!$session_expired)
389
                                        {
390
                                                // Check the session length timeframe if autologin is not enabled.
391
                                                // Else check the autologin length... and also removing those having autologin enabled but no longer allowed board-wide.
392
                                                if (!$this->data['session_autologin'])
393
                                                {
394
                                                        if ($this->data['session_time'] < $this->time_now - ($config['session_length'] + 60))
395
                                                        {
396
                                                                $session_expired = true;
397
                                                        }
398
                                                }
399
                                                else if (!$config['allow_autologin'] || ($config['max_autologin_time'] && $this->data['session_time'] < $this->time_now - (86400 * (int) $config['max_autologin_time']) + 60))
400
                                                {
401
                                                        $session_expired = true;
402
                                                }
403
                                        }
404
405
                                        if (!$session_expired)
406
                                        {
407
                                                // Only update session DB a minute or so after last update or if page changes
408
                                                if ($this->time_now - $this->data['session_time'] > 60 || ($this->update_session_page && $this->data['session_page'] != $this->page['page']))
409
                                                {
410
                                                        $sql_ary = array('session_time' => $this->time_now);
411
412
                                                        if ($this->update_session_page)
413
                                                        {
414
                                                                $sql_ary['session_page'] = substr($this->page['page'], 0, 199);
415
                                                                $sql_ary['session_forum_id'] = $this->page['forum'];
416
                                                        }
417
418
                                                        $db->sql_return_on_error(true);
419
420
                                                        $sql = 'UPDATE ' . SESSIONS_TABLE . ' SET ' . $db->sql_build_array('UPDATE', $sql_ary) . "
421
                                                                WHERE session_id = '" . $db->sql_escape($this->session_id) . "'";
422
                                                        $result = $db->sql_query($sql);
423
424
                                                        $db->sql_return_on_error(false);
425
426
                                                        // If the database is not yet updated, there will be an error due to the session_forum_id
427
                                                        // @todo REMOVE for 3.0.2
428
                                                        if ($result === false)
429
                                                        {
430
                                                                unset($sql_ary['session_forum_id']);
431
432
                                                                $sql = 'UPDATE ' . SESSIONS_TABLE . ' SET ' . $db->sql_build_array('UPDATE', $sql_ary) . "
433
                                                                        WHERE session_id = '" . $db->sql_escape($this->session_id) . "'";
434
                                                                $db->sql_query($sql);
435
                                                        }
436
437
                                                        if ($this->data['user_id'] != ANONYMOUS && !empty($config['new_member_post_limit']) && $this->data['user_new'] && $config['new_member_post_limit'] <= $this->data['user_posts'])
438
                                                        {
439
                                                                $this->leave_newly_registered();
440
                                                        }
441
                                                }
442
443
                                                $this->data['is_registered'] = ($this->data['user_id'] != ANONYMOUS && ($this->data['user_type'] == USER_NORMAL || $this->data['user_type'] == USER_FOUNDER)) ? true : false;
444
                                                $this->data['is_bot'] = (!$this->data['is_registered'] && $this->data['user_id'] != ANONYMOUS) ? true : false;
445
                                                $this->data['user_lang'] = basename($this->data['user_lang']);
446
447
                                                return true;
448
                                        }
449
                                }
450
                                else
451
                                {
452
                                        // Added logging temporarly to help debug bugs...
453
                                        if (defined('DEBUG_EXTRA') && $this->data['user_id'] != ANONYMOUS)
454
                                        {
455
                                                if ($referer_valid)
456
                                                {
457
                                                        add_log('critical', 'LOG_IP_BROWSER_FORWARDED_CHECK', $u_ip, $s_ip, $u_browser, $s_browser, htmlspecialchars($u_forwarded_for), htmlspecialchars($s_forwarded_for));
458
                                                }
459
                                                else
460
                                                {
461
                                                        add_log('critical', 'LOG_REFERER_INVALID', $this->referer);
462
                                                }
463
                                        }
464
                                }
465
                        }
466
                }
467
468
                // If we reach here then no (valid) session exists. So we'll create a new one
469
                return $this->session_create();
470
        }
471
472
        /**
473
        * Create a new session
474
        *
475
        * If upon trying to start a session we discover there is nothing existing we
476
        * jump here. Additionally this method is called directly during login to regenerate
477
        * the session for the specific user. In this method we carry out a number of tasks;
478
        * garbage collection, (search)bot checking, banned user comparison. Basically
479
        * though this method will result in a new session for a specific user.
480
        */
481
        function session_create($user_id = false, $set_admin = false, $persist_login = false, $viewonline = true)
482
        {
483
                global $SID, $_SID, $db, $config, $cache, $phpbb_root_path, $phpEx;
484
485
                $this->data = array();
486
487
                /* Garbage collection ... remove old sessions updating user information
488
                // if necessary. It means (potentially) 11 queries but only infrequently
489
                if ($this->time_now > $config['session_last_gc'] + $config['session_gc'])
490
                {
491
                        $this->session_gc();
492
                }*/
493
494
                // Do we allow autologin on this board? No? Then override anything
495
                // that may be requested here
496
                if (!$config['allow_autologin'])
497
                {
498
                        $this->cookie_data['k'] = $persist_login = false;
499
                }
500
501
                /**
502
                * Here we do a bot check, oh er saucy! No, not that kind of bot
503
                * check. We loop through the list of bots defined by the admin and
504
                * see if we have any useragent and/or IP matches. If we do, this is a
505
                * bot, act accordingly
506
                */
507
                $bot = false;
508
                $active_bots = $cache->obtain_bots();
509
510
                foreach ($active_bots as $row)
511
                {
512
                        if ($row['bot_agent'] && preg_match('#' . str_replace('\*', '.*?', preg_quote($row['bot_agent'], '#')) . '#i', $this->browser))
513
                        {
514
                                $bot = $row['user_id'];
515
                        }
516
517
                        // If ip is supplied, we will make sure the ip is matching too...
518
                        if ($row['bot_ip'] && ($bot || !$row['bot_agent']))
519
                        {
520
                                // Set bot to false, then we only have to set it to true if it is matching
521
                                $bot = false;
522
523
                                foreach (explode(',', $row['bot_ip']) as $bot_ip)
524
                                {
525
                                        $bot_ip = trim($bot_ip);
526
527
                                        if (!$bot_ip)
528
                                        {
529
                                                continue;
530
                                        }
531
532
                                        if (strpos($this->ip, $bot_ip) === 0)
533
                                        {
534
                                                $bot = (int) $row['user_id'];
535
                                                break;
536
                                        }
537
                                }
538
                        }
539
540
                        if ($bot)
541
                        {
542
                                break;
543
                        }
544
                }
545
546
                $method = basename(trim($config['auth_method']));
547
                include_once($phpbb_root_path . 'includes/auth/auth_' . $method . '.' . $phpEx);
548
549
                $method = 'autologin_' . $method;
550
                if (function_exists($method))
551
                {
552
                        $this->data = $method();
553
554
                        if (sizeof($this->data))
555
                        {
556
                                $this->cookie_data['k'] = '';
557
                                $this->cookie_data['u'] = $this->data['user_id'];
558
                        }
559
                }
560
561
                // If we're presented with an autologin key we'll join against it.
562
                // Else if we've been passed a user_id we'll grab data based on that
563
                if (isset($this->cookie_data['k']) && $this->cookie_data['k'] && $this->cookie_data['u'] && !sizeof($this->data))
564
                {
565
                        $sql = 'SELECT u.*
566
                                FROM ' . USERS_TABLE . ' u, ' . SESSIONS_KEYS_TABLE . ' k
567
                                WHERE u.user_id = ' . (int) $this->cookie_data['u'] . '
568
                                        AND u.user_type IN (' . USER_NORMAL . ', ' . USER_FOUNDER . ")
569
                                        AND k.user_id = u.user_id
570
                                        AND k.key_id = '" . $db->sql_escape(md5($this->cookie_data['k'])) . "'";
571
                        $result = $db->sql_query($sql);
572
                        $this->data = $db->sql_fetchrow($result);
573
                        $db->sql_freeresult($result);
574
                        $bot = false;
575
                }
576
                else if ($user_id !== false && !sizeof($this->data))
577
                {
578
                        $this->cookie_data['k'] = '';
579
                        $this->cookie_data['u'] = $user_id;
580
581
                        $sql = 'SELECT *
582
                                FROM ' . USERS_TABLE . '
583
                                WHERE user_id = ' . (int) $this->cookie_data['u'] . '
584
                                        AND user_type IN (' . USER_NORMAL . ', ' . USER_FOUNDER . ')';
585
                        $result = $db->sql_query($sql);
586
                        $this->data = $db->sql_fetchrow($result);
587
                        $db->sql_freeresult($result);
588
                        $bot = false;
589
                }
590
591
                // Bot user, if they have a SID in the Request URI we need to get rid of it
592
                // otherwise they'll index this page with the SID, duplicate content oh my!
593
                if ($bot && isset($_GET['sid']))
594
                {
595
                        send_status_line(301, 'Moved Permanently');
596
                        redirect(build_url(array('sid')));
597
                }
598
599
                // If no data was returned one or more of the following occurred:
600
                // Key didn't match one in the DB
601
                // User does not exist
602
                // User is inactive
603
                // User is bot
604
                if (!sizeof($this->data) || !is_array($this->data))
605
                {
606
                        $this->cookie_data['k'] = '';
607
                        $this->cookie_data['u'] = ($bot) ? $bot : ANONYMOUS;
608
609
                        if (!$bot)
610
                        {
611
                                $sql = 'SELECT *
612
                                        FROM ' . USERS_TABLE . '
613
                                        WHERE user_id = ' . (int) $this->cookie_data['u'];
614
                        }
615
                        else
616
                        {
617
                                // We give bots always the same session if it is not yet expired.
618
                                $sql = 'SELECT u.*, s.*
619
                                        FROM ' . USERS_TABLE . ' u
620
                                        LEFT JOIN ' . SESSIONS_TABLE . ' s ON (s.session_user_id = u.user_id)
621
                                        WHERE u.user_id = ' . (int) $bot;
622
                        }
623
624
                        $result = $db->sql_query($sql);
625
                        $this->data = $db->sql_fetchrow($result);
626
                        $db->sql_freeresult($result);
627
                }
628
629
                if ($this->data['user_id'] != ANONYMOUS && !$bot)
630
                {
631
                        $this->data['session_last_visit'] = (isset($this->data['session_time']) && $this->data['session_time']) ? $this->data['session_time'] : (($this->data['user_lastvisit']) ? $this->data['user_lastvisit'] : time());
632
                }
633
                else
634
                {
635
                        $this->data['session_last_visit'] = $this->time_now;
636
                }
637
638
                // Force user id to be integer...
639
                $this->data['user_id'] = (int) $this->data['user_id'];
640
641
                // At this stage we should have a filled data array, defined cookie u and k data.
642
                // data array should contain recent session info if we're a real user and a recent
643
                // session exists in which case session_id will also be set
644
645
                // Is user banned? Are they excluded? Won't return on ban, exists within method
646
                if ($this->data['user_type'] != USER_FOUNDER)
647
                {
648
                        if (!$config['forwarded_for_check'])
649
                        {
650
                                $this->check_ban($this->data['user_id'], $this->ip);
651
                        }
652
                        else
653
                        {
654
                                $ips = explode(' ', $this->forwarded_for);
655
                                $ips[] = $this->ip;
656
                                $this->check_ban($this->data['user_id'], $ips);
657
                        }
658
                }
659
660
                $this->data['is_registered'] = (!$bot && $this->data['user_id'] != ANONYMOUS && ($this->data['user_type'] == USER_NORMAL || $this->data['user_type'] == USER_FOUNDER)) ? true : false;
661
                $this->data['is_bot'] = ($bot) ? true : false;
662
663
                // If our friend is a bot, we re-assign a previously assigned session
664
                if ($this->data['is_bot'] && $bot == $this->data['user_id'] && $this->data['session_id'])
665
                {
666
                        // Only assign the current session if the ip, browser and forwarded_for match...
667
                        if (strpos($this->ip, ':') !== false && strpos($this->data['session_ip'], ':') !== false)
668
                        {
669
                                $s_ip = short_ipv6($this->data['session_ip'], $config['ip_check']);
670
                                $u_ip = short_ipv6($this->ip, $config['ip_check']);
671
                        }
672
                        else
673
                        {
674
                                $s_ip = implode('.', array_slice(explode('.', $this->data['session_ip']), 0, $config['ip_check']));
675
                                $u_ip = implode('.', array_slice(explode('.', $this->ip), 0, $config['ip_check']));
676
                        }
677
678
                        $s_browser = ($config['browser_check']) ? trim(strtolower(substr($this->data['session_browser'], 0, 149))) : '';
679
                        $u_browser = ($config['browser_check']) ? trim(strtolower(substr($this->browser, 0, 149))) : '';
680
681
                        $s_forwarded_for = ($config['forwarded_for_check']) ? substr($this->data['session_forwarded_for'], 0, 254) : '';
682
                        $u_forwarded_for = ($config['forwarded_for_check']) ? substr($this->forwarded_for, 0, 254) : '';
683
684
                        if ($u_ip === $s_ip && $s_browser === $u_browser && $s_forwarded_for === $u_forwarded_for)
685
                        {
686
                                $this->session_id = $this->data['session_id'];
687
688
                                // Only update session DB a minute or so after last update or if page changes
689
                                if ($this->time_now - $this->data['session_time'] > 60 || ($this->update_session_page && $this->data['session_page'] != $this->page['page']))
690
                                {
691
                                        $this->data['session_time'] = $this->data['session_last_visit'] = $this->time_now;
692
693
                                        $sql_ary = array('session_time' => $this->time_now, 'session_last_visit' => $this->time_now, 'session_admin' => 0);
694
695
                                        if ($this->update_session_page)
696
                                        {
697
                                                $sql_ary['session_page'] = substr($this->page['page'], 0, 199);
698
                                                $sql_ary['session_forum_id'] = $this->page['forum'];
699
                                        }
700
701
                                        $sql = 'UPDATE ' . SESSIONS_TABLE . ' SET ' . $db->sql_build_array('UPDATE', $sql_ary) . "
702
                                                WHERE session_id = '" . $db->sql_escape($this->session_id) . "'";
703
                                        $db->sql_query($sql);
704
705
                                        // Update the last visit time
706
                                        $sql = 'UPDATE ' . USERS_TABLE . '
707
                                                SET user_lastvisit = ' . (int) $this->data['session_time'] . '
708
                                                WHERE user_id = ' . (int) $this->data['user_id'];
709
                                        $db->sql_query($sql);
710
                                }
711
712
                                $SID = '?sid=';
713
                                $_SID = '';
714
                                return true;
715
                        }
716
                        else
717
                        {
718
                                // If the ip and browser does not match make sure we only have one bot assigned to one session
719
                                $db->sql_query('DELETE FROM ' . SESSIONS_TABLE . ' WHERE session_user_id = ' . $this->data['user_id']);
720
                        }
721
                }
722
723
                $session_autologin = (($this->cookie_data['k'] || $persist_login) && $this->data['is_registered']) ? true : false;
724
                $set_admin = ($set_admin && $this->data['is_registered']) ? true : false;
725
726
                // Create or update the session
727
                $sql_ary = array(
728
                        'session_user_id'                => (int) $this->data['user_id'],
729
                        'session_start'                        => (int) $this->time_now,
730
                        'session_last_visit'        => (int) $this->data['session_last_visit'],
731
                        'session_time'                        => (int) $this->time_now,
732
                        'session_browser'                => (string) trim(substr($this->browser, 0, 149)),
733
                        'session_forwarded_for'        => (string) $this->forwarded_for,
734
                        'session_ip'                        => (string) $this->ip,
735
                        'session_autologin'                => ($session_autologin) ? 1 : 0,
736
                        'session_admin'                        => ($set_admin) ? 1 : 0,
737
                        'session_viewonline'        => ($viewonline) ? 1 : 0,
738
                );
739
740
                if ($this->update_session_page)
741
                {
742
                        $sql_ary['session_page'] = (string) substr($this->page['page'], 0, 199);
743
                        $sql_ary['session_forum_id'] = $this->page['forum'];
744
                }
745
746
                $db->sql_return_on_error(true);
747
748
                $sql = 'DELETE
749
                        FROM ' . SESSIONS_TABLE . '
750
                        WHERE session_id = \'' . $db->sql_escape($this->session_id) . '\'
751
                                AND session_user_id = ' . ANONYMOUS;
752
753
                if (!defined('IN_ERROR_HANDLER') && (!$this->session_id || !$db->sql_query($sql) || !$db->sql_affectedrows()))
754
                {
755
                        // Limit new sessions in 1 minute period (if required)
756
                        if (empty($this->data['session_time']) && $config['active_sessions'])
757
                        {
758
//                                $db->sql_return_on_error(false);
759
760
                                $sql = 'SELECT COUNT(session_id) AS sessions
761
                                        FROM ' . SESSIONS_TABLE . '
762
                                        WHERE session_time >= ' . ($this->time_now - 60);
763
                                $result = $db->sql_query($sql);
764
                                $row = $db->sql_fetchrow($result);
765
                                $db->sql_freeresult($result);
766
767
                                if ((int) $row['sessions'] > (int) $config['active_sessions'])
768
                                {
769
                                        send_status_line(503, 'Service Unavailable');
770
                                        trigger_error('BOARD_UNAVAILABLE');
771
                                }
772
                        }
773
                }
774
775
                // Since we re-create the session id here, the inserted row must be unique. Therefore, we display potential errors.
776
                // Commented out because it will not allow forums to update correctly
777
//                $db->sql_return_on_error(false);
778
779
                // Something quite important: session_page always holds the *last* page visited, except for the *first* visit.
780
                // We are not able to simply have an empty session_page btw, therefore we need to tell phpBB how to detect this special case.
781
                // If the session id is empty, we have a completely new one and will set an "identifier" here. This identifier is able to be checked later.
782
                if (empty($this->data['session_id']))
783
                {
784
                        // This is a temporary variable, only set for the very first visit
785
                        $this->data['session_created'] = true;
786
                }
787
788
                $this->session_id = $this->data['session_id'] = md5(unique_id());
789
790
                $sql_ary['session_id'] = (string) $this->session_id;
791
                $sql_ary['session_page'] = (string) substr($this->page['page'], 0, 199);
792
                $sql_ary['session_forum_id'] = $this->page['forum'];
793
794
                $sql = 'INSERT INTO ' . SESSIONS_TABLE . ' ' . $db->sql_build_array('INSERT', $sql_ary);
795
                $db->sql_query($sql);
796
797
                $db->sql_return_on_error(false);
798
799
                // Regenerate autologin/persistent login key
800
                if ($session_autologin)
801
                {
802
                        $this->set_login_key();
803
                }
804
805
                // refresh data
806
                $SID = '?sid=' . $this->session_id;
807
                $_SID = $this->session_id;
808
                $this->data = array_merge($this->data, $sql_ary);
809
810
                if (!$bot)
811
                {
812
                        $cookie_expire = $this->time_now + (($config['max_autologin_time']) ? 86400 * (int) $config['max_autologin_time'] : 31536000);
813
814
                        $this->set_cookie('u', $this->cookie_data['u'], $cookie_expire);
815
                        $this->set_cookie('k', $this->cookie_data['k'], $cookie_expire);
816
                        $this->set_cookie('sid', $this->session_id, $cookie_expire);
817
818
                        unset($cookie_expire);
819
820
                        $sql = 'SELECT COUNT(session_id) AS sessions
821
                                        FROM ' . SESSIONS_TABLE . '
822
                                        WHERE session_user_id = ' . (int) $this->data['user_id'] . '
823
                                        AND session_time >= ' . (int) ($this->time_now - (max($config['session_length'], $config['form_token_lifetime'])));
824
                        $result = $db->sql_query($sql);
825
                        $row = $db->sql_fetchrow($result);
826
                        $db->sql_freeresult($result);
827
828
                        if ((int) $row['sessions'] <= 1 || empty($this->data['user_form_salt']))
829
                        {
830
                                $this->data['user_form_salt'] = unique_id();
831
                                // Update the form key
832
                                $sql = 'UPDATE ' . USERS_TABLE . '
833
                                        SET user_form_salt = \'' . $db->sql_escape($this->data['user_form_salt']) . '\'
834
                                        WHERE user_id = ' . (int) $this->data['user_id'];
835
                                $db->sql_query($sql);
836
                        }
837
                }
838
                else
839
                {
840
                        $this->data['session_time'] = $this->data['session_last_visit'] = $this->time_now;
841
842
                        // Update the last visit time
843
                        $sql = 'UPDATE ' . USERS_TABLE . '
844
                                SET user_lastvisit = ' . (int) $this->data['session_time'] . '
845
                                WHERE user_id = ' . (int) $this->data['user_id'];
846
                        $db->sql_query($sql);
847
848
                        $SID = '?sid=';
849
                        $_SID = '';
850
                }
851
852
                return true;
853
        }
854
855
        /**
856
        * Kills a session
857
        *
858
        * This method does what it says on the tin. It will delete a pre-existing session.
859
        * It resets cookie information (destroying any autologin key within that cookie data)
860
        * and update the users information from the relevant session data. It will then
861
        * grab guest user information.
862
        */
863
        function session_kill($new_session = true)
864
        {
865
                global $SID, $_SID, $db, $config, $phpbb_root_path, $phpEx;
866
867
                $sql = 'DELETE FROM ' . SESSIONS_TABLE . "
868
                        WHERE session_id = '" . $db->sql_escape($this->session_id) . "'
869
                                AND session_user_id = " . (int) $this->data['user_id'];
870
                $db->sql_query($sql);
871
872
                // Allow connecting logout with external auth method logout
873
                $method = basename(trim($config['auth_method']));
874
                include_once($phpbb_root_path . 'includes/auth/auth_' . $method . '.' . $phpEx);
875
876
                $method = 'logout_' . $method;
877
                if (function_exists($method))
878
                {
879
                        $method($this->data, $new_session);
880
                }
881
882
                if ($this->data['user_id'] != ANONYMOUS)
883
                {
884
                        // Delete existing session, update last visit info first!
885
                        if (!isset($this->data['session_time']))
886
                        {
887
                                $this->data['session_time'] = time();
888
                        }
889
890
                        $sql = 'UPDATE ' . USERS_TABLE . '
891
                                SET user_lastvisit = ' . (int) $this->data['session_time'] . '
892
                                WHERE user_id = ' . (int) $this->data['user_id'];
893
                        $db->sql_query($sql);
894
895
                        if ($this->cookie_data['k'])
896
                        {
897
                                $sql = 'DELETE FROM ' . SESSIONS_KEYS_TABLE . '
898
                                        WHERE user_id = ' . (int) $this->data['user_id'] . "
899
                                                AND key_id = '" . $db->sql_escape(md5($this->cookie_data['k'])) . "'";
900
                                $db->sql_query($sql);
901
                        }
902
903
                        // Reset the data array
904
                        $this->data = array();
905
906
                        $sql = 'SELECT *
907
                                FROM ' . USERS_TABLE . '
908
                                WHERE user_id = ' . ANONYMOUS;
909
                        $result = $db->sql_query($sql);
910
                        $this->data = $db->sql_fetchrow($result);
911
                        $db->sql_freeresult($result);
912
                }
913
914
                $cookie_expire = $this->time_now - 31536000;
915
                $this->set_cookie('u', '', $cookie_expire);
916
                $this->set_cookie('k', '', $cookie_expire);
917
                $this->set_cookie('sid', '', $cookie_expire);
918
                unset($cookie_expire);
919
920
                $SID = '?sid=';
921
                $this->session_id = $_SID = '';
922
923
                // To make sure a valid session is created we create one for the anonymous user
924
                if ($new_session)
925
                {
926
                        $this->session_create(ANONYMOUS);
927
                }
928
929
                return true;
930
        }
931
932
        /**
933
        * Session garbage collection
934
        *
935
        * This looks a lot more complex than it really is. Effectively we are
936
        * deleting any sessions older than an admin definable limit. Due to the
937
        * way in which we maintain session data we have to ensure we update user
938
        * data before those sessions are destroyed. In addition this method
939
        * removes autologin key information that is older than an admin defined
940
        * limit.
941
        */
942
        function session_gc()
943
        {
944
                global $db, $config, $phpbb_root_path, $phpEx;
945
946
                $batch_size = 10;
947
948
                if (!$this->time_now)
949
                {
950
                        $this->time_now = time();
951
                }
952
953
                // Firstly, delete guest sessions
954
                $sql = 'DELETE FROM ' . SESSIONS_TABLE . '
955
                        WHERE session_user_id = ' . ANONYMOUS . '
956
                                AND session_time < ' . (int) ($this->time_now - $config['session_length']);
957
                $db->sql_query($sql);
958
959
                // Get expired sessions, only most recent for each user
960
                $sql = 'SELECT session_user_id, session_page, MAX(session_time) AS recent_time
961
                        FROM ' . SESSIONS_TABLE . '
962
                        WHERE session_time < ' . ($this->time_now - $config['session_length']) . '
963
                        GROUP BY session_user_id, session_page';
964
                $result = $db->sql_query_limit($sql, $batch_size);
965
966
                $del_user_id = array();
967
                $del_sessions = 0;
968
969
                while ($row = $db->sql_fetchrow($result))
970
                {
971
                        $sql = 'UPDATE ' . USERS_TABLE . '
972
                                SET user_lastvisit = ' . (int) $row['recent_time'] . ", user_lastpage = '" . $db->sql_escape($row['session_page']) . "'
973
                                WHERE user_id = " . (int) $row['session_user_id'];
974
                        $db->sql_query($sql);
975
976
                        $del_user_id[] = (int) $row['session_user_id'];
977
                        $del_sessions++;
978
                }
979
                $db->sql_freeresult($result);
980
981
                if (sizeof($del_user_id))
982
                {
983
                        // Delete expired sessions
984
                        $sql = 'DELETE FROM ' . SESSIONS_TABLE . '
985
                                WHERE ' . $db->sql_in_set('session_user_id', $del_user_id) . '
986
                                        AND session_time < ' . ($this->time_now - $config['session_length']);
987
                        $db->sql_query($sql);
988
                }
989
990
                if ($del_sessions < $batch_size)
991
                {
992
                        // Less than 10 users, update gc timer ... else we want gc
993
                        // called again to delete other sessions
994
                        set_config('session_last_gc', $this->time_now, true);
995
996
                        if ($config['max_autologin_time'])
997
                        {
998
                                $sql = 'DELETE FROM ' . SESSIONS_KEYS_TABLE . '
999
                                        WHERE last_login < ' . (time() - (86400 * (int) $config['max_autologin_time']));
1000
                                $db->sql_query($sql);
1001
                        }
1002
1003
                        // only called from CRON; should be a safe workaround until the infrastructure gets going
1004
                        if (!class_exists('phpbb_captcha_factory'))
1005
                        {
1006
                                include($phpbb_root_path . "includes/captcha/captcha_factory." . $phpEx);
1007
                        }
1008
                        phpbb_captcha_factory::garbage_collect($config['captcha_plugin']);
1009
1010
                        $sql = 'DELETE FROM ' . LOGIN_ATTEMPT_TABLE . '
1011
                                WHERE attempt_time < ' . (time() - (int) $config['ip_login_limit_time']);
1012
                        $db->sql_query($sql);
1013
                }
1014
1015
                return;
1016
        }
1017
1018
        /**
1019
        * Sets a cookie
1020
        *
1021
        * Sets a cookie of the given name with the specified data for the given length of time. If no time is specified, a session cookie will be set.
1022
        *
1023
        * @param string $name                Name of the cookie, will be automatically prefixed with the phpBB cookie name. track becomes [cookie_name]_track then.
1024
        * @param string $cookiedata        The data to hold within the cookie
1025
        * @param int $cookietime        The expiration time as UNIX timestamp. If 0 is provided, a session cookie is set.
1026
        */
1027
        function set_cookie($name, $cookiedata, $cookietime)
1028
        {
1029
                global $config;
1030
1031
                $name_data = rawurlencode($config['cookie_name'] . '_' . $name) . '=' . rawurlencode($cookiedata);
1032
                $expire = gmdate('D, d-M-Y H:i:s \\G\\M\\T', $cookietime);
1033
                $domain = (!$config['cookie_domain'] || $config['cookie_domain'] == 'localhost' || $config['cookie_domain'] == '127.0.0.1') ? '' : '; domain=' . $config['cookie_domain'];
1034
1035
                header('Set-Cookie: ' . $name_data . (($cookietime) ? '; expires=' . $expire : '') . '; path=' . $config['cookie_path'] . $domain . ((!$config['cookie_secure']) ? '' : '; secure') . '; HttpOnly', false);
1036
        }
1037
1038
        /**
1039
        * Check for banned user
1040
        *
1041
        * Checks whether the supplied user is banned by id, ip or email. If no parameters
1042
        * are passed to the method pre-existing session data is used. If $return is false
1043
        * this routine does not return on finding a banned user, it outputs a relevant
1044
        * message and stops execution.
1045
        *
1046
        * @param string|array        $user_ips        Can contain a string with one IP or an array of multiple IPs
1047
        */
1048
        function check_ban($user_id = false, $user_ips = false, $user_email = false, $return = false)
1049
        {
1050
                global $config, $db;
1051
1052
                if (defined('IN_CHECK_BAN'))
1053
                {
1054
                        return;
1055
                }
1056
1057
                $banned = false;
1058
                $cache_ttl = 3600;
1059
                $where_sql = array();
1060
1061
                $sql = 'SELECT ban_ip, ban_userid, ban_email, ban_exclude, ban_give_reason, ban_end
1062
                        FROM ' . BANLIST_TABLE . '
1063
                        WHERE ';
1064
1065
                // Determine which entries to check, only return those
1066
                if ($user_email === false)
1067
                {
1068
                        $where_sql[] = "ban_email = ''";
1069
                }
1070
1071
                if ($user_ips === false)
1072
                {
1073
                        $where_sql[] = "(ban_ip = '' OR ban_exclude = 1)";
1074
                }
1075
1076
                if ($user_id === false)
1077
                {
1078
                        $where_sql[] = '(ban_userid = 0 OR ban_exclude = 1)';
1079
                }
1080
                else
1081
                {
1082
                        $cache_ttl = ($user_id == ANONYMOUS) ? 3600 : 0;
1083
                        $_sql = '(ban_userid = ' . $user_id;
1084
1085
                        if ($user_email !== false)
1086
                        {
1087
                                $_sql .= " OR ban_email <> ''";
1088
                        }
1089
1090
                        if ($user_ips !== false)
1091
                        {
1092
                                $_sql .= " OR ban_ip <> ''";
1093
                        }
1094
1095
                        $_sql .= ')';
1096
1097
                        $where_sql[] = $_sql;
1098
                }
1099
1100
                $sql .= (sizeof($where_sql)) ? implode(' AND ', $where_sql) : '';
1101
                $result = $db->sql_query($sql, $cache_ttl);
1102
1103
                $ban_triggered_by = 'user';
1104
                while ($row = $db->sql_fetchrow($result))
1105
                {
1106
                        if ($row['ban_end'] && $row['ban_end'] < time())
1107
                        {
1108
                                continue;
1109
                        }
1110
1111
                        $ip_banned = false;
1112
                        if (!empty($row['ban_ip']))
1113
                        {
1114
                                if (!is_array($user_ips))
1115
                                {
1116
                                        $ip_banned = preg_match('#^' . str_replace('\*', '.*?', preg_quote($row['ban_ip'], '#')) . '$#i', $user_ips);
1117
                                }
1118
                                else
1119
                                {
1120
                                        foreach ($user_ips as $user_ip)
1121
                                        {
1122
                                                if (preg_match('#^' . str_replace('\*', '.*?', preg_quote($row['ban_ip'], '#')) . '$#i', $user_ip))
1123
                                                {
1124
                                                        $ip_banned = true;
1125
                                                        break;
1126
                                                }
1127
                                        }
1128
                                }
1129
                        }
1130
1131
                        if ((!empty($row['ban_userid']) && intval($row['ban_userid']) == $user_id) ||
1132
                                $ip_banned ||
1133
                                (!empty($row['ban_email']) && preg_match('#^' . str_replace('\*', '.*?', preg_quote($row['ban_email'], '#')) . '$#i', $user_email)))
1134
                        {
1135
                                if (!empty($row['ban_exclude']))
1136
                                {
1137
                                        $banned = false;
1138
                                        break;
1139
                                }
1140
                                else
1141
                                {
1142
                                        $banned = true;
1143
                                        $ban_row = $row;
1144
1145
                                        if (!empty($row['ban_userid']) && intval($row['ban_userid']) == $user_id)
1146
                                        {
1147
                                                $ban_triggered_by = 'user';
1148
                                        }
1149
                                        else if ($ip_banned)
1150
                                        {
1151
                                                $ban_triggered_by = 'ip';
1152
                                        }
1153
                                        else
1154
                                        {
1155
                                                $ban_triggered_by = 'email';
1156
                                        }
1157
1158
                                        // Don't break. Check if there is an exclude rule for this user
1159
                                }
1160
                        }
1161
                }
1162
                $db->sql_freeresult($result);
1163
1164
                if ($banned && !$return)
1165
                {
1166
                        global $template;
1167
1168
                        // If the session is empty we need to create a valid one...
1169
                        if (empty($this->session_id))
1170
                        {
1171
                                // This seems to be no longer needed? - #14971
1172
//                                $this->session_create(ANONYMOUS);
1173
                        }
1174
1175
                        // Initiate environment ... since it won't be set at this stage
1176
                        $this->setup();
1177
1178
                        // Logout the user, banned users are unable to use the normal 'logout' link
1179
                        if ($this->data['user_id'] != ANONYMOUS)
1180
                        {
1181
                                $this->session_kill();
1182
                        }
1183
1184
                        // We show a login box here to allow founders accessing the board if banned by IP
1185
                        if (defined('IN_LOGIN') && $this->data['user_id'] == ANONYMOUS)
1186
                        {
1187
                                global $phpEx;
1188
1189
                                $this->setup('ucp');
1190
                                $this->data['is_registered'] = $this->data['is_bot'] = false;
1191
1192
                                // Set as a precaution to allow login_box() handling this case correctly as well as this function not being executed again.
1193
                                define('IN_CHECK_BAN', 1);
1194
1195
                                login_box("index.$phpEx");
1196
1197
                                // The false here is needed, else the user is able to circumvent the ban.
1198
                                $this->session_kill(false);
1199
                        }
1200
1201
                        // Ok, we catch the case of an empty session id for the anonymous user...
1202
                        // This can happen if the user is logging in, banned by username and the login_box() being called "again".
1203
                        if (empty($this->session_id) && defined('IN_CHECK_BAN'))
1204
                        {
1205
                                $this->session_create(ANONYMOUS);
1206
                        }
1207
1208
1209
                        // Determine which message to output
1210
                        $till_date = ($ban_row['ban_end']) ? $this->format_date($ban_row['ban_end']) : '';
1211
                        $message = ($ban_row['ban_end']) ? 'BOARD_BAN_TIME' : 'BOARD_BAN_PERM';
1212
1213
                        $message = sprintf($this->lang[$message], $till_date, '<a href="mailto:' . $config['board_contact'] . '">', '</a>');
1214
                        $message .= ($ban_row['ban_give_reason']) ? '<br /><br />' . sprintf($this->lang['BOARD_BAN_REASON'], $ban_row['ban_give_reason']) : '';
1215
                        $message .= '<br /><br /><em>' . $this->lang['BAN_TRIGGERED_BY_' . strtoupper($ban_triggered_by)] . '</em>';
1216
1217
                        // To circumvent session_begin returning a valid value and the check_ban() not called on second page view, we kill the session again
1218
                        $this->session_kill(false);
1219
1220
                        // A very special case... we are within the cron script which is not supposed to print out the ban message... show blank page
1221
                        if (defined('IN_CRON'))
1222
                        {
1223
                                garbage_collection();
1224
                                exit_handler();
1225
                                exit;
1226
                        }
1227
1228
                        trigger_error($message);
1229
                }
1230
1231
                return ($banned && $ban_row['ban_give_reason']) ? $ban_row['ban_give_reason'] : $banned;
1232
        }
1233
1234
        /**
1235
        * Check if ip is blacklisted
1236
        * This should be called only where absolutly necessary
1237
        *
1238
        * Only IPv4 (rbldns does not support AAAA records/IPv6 lookups)
1239
        *
1240
        * @author satmd (from the php manual)
1241
        * @param string $mode register/post - spamcop for example is ommitted for posting
1242
        * @return false if ip is not blacklisted, else an array([checked server], [lookup])
1243
        */
1244
        function check_dnsbl($mode, $ip = false)
1245
        {
1246
                if ($ip === false)
1247
                {
1248
                        $ip = $this->ip;
1249
                }
1250
1251
                // Neither Spamhaus nor Spamcop supports IPv6 addresses.
1252
                if (strpos($ip, ':') !== false)
1253
                {
1254
                        return false;
1255
                }
1256
1257
                $dnsbl_check = array(
1258
                        'sbl.spamhaus.org'        => 'http://www.spamhaus.org/query/bl?ip=',
1259
                );
1260
1261
                if ($mode == 'register')
1262
                {
1263
                        $dnsbl_check['bl.spamcop.net'] = 'http://spamcop.net/bl.shtml?';
1264
                }
1265
1266
                if ($ip)
1267
                {
1268
                        $quads = explode('.', $ip);
1269
                        $reverse_ip = $quads[3] . '.' . $quads[2] . '.' . $quads[1] . '.' . $quads[0];
1270
1271
                        // Need to be listed on all servers...
1272
                        $listed = true;
1273
                        $info = array();
1274
1275
                        foreach ($dnsbl_check as $dnsbl => $lookup)
1276
                        {
1277
                                if (phpbb_checkdnsrr($reverse_ip . '.' . $dnsbl . '.', 'A') === true)
1278
                                {
1279
                                        $info = array($dnsbl, $lookup . $ip);
1280
                                }
1281
                                else
1282
                                {
1283
                                        $listed = false;
1284
                                }
1285
                        }
1286
1287
                        if ($listed)
1288
                        {
1289
                                return $info;
1290
                        }
1291
                }
1292
1293
                return false;
1294
        }
1295
1296
        /**
1297
        * Check if URI is blacklisted
1298
        * This should be called only where absolutly necessary, for example on the submitted website field
1299
        * This function is not in use at the moment and is only included for testing purposes, it may not work at all!
1300
        * This means it is untested at the moment and therefore commented out
1301
        *
1302
        * @param string $uri URI to check
1303
        * @return true if uri is on blacklist, else false. Only blacklist is checked (~zero FP), no grey lists
1304
        function check_uribl($uri)
1305
        {
1306
                // Normally parse_url() is not intended to parse uris
1307
                // We need to get the top-level domain name anyway... change.
1308
                $uri = parse_url($uri);
1309
1310
                if ($uri === false || empty($uri['host']))
1311
                {
1312
                        return false;
1313
                }
1314
1315
                $uri = trim($uri['host']);
1316
1317
                if ($uri)
1318
                {
1319
                        // One problem here... the return parameter for the "windows" method is different from what
1320
                        // we expect... this may render this check useless...
1321
                        if (phpbb_checkdnsrr($uri . '.multi.uribl.com.', 'A') === true)
1322
                        {
1323
                                return true;
1324
                        }
1325
                }
1326
1327
                return false;
1328
        }
1329
        */
1330
1331
        /**
1332
        * Set/Update a persistent login key
1333
        *
1334
        * This method creates or updates a persistent session key. When a user makes
1335
        * use of persistent (formerly auto-) logins a key is generated and stored in the
1336
        * DB. When they revisit with the same key it's automatically updated in both the
1337
        * DB and cookie. Multiple keys may exist for each user representing different
1338
        * browsers or locations. As with _any_ non-secure-socket no passphrase login this
1339
        * remains vulnerable to exploit.
1340
        */
1341
        function set_login_key($user_id = false, $key = false, $user_ip = false)
1342
        {
1343
                global $config, $db;
1344
1345
                $user_id = ($user_id === false) ? $this->data['user_id'] : $user_id;
1346
                $user_ip = ($user_ip === false) ? $this->ip : $user_ip;
1347
                $key = ($key === false) ? (($this->cookie_data['k']) ? $this->cookie_data['k'] : false) : $key;
1348
1349
                $key_id = unique_id(hexdec(substr($this->session_id, 0, 8)));
1350
1351
                $sql_ary = array(
1352
                        'key_id'                => (string) md5($key_id),
1353
                        'last_ip'                => (string) $this->ip,
1354
                        'last_login'        => (int) time()
1355
                );
1356
1357
                if (!$key)
1358
                {
1359
                        $sql_ary += array(
1360
                                'user_id'        => (int) $user_id
1361
                        );
1362
                }
1363
1364
                if ($key)
1365
                {
1366
                        $sql = 'UPDATE ' . SESSIONS_KEYS_TABLE . '
1367
                                SET ' . $db->sql_build_array('UPDATE', $sql_ary) . '
1368
                                WHERE user_id = ' . (int) $user_id . "
1369
                                        AND key_id = '" . $db->sql_escape(md5($key)) . "'";
1370
                }
1371
                else
1372
                {
1373
                        $sql = 'INSERT INTO ' . SESSIONS_KEYS_TABLE . ' ' . $db->sql_build_array('INSERT', $sql_ary);
1374
                }
1375
                $db->sql_query($sql);
1376
1377
                $this->cookie_data['k'] = $key_id;
1378
1379
                return false;
1380
        }
1381
1382
        /**
1383
        * Reset all login keys for the specified user
1384
        *
1385
        * This method removes all current login keys for a specified (or the current)
1386
        * user. It will be called on password change to render old keys unusable
1387
        */
1388
        function reset_login_keys($user_id = false)
1389
        {
1390
                global $config, $db;
1391
1392
                $user_id = ($user_id === false) ? (int) $this->data['user_id'] : (int) $user_id;
1393
1394
                $sql = 'DELETE FROM ' . SESSIONS_KEYS_TABLE . '
1395
                        WHERE user_id = ' . (int) $user_id;
1396
                $db->sql_query($sql);
1397
1398
                // If the user is logged in, update last visit info first before deleting sessions
1399
                $sql = 'SELECT session_time, session_page
1400
                        FROM ' . SESSIONS_TABLE . '
1401
                        WHERE session_user_id = ' . (int) $user_id . '
1402
                        ORDER BY session_time DESC';
1403
                $result = $db->sql_query_limit($sql, 1);
1404
                $row = $db->sql_fetchrow($result);
1405
                $db->sql_freeresult($result);
1406
1407
                if ($row)
1408
                {
1409
                        $sql = 'UPDATE ' . USERS_TABLE . '
1410
                                SET user_lastvisit = ' . (int) $row['session_time'] . ", user_lastpage = '" . $db->sql_escape($row['session_page']) . "'
1411
                                WHERE user_id = " . (int) $user_id;
1412
                        $db->sql_query($sql);
1413
                }
1414
1415
                // Let's also clear any current sessions for the specified user_id
1416
                // If it's the current user then we'll leave this session intact
1417
                $sql_where = 'session_user_id = ' . (int) $user_id;
1418
                $sql_where .= ($user_id === (int) $this->data['user_id']) ? " AND session_id <> '" . $db->sql_escape($this->session_id) . "'" : '';
1419
1420
                $sql = 'DELETE FROM ' . SESSIONS_TABLE . "
1421
                        WHERE $sql_where";
1422
                $db->sql_query($sql);
1423
1424
                // We're changing the password of the current user and they have a key
1425
                // Lets regenerate it to be safe
1426
                if ($user_id === (int) $this->data['user_id'] && $this->cookie_data['k'])
1427
                {
1428
                        $this->set_login_key($user_id);
1429
                }
1430
        }
1431
1432
1433
        /**
1434
        * Check if the request originated from the same page.
1435
        * @param bool $check_script_path If true, the path will be checked as well
1436
        */
1437
        function validate_referer($check_script_path = false)
1438
        {
1439
                global $config;
1440
1441
                // no referer - nothing to validate, user's fault for turning it off (we only check on POST; so meta can't be the reason)
1442
                if (empty($this->referer) || empty($this->host))
1443
                {
1444
                        return true;
1445
                }
1446
1447
                $host = htmlspecialchars($this->host);
1448
                $ref = substr($this->referer, strpos($this->referer, '://') + 3);
1449
1450
                if (!(stripos($ref, $host) === 0) && (!$config['force_server_vars'] || !(stripos($ref, $config['server_name']) === 0)))
1451
                {
1452
                        return false;
1453
                }
1454
                else if ($check_script_path && rtrim($this->page['root_script_path'], '/') !== '')
1455
                {
1456
                        $ref = substr($ref, strlen($host));
1457
                        $server_port = (!empty($_SERVER['SERVER_PORT'])) ? (int) $_SERVER['SERVER_PORT'] : (int) getenv('SERVER_PORT');
1458
1459
                        if ($server_port !== 80 && $server_port !== 443 && stripos($ref, ":$server_port") === 0)
1460
                        {
1461
                                $ref = substr($ref, strlen(":$server_port"));
1462
                        }
1463
1464
                        if (!(stripos(rtrim($ref, '/'), rtrim($this->page['root_script_path'], '/')) === 0))
1465
                        {
1466
                                return false;
1467
                        }
1468
                }
1469
1470
                return true;
1471
        }
1472
1473
1474
        function unset_admin()
1475
        {
1476
                global $db;
1477
                $sql = 'UPDATE ' . SESSIONS_TABLE . '
1478
                        SET session_admin = 0
1479
                        WHERE session_id = \'' . $db->sql_escape($this->session_id) . '\'';
1480
                $db->sql_query($sql);
1481
        }
1482
}
1483
1484
1485
/**
1486
* Base user class
1487
*
1488
* This is the overarching class which contains (through session extend)
1489
* all methods utilised for user functionality during a session.
1490
*
1491
* @package phpBB3
1492
*/
1493
class user extends session
1494
{
1495
        var $lang = array();
1496
        var $help = array();
1497
        var $theme = array();
1498
        var $date_format;
1499
        var $timezone;
1500
        var $dst;
1501
1502
        var $lang_name = false;
1503
        var $lang_id = false;
1504
        var $lang_path;
1505
        var $img_lang;
1506
        var $img_array = array();
1507
1508
        // Able to add new options (up to id 31)
1509
        var $keyoptions = array('viewimg' => 0, 'viewflash' => 1, 'viewsmilies' => 2, 'viewsigs' => 3, 'viewavatars' => 4, 'viewcensors' => 5, 'attachsig' => 6, 'bbcode' => 8, 'smilies' => 9, 'popuppm' => 10, 'sig_bbcode' => 15, 'sig_smilies' => 16, 'sig_links' => 17);
1510
1511
        /**
1512
        * Constructor to set the lang path
1513
        */
1514
        function user()
1515
        {
1516
                global $phpbb_root_path;
1517
1518
                $this->lang_path = $phpbb_root_path . 'language/';
1519
        }
1520
1521
        /**
1522
        * Function to set custom language path (able to use directory outside of phpBB)
1523
        *
1524
        * @param string $lang_path New language path used.
1525
        * @access public
1526
        */
1527
        function set_custom_lang_path($lang_path)
1528
        {
1529
                $this->lang_path = $lang_path;
1530
1531
                if (substr($this->lang_path, -1) != '/')
1532
                {
1533
                        $this->lang_path .= '/';
1534
                }
1535
        }
1536
1537
        /**
1538
        * Setup basic user-specific items (style, language, ...)
1539
        */
1540
        function setup($lang_set = false, $style = false)
1541
        {
1542
                global $db, $template, $config, $auth, $phpEx, $phpbb_root_path, $cache;
1543
1544
                if ($this->data['user_id'] != ANONYMOUS)
1545
                {
1546
                        $this->lang_name = (file_exists($this->lang_path . $this->data['user_lang'] . "/common.$phpEx")) ? $this->data['user_lang'] : basename($config['default_lang']);
1547
1548
                        $this->date_format = $this->data['user_dateformat'];
1549
                        $this->timezone = $this->data['user_timezone'] * 3600;
1550
                        $this->dst = $this->data['user_dst'] * 3600;
1551
                }
1552
                else
1553
                {
1554
                        $this->lang_name = basename($config['default_lang']);
1555
                        $this->date_format = $config['default_dateformat'];
1556
                        $this->timezone = $config['board_timezone'] * 3600;
1557
                        $this->dst = $config['board_dst'] * 3600;
1558
1559
                        /**
1560
                        * If a guest user is surfing, we try to guess his/her language first by obtaining the browser language
1561
                        * If re-enabled we need to make sure only those languages installed are checked
1562
                        * Commented out so we do not loose the code.
1563
1564
                        if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE']))
1565
                        {
1566
                                $accept_lang_ary = explode(',', $_SERVER['HTTP_ACCEPT_LANGUAGE']);
1567
1568
                                foreach ($accept_lang_ary as $accept_lang)
1569
                                {
1570
                                        // Set correct format ... guess full xx_YY form
1571
                                        $accept_lang = substr($accept_lang, 0, 2) . '_' . strtoupper(substr($accept_lang, 3, 2));
1572
                                        $accept_lang = basename($accept_lang);
1573
1574
                                        if (file_exists($this->lang_path . $accept_lang . "/common.$phpEx"))
1575
                                        {
1576
                                                $this->lang_name = $config['default_lang'] = $accept_lang;
1577
                                                break;
1578
                                        }
1579
                                        else
1580
                                        {
1581
                                                // No match on xx_YY so try xx
1582
                                                $accept_lang = substr($accept_lang, 0, 2);
1583
                                                $accept_lang = basename($accept_lang);
1584
1585
                                                if (file_exists($this->lang_path . $accept_lang . "/common.$phpEx"))
1586
                                                {
1587
                                                        $this->lang_name = $config['default_lang'] = $accept_lang;
1588
                                                        break;
1589
                                                }
1590
                                        }
1591
                                }
1592
                        }
1593
                        */
1594
                }
1595
1596
                // We include common language file here to not load it every time a custom language file is included
1597
                $lang = &$this->lang;
1598
1599
                // Do not suppress error if in DEBUG_EXTRA mode
1600
                $include_result = (defined('DEBUG_EXTRA')) ? (include $this->lang_path . $this->lang_name . "/common.$phpEx") : (@include $this->lang_path . $this->lang_name . "/common.$phpEx");
1601
1602
                if ($include_result === false)
1603
                {
1604
                        die('Language file ' . $this->lang_path . $this->lang_name . "/common.$phpEx" . " couldn't be opened.");
1605
                }
1606
1607
                $this->add_lang($lang_set);
1608
                unset($lang_set);
1609
1610
                if (!empty($_GET['style']) && $auth->acl_get('a_styles') && !defined('ADMIN_START'))
1611
                {
1612
                        global $SID, $_EXTRA_URL;
1613
1614
                        $style = request_var('style', 0);
1615
                        $SID .= '&amp;style=' . $style;
1616
                        $_EXTRA_URL = array('style=' . $style);
1617
                }
1618
                else
1619
                {
1620
                        // Set up style
1621
                        $style = ($style) ? $style : ((!$config['override_user_style']) ? $this->data['user_style'] : $config['default_style']);
1622
                }
1623
1624
                $sql = 'SELECT s.style_id, t.template_storedb, t.template_path, t.template_id, t.bbcode_bitfield, t.template_inherits_id, t.template_inherit_path, c.theme_path, c.theme_name, c.theme_storedb, c.theme_id, i.imageset_path, i.imageset_id, i.imageset_name
1625
                        FROM ' . STYLES_TABLE . ' s, ' . STYLES_TEMPLATE_TABLE . ' t, ' . STYLES_THEME_TABLE . ' c, ' . STYLES_IMAGESET_TABLE . " i
1626
                        WHERE s.style_id = $style
1627
                                AND t.template_id = s.template_id
1628
                                AND c.theme_id = s.theme_id
1629
                                AND i.imageset_id = s.imageset_id";
1630
                $result = $db->sql_query($sql, 3600);
1631
                $this->theme = $db->sql_fetchrow($result);
1632
                $db->sql_freeresult($result);
1633
1634
                // User has wrong style
1635
                if (!$this->theme && $style == $this->data['user_style'])
1636
                {
1637
                        $style = $this->data['user_style'] = $config['default_style'];
1638
1639
                        $sql = 'UPDATE ' . USERS_TABLE . "
1640
                                SET user_style = $style
1641
                                WHERE user_id = {$this->data['user_id']}";
1642
                        $db->sql_query($sql);
1643
1644
                        $sql = 'SELECT s.style_id, t.template_storedb, t.template_path, t.template_id, t.bbcode_bitfield, c.theme_path, c.theme_name, c.theme_storedb, c.theme_id, i.imageset_path, i.imageset_id, i.imageset_name
1645
                                FROM ' . STYLES_TABLE . ' s, ' . STYLES_TEMPLATE_TABLE . ' t, ' . STYLES_THEME_TABLE . ' c, ' . STYLES_IMAGESET_TABLE . " i
1646
                                WHERE s.style_id = $style
1647
                                        AND t.template_id = s.template_id
1648
                                        AND c.theme_id = s.theme_id
1649
                                        AND i.imageset_id = s.imageset_id";
1650
                        $result = $db->sql_query($sql, 3600);
1651
                        $this->theme = $db->sql_fetchrow($result);
1652
                        $db->sql_freeresult($result);
1653
                }
1654
1655
                if (!$this->theme)
1656
                {
1657
                        trigger_error('Could not get style data', E_USER_ERROR);
1658
                }
1659
1660
                // Now parse the cfg file and cache it
1661
                $parsed_items = $cache->obtain_cfg_items($this->theme);
1662
1663
                // We are only interested in the theme configuration for now
1664
                $parsed_items = $parsed_items['theme'];
1665
1666
                $check_for = array(
1667
                        'parse_css_file'        => (int) 0,
1668
                        'pagination_sep'        => (string) ', '
1669
                );
1670
1671
                foreach ($check_for as $key => $default_value)
1672
                {
1673
                        $this->theme[$key] = (isset($parsed_items[$key])) ? $parsed_items[$key] : $default_value;
1674
                        settype($this->theme[$key], gettype($default_value));
1675
1676
                        if (is_string($default_value))
1677
                        {
1678
                                $this->theme[$key] = htmlspecialchars($this->theme[$key]);
1679
                        }
1680
                }
1681
1682
                // If the style author specified the theme needs to be cached
1683
                // (because of the used paths and variables) than make sure it is the case.
1684
                // For example, if the theme uses language-specific images it needs to be stored in db.
1685
                if (!$this->theme['theme_storedb'] && $this->theme['parse_css_file'])
1686
                {
1687
                        $this->theme['theme_storedb'] = 1;
1688
1689
                        $stylesheet = file_get_contents("{$phpbb_root_path}styles/{$this->theme['theme_path']}/theme/stylesheet.css");
1690
                        // Match CSS imports
1691
                        $matches = array();
1692
                        preg_match_all('/@import url\(["\'](.*)["\']\);/i', $stylesheet, $matches);
1693
1694
                        if (sizeof($matches))
1695
                        {
1696
                                $content = '';
1697
                                foreach ($matches[0] as $idx => $match)
1698
                                {
1699
                                        if ($content = @file_get_contents("{$phpbb_root_path}styles/{$this->theme['theme_path']}/theme/" . $matches[1][$idx]))
1700
                                        {
1701
                                                $content = trim($content);
1702
                                        }
1703
                                        else
1704
                                        {
1705
                                                $content = '';
1706
                                        }
1707
                                        $stylesheet = str_replace($match, $content, $stylesheet);
1708
                                }
1709
                                unset($content);
1710
                        }
1711
1712
                        $stylesheet = str_replace('./', 'styles/' . $this->theme['theme_path'] . '/theme/', $stylesheet);
1713
1714
                        $sql_ary = array(
1715
                                'theme_data'        => $stylesheet,
1716
                                'theme_mtime'        => time(),
1717
                                'theme_storedb'        => 1
1718
                        );
1719
1720
                        $sql = 'UPDATE ' . STYLES_THEME_TABLE . '
1721
                                SET ' . $db->sql_build_array('UPDATE', $sql_ary) . '
1722
                                WHERE theme_id = ' . $this->theme['theme_id'];
1723
                        $db->sql_query($sql);
1724
1725
                        unset($sql_ary);
1726
                }
1727
1728
                $template->set_template();
1729
1730
                $this->img_lang = (file_exists($phpbb_root_path . 'styles/' . $this->theme['imageset_path'] . '/imageset/' . $this->lang_name)) ? $this->lang_name : $config['default_lang'];
1731
1732
                // Same query in style.php
1733
                $sql = 'SELECT *
1734
                        FROM ' . STYLES_IMAGESET_DATA_TABLE . '
1735
                        WHERE imageset_id = ' . $this->theme['imageset_id'] . "
1736
                        AND image_filename <> ''
1737
                        AND image_lang IN ('" . $db->sql_escape($this->img_lang) . "', '')";
1738
                $result = $db->sql_query($sql, 3600);
1739
1740
                $localised_images = false;
1741
                while ($row = $db->sql_fetchrow($result))
1742
                {
1743
                        if ($row['image_lang'])
1744
                        {
1745
                                $localised_images = true;
1746
                        }
1747
1748
                        $row['image_filename'] = rawurlencode($row['image_filename']);
1749
                        $this->img_array[$row['image_name']] = $row;
1750
                }
1751
                $db->sql_freeresult($result);
1752
1753
                // there were no localised images, try to refresh the localised imageset for the user's language
1754
                if (!$localised_images)
1755
                {
1756
                        // Attention: this code ignores the image definition list from acp_styles and just takes everything
1757
                        // that the config file contains
1758
                        $sql_ary = array();
1759
1760
                        $db->sql_transaction('begin');
1761
1762
                        $sql = 'DELETE FROM ' . STYLES_IMAGESET_DATA_TABLE . '
1763
                                WHERE imageset_id = ' . $this->theme['imageset_id'] . '
1764
                                        AND image_lang = \'' . $db->sql_escape($this->img_lang) . '\'';
1765
                        $result = $db->sql_query($sql);
1766
1767
                        if (@file_exists("{$phpbb_root_path}styles/{$this->theme['imageset_path']}/imageset/{$this->img_lang}/imageset.cfg"))
1768
                        {
1769
                                $cfg_data_imageset_data = parse_cfg_file("{$phpbb_root_path}styles/{$this->theme['imageset_path']}/imageset/{$this->img_lang}/imageset.cfg");
1770
                                foreach ($cfg_data_imageset_data as $image_name => $value)
1771
                                {
1772
                                        if (strpos($value, '*') !== false)
1773
                                        {
1774
                                                if (substr($value, -1, 1) === '*')
1775
                                                {
1776
                                                        list($image_filename, $image_height) = explode('*', $value);
1777
                                                        $image_width = 0;
1778
                                                }
1779
                                                else
1780
                                                {
1781
                                                        list($image_filename, $image_height, $image_width) = explode('*', $value);
1782
                                                }
1783
                                        }
1784
                                        else
1785
                                        {
1786
                                                $image_filename = $value;
1787
                                                $image_height = $image_width = 0;
1788
                                        }
1789
1790
                                        if (strpos($image_name, 'img_') === 0 && $image_filename)
1791
                                        {
1792
                                                $image_name = substr($image_name, 4);
1793
                                                $sql_ary[] = array(
1794
                                                        'image_name'                => (string) $image_name,
1795
                                                        'image_filename'        => (string) $image_filename,
1796
                                                        'image_height'                => (int) $image_height,
1797
                                                        'image_width'                => (int) $image_width,
1798
                                                        'imageset_id'                => (int) $this->theme['imageset_id'],
1799
                                                        'image_lang'                => (string) $this->img_lang,
1800
                                                );
1801
                                        }
1802
                                }
1803
                        }
1804
1805
                        if (sizeof($sql_ary))
1806
                        {
1807
                                $db->sql_multi_insert(STYLES_IMAGESET_DATA_TABLE, $sql_ary);
1808
                                $db->sql_transaction('commit');
1809
                                $cache->destroy('sql', STYLES_IMAGESET_DATA_TABLE);
1810
1811
                                add_log('admin', 'LOG_IMAGESET_LANG_REFRESHED', $this->theme['imageset_name'], $this->img_lang);
1812
                        }
1813
                        else
1814
                        {
1815
                                $db->sql_transaction('commit');
1816
                                add_log('admin', 'LOG_IMAGESET_LANG_MISSING', $this->theme['imageset_name'], $this->img_lang);
1817
                        }
1818
                }
1819
1820
                // Call phpbb_user_session_handler() in case external application want to "bend" some variables or replace classes...
1821
                // After calling it we continue script execution...
1822
                phpbb_user_session_handler();
1823
1824
                // If this function got called from the error handler we are finished here.
1825
                if (defined('IN_ERROR_HANDLER'))
1826
                {
1827
                        return;
1828
                }
1829
1830
                // Disable board if the install/ directory is still present
1831
                // For the brave development army we do not care about this, else we need to comment out this everytime we develop locally
1832
                if (!defined('DEBUG_EXTRA') && !defined('ADMIN_START') && !defined('IN_INSTALL') && !defined('IN_LOGIN') && file_exists($phpbb_root_path . 'install') && !is_file($phpbb_root_path . 'install'))
1833
                {
1834
                        // Adjust the message slightly according to the permissions
1835
                        if ($auth->acl_gets('a_', 'm_') || $auth->acl_getf_global('m_'))
1836
                        {
1837
                                $message = 'REMOVE_INSTALL';
1838
                        }
1839
                        else
1840
                        {
1841
                                $message = (!empty($config['board_disable_msg'])) ? $config['board_disable_msg'] : 'BOARD_DISABLE';
1842
                        }
1843
                        trigger_error($message);
1844
                }
1845
1846
                // Is board disabled and user not an admin or moderator?
1847
                if ($config['board_disable'] && !defined('IN_LOGIN') && !$auth->acl_gets('a_', 'm_') && !$auth->acl_getf_global('m_'))
1848
                {
1849
                        if ($this->data['is_bot'])
1850
                        {
1851
                                send_status_line(503, 'Service Unavailable');
1852
                        }
1853
1854
                        $message = (!empty($config['board_disable_msg'])) ? $config['board_disable_msg'] : 'BOARD_DISABLE';
1855
                        trigger_error($message);
1856
                }
1857
1858
                // Is load exceeded?
1859
                if ($config['limit_load'] && $this->load !== false)
1860
                {
1861
                        if ($this->load > floatval($config['limit_load']) && !defined('IN_LOGIN') && !defined('IN_ADMIN'))
1862
                        {
1863
                                // Set board disabled to true to let the admins/mods get the proper notification
1864
                                $config['board_disable'] = '1';
1865
1866
                                if (!$auth->acl_gets('a_', 'm_') && !$auth->acl_getf_global('m_'))
1867
                                {
1868
                                        if ($this->data['is_bot'])
1869
                                        {
1870
                                                send_status_line(503, 'Service Unavailable');
1871
                                        }
1872
                                        trigger_error('BOARD_UNAVAILABLE');
1873
                                }
1874
                        }
1875
                }
1876
1877
                if (isset($this->data['session_viewonline']))
1878
                {
1879
                        // Make sure the user is able to hide his session
1880
                        if (!$this->data['session_viewonline'])
1881
                        {
1882
                                // Reset online status if not allowed to hide the session...
1883
                                if (!$auth->acl_get('u_hideonline'))
1884
                                {
1885
                                        $sql = 'UPDATE ' . SESSIONS_TABLE . '
1886
                                                SET session_viewonline = 1
1887
                                                WHERE session_user_id = ' . $this->data['user_id'];
1888
                                        $db->sql_query($sql);
1889
                                        $this->data['session_viewonline'] = 1;
1890
                                }
1891
                        }
1892
                        else if (!$this->data['user_allow_viewonline'])
1893
                        {
1894
                                // the user wants to hide and is allowed to  -> cloaking device on.
1895
                                if ($auth->acl_get('u_hideonline'))
1896
                                {
1897
                                        $sql = 'UPDATE ' . SESSIONS_TABLE . '
1898
                                                SET session_viewonline = 0
1899
                                                WHERE session_user_id = ' . $this->data['user_id'];
1900
                                        $db->sql_query($sql);
1901
                                        $this->data['session_viewonline'] = 0;
1902
                                }
1903
                        }
1904
                }
1905
1906
1907
                // Does the user need to change their password? If so, redirect to the
1908
                // ucp profile reg_details page ... of course do not redirect if we're already in the ucp
1909
                if (!defined('IN_ADMIN') && !defined('ADMIN_START') && $config['chg_passforce'] && !empty($this->data['is_registered']) && $auth->acl_get('u_chgpasswd') && $this->data['user_passchg'] < time() - ($config['chg_passforce'] * 86400))
1910
                {
1911
                        if (strpos($this->page['query_string'], 'mode=reg_details') === false && $this->page['page_name'] != "ucp.$phpEx")
1912
                        {
1913
                                redirect(append_sid("{$phpbb_root_path}ucp.$phpEx", 'i=profile&amp;mode=reg_details'));
1914
                        }
1915
                }
1916
1917
                return;
1918
        }
1919
1920
        /**
1921
        * More advanced language substitution
1922
        * Function to mimic sprintf() with the possibility of using phpBB's language system to substitute nullar/singular/plural forms.
1923
        * Params are the language key and the parameters to be substituted.
1924
        * This function/functionality is inspired by SHS` and Ashe.
1925
        *
1926
        * Example call: <samp>$user->lang('NUM_POSTS_IN_QUEUE', 1);</samp>
1927
        */
1928
        function lang()
1929
        {
1930
                $args = func_get_args();
1931
                $key = $args[0];
1932
1933
                if (is_array($key))
1934
                {
1935
                        $lang = &$this->lang[array_shift($key)];
1936
1937
                        foreach ($key as $_key)
1938
                        {
1939
                                $lang = &$lang[$_key];
1940
                        }
1941
                }
1942
                else
1943
                {
1944
                        $lang = &$this->lang[$key];
1945
                }
1946
1947
                // Return if language string does not exist
1948
                if (!isset($lang) || (!is_string($lang) && !is_array($lang)))
1949
                {
1950
                        return $key;
1951
                }
1952
1953
                // If the language entry is a string, we simply mimic sprintf() behaviour
1954
                if (is_string($lang))
1955
                {
1956
                        if (sizeof($args) == 1)
1957
                        {
1958
                                return $lang;
1959
                        }
1960
1961
                        // Replace key with language entry and simply pass along...
1962
                        $args[0] = $lang;
1963
                        return call_user_func_array('sprintf', $args);
1964
                }
1965
1966
                // It is an array... now handle different nullar/singular/plural forms
1967
                $key_found = false;
1968
1969
                // We now get the first number passed and will select the key based upon this number
1970
                for ($i = 1, $num_args = sizeof($args); $i < $num_args; $i++)
1971
                {
1972
                        if (is_int($args[$i]))
1973
                        {
1974
                                $numbers = array_keys($lang);
1975
1976
                                foreach ($numbers as $num)
1977
                                {
1978
                                        if ($num > $args[$i])
1979
                                        {
1980
                                                break;
1981
                                        }
1982
1983
                                        $key_found = $num;
1984
                                }
1985
                                break;
1986
                        }
1987
                }
1988
1989
                // Ok, let's check if the key was found, else use the last entry (because it is mostly the plural form)
1990
                if ($key_found === false)
1991
                {
1992
                        $numbers = array_keys($lang);
1993
                        $key_found = end($numbers);
1994
                }
1995
1996
                // Use the language string we determined and pass it to sprintf()
1997
                $args[0] = $lang[$key_found];
1998
                return call_user_func_array('sprintf', $args);
1999
        }
2000
2001
        /**
2002
        * Add Language Items - use_db and use_help are assigned where needed (only use them to force inclusion)
2003
        *
2004
        * @param mixed $lang_set specifies the language entries to include
2005
        * @param bool $use_db internal variable for recursion, do not use
2006
        * @param bool $use_help internal variable for recursion, do not use
2007
        *
2008
        * Examples:
2009
        * <code>
2010
        * $lang_set = array('posting', 'help' => 'faq');
2011
        * $lang_set = array('posting', 'viewtopic', 'help' => array('bbcode', 'faq'))
2012
        * $lang_set = array(array('posting', 'viewtopic'), 'help' => array('bbcode', 'faq'))
2013
        * $lang_set = 'posting'
2014
        * $lang_set = array('help' => 'faq', 'db' => array('help:faq', 'posting'))
2015
        * </code>
2016
        */
2017
        function add_lang($lang_set, $use_db = false, $use_help = false)
2018
        {
2019
                global $phpEx;
2020
2021
                if (is_array($lang_set))
2022
                {
2023
                        foreach ($lang_set as $key => $lang_file)
2024
                        {
2025
                                // Please do not delete this line.
2026
                                // We have to force the type here, else [array] language inclusion will not work
2027
                                $key = (string) $key;
2028
2029
                                if ($key == 'db')
2030
                                {
2031
                                        $this->add_lang($lang_file, true, $use_help);
2032
                                }
2033
                                else if ($key == 'help')
2034
                                {
2035
                                        $this->add_lang($lang_file, $use_db, true);
2036
                                }
2037
                                else if (!is_array($lang_file))
2038
                                {
2039
                                        $this->set_lang($this->lang, $this->help, $lang_file, $use_db, $use_help);
2040
                                }
2041
                                else
2042
                                {
2043
                                        $this->add_lang($lang_file, $use_db, $use_help);
2044
                                }
2045
                        }
2046
                        unset($lang_set);
2047
                }
2048
                else if ($lang_set)
2049
                {
2050
                        $this->set_lang($this->lang, $this->help, $lang_set, $use_db, $use_help);
2051
                }
2052
        }
2053
2054
        /**
2055
        * Set language entry (called by add_lang)
2056
        * @access private
2057
        */
2058
        function set_lang(&$lang, &$help, $lang_file, $use_db = false, $use_help = false)
2059
        {
2060
                global $phpEx;
2061
2062
                // Make sure the language name is set (if the user setup did not happen it is not set)
2063
                if (!$this->lang_name)
2064
                {
2065
                        global $config;
2066
                        $this->lang_name = basename($config['default_lang']);
2067
                }
2068
2069
                // $lang == $this->lang
2070
                // $help == $this->help
2071
                // - add appropriate variables here, name them as they are used within the language file...
2072
                if (!$use_db)
2073
                {
2074
                        if ($use_help && strpos($lang_file, '/') !== false)
2075
                        {
2076
                                $language_filename = $this->lang_path . $this->lang_name . '/' . substr($lang_file, 0, stripos($lang_file, '/') + 1) . 'help_' . substr($lang_file, stripos($lang_file, '/') + 1) . '.' . $phpEx;
2077
                        }
2078
                        else
2079
                        {
2080
                                $language_filename = $this->lang_path . $this->lang_name . '/' . (($use_help) ? 'help_' : '') . $lang_file . '.' . $phpEx;
2081
                        }
2082
2083
                        if (!file_exists($language_filename))
2084
                        {
2085
                                global $config;
2086
2087
                                if ($this->lang_name == 'en')
2088
                                {
2089
                                        // The user's selected language is missing the file, the board default's language is missing the file, and the file doesn't exist in /en.
2090
                                        $language_filename = str_replace($this->lang_path . 'en', $this->lang_path . $this->data['user_lang'], $language_filename);
2091
                                        trigger_error('Language file ' . $language_filename . ' couldn\'t be opened.', E_USER_ERROR);
2092
                                }
2093
                                else if ($this->lang_name == basename($config['default_lang']))
2094
                                {
2095
                                        // Fall back to the English Language
2096
                                        $this->lang_name = 'en';
2097
                                        $this->set_lang($lang, $help, $lang_file, $use_db, $use_help);
2098
                                }
2099
                                else if ($this->lang_name == $this->data['user_lang'])
2100
                                {
2101
                                        // Fall back to the board default language
2102
                                        $this->lang_name = basename($config['default_lang']);
2103
                                        $this->set_lang($lang, $help, $lang_file, $use_db, $use_help);
2104
                                }
2105
2106
                                // Reset the lang name
2107
                                $this->lang_name = (file_exists($this->lang_path . $this->data['user_lang'] . "/common.$phpEx")) ? $this->data['user_lang'] : basename($config['default_lang']);
2108
                                return;
2109
                        }
2110
2111
                        // Do not suppress error if in DEBUG_EXTRA mode
2112
                        $include_result = (defined('DEBUG_EXTRA')) ? (include $language_filename) : (@include $language_filename);
2113
2114
                        if ($include_result === false)
2115
                        {
2116
                                trigger_error('Language file ' . $language_filename . ' couldn\'t be opened.', E_USER_ERROR);
2117
                        }
2118
                }
2119
                else if ($use_db)
2120
                {
2121
                        // Get Database Language Strings
2122
                        // Put them into $lang if nothing is prefixed, put them into $help if help: is prefixed
2123
                        // For example: help:faq, posting
2124
                }
2125
        }
2126
2127
        /**
2128
        * Format user date
2129
        *
2130
        * @param int $gmepoch unix timestamp
2131
        * @param string $format date format in date() notation. | used to indicate relative dates, for example |d m Y|, h:i is translated to Today, h:i.
2132
        * @param bool $forcedate force non-relative date format.
2133
        *
2134
        * @return mixed translated date
2135
        */
2136
        function format_date($gmepoch, $format = false, $forcedate = false)
2137
        {
2138
                static $midnight;
2139
                static $date_cache;
2140
2141
                $format = (!$format) ? $this->date_format : $format;
2142
                $now = time();
2143
                $delta = $now - $gmepoch;
2144
2145
                if (!isset($date_cache[$format]))
2146
                {
2147
                        // Is the user requesting a friendly date format (i.e. 'Today 12:42')?
2148
                        $date_cache[$format] = array(
2149
                                'is_short'                => strpos($format, '|'),
2150
                                'format_short'        => substr($format, 0, strpos($format, '|')) . '||' . substr(strrchr($format, '|'), 1),
2151
                                'format_long'        => str_replace('|', '', $format),
2152
                                'lang'                        => $this->lang['datetime'],
2153
                        );
2154
2155
                        // Short representation of month in format? Some languages use different terms for the long and short format of May
2156
                        if ((strpos($format, '\M') === false && strpos($format, 'M') !== false) || (strpos($format, '\r') === false && strpos($format, 'r') !== false))
2157
                        {
2158
                                $date_cache[$format]['lang']['May'] = $this->lang['datetime']['May_short'];
2159
                        }
2160
                }
2161
2162
                // Zone offset
2163
                $zone_offset = $this->timezone + $this->dst;
2164
2165
                // Show date <= 1 hour ago as 'xx min ago' but not greater than 60 seconds in the future
2166
                // A small tolerence is given for times in the future but in the same minute are displayed as '< than a minute ago'
2167
                if ($delta <= 3600 && $delta > -60 && ($delta >= -5 || (($now / 60) % 60) == (($gmepoch / 60) % 60)) && $date_cache[$format]['is_short'] !== false && !$forcedate && isset($this->lang['datetime']['AGO']))
2168
                {
2169
                        return $this->lang(array('datetime', 'AGO'), max(0, (int) floor($delta / 60)));
2170
                }
2171
2172
                if (!$midnight)
2173
                {
2174
                        list($d, $m, $y) = explode(' ', gmdate('j n Y', time() + $zone_offset));
2175
                        $midnight = gmmktime(0, 0, 0, $m, $d, $y) - $zone_offset;
2176
                }
2177
2178
                if ($date_cache[$format]['is_short'] !== false && !$forcedate && !($gmepoch < $midnight - 86400 || $gmepoch > $midnight + 172800))
2179
                {
2180
                        $day = false;
2181
2182
                        if ($gmepoch > $midnight + 86400)
2183
                        {
2184
                                $day = 'TOMORROW';
2185
                        }
2186
                        else if ($gmepoch > $midnight)
2187
                        {
2188
                                $day = 'TODAY';
2189
                        }
2190
                        else if ($gmepoch > $midnight - 86400)
2191
                        {
2192
                                $day = 'YESTERDAY';
2193
                        }
2194
2195
                        if ($day !== false)
2196
                        {
2197
                                return str_replace('||', $this->lang['datetime'][$day], strtr(@gmdate($date_cache[$format]['format_short'], $gmepoch + $zone_offset), $date_cache[$format]['lang']));
2198
                        }
2199
                }
2200
2201
                return strtr(@gmdate($date_cache[$format]['format_long'], $gmepoch + $zone_offset), $date_cache[$format]['lang']);
2202
        }
2203
2204
        /**
2205
        * Get language id currently used by the user
2206
        */
2207
        function get_iso_lang_id()
2208
        {
2209
                global $config, $db;
2210
2211
                if (!empty($this->lang_id))
2212
                {
2213
                        return $this->lang_id;
2214
                }
2215
2216
                if (!$this->lang_name)
2217
                {
2218
                        $this->lang_name = $config['default_lang'];
2219
                }
2220
2221
                $sql = 'SELECT lang_id
2222
                        FROM ' . LANG_TABLE . "
2223
                        WHERE lang_iso = '" . $db->sql_escape($this->lang_name) . "'";
2224
                $result = $db->sql_query($sql);
2225
                $this->lang_id = (int) $db->sql_fetchfield('lang_id');
2226
                $db->sql_freeresult($result);
2227
2228
                return $this->lang_id;
2229
        }
2230
2231
        /**
2232
        * Get users profile fields
2233
        */
2234
        function get_profile_fields($user_id)
2235
        {
2236
                global $db;
2237
2238
                if (isset($this->profile_fields))
2239
                {
2240
                        return;
2241
                }
2242
2243
                $sql = 'SELECT *
2244
                        FROM ' . PROFILE_FIELDS_DATA_TABLE . "
2245
                        WHERE user_id = $user_id";
2246
                $result = $db->sql_query_limit($sql, 1);
2247
                $this->profile_fields = (!($row = $db->sql_fetchrow($result))) ? array() : $row;
2248
                $db->sql_freeresult($result);
2249
        }
2250
2251
        /**
2252
        * Specify/Get image
2253
        * $suffix is no longer used - we know it. ;) It is there for backward compatibility.
2254
        */
2255
        function img($img, $alt = '', $width = false, $suffix = '', $type = 'full_tag')
2256
        {
2257
                static $imgs;
2258
                global $phpbb_root_path;
2259
2260
                $img_data = &$imgs[$img];
2261
2262
                if (empty($img_data))
2263
                {
2264
                        if (!isset($this->img_array[$img]))
2265
                        {
2266
                                // Do not fill the image to let designers decide what to do if the image is empty
2267
                                $img_data = '';
2268
                                return $img_data;
2269
                        }
2270
2271
                        // Use URL if told so
2272
                        $root_path = (defined('PHPBB_USE_BOARD_URL_PATH') && PHPBB_USE_BOARD_URL_PATH) ? generate_board_url() . '/' : $phpbb_root_path;
2273
2274
                        $path = 'styles/' . rawurlencode($this->theme['imageset_path']) . '/imageset/' . ($this->img_array[$img]['image_lang'] ? $this->img_array[$img]['image_lang'] .'/' : '') . $this->img_array[$img]['image_filename'];
2275
2276
                        $img_data['src'] = $root_path . $path;
2277
                        $img_data['width'] = $this->img_array[$img]['image_width'];
2278
                        $img_data['height'] = $this->img_array[$img]['image_height'];
2279
2280
                        // We overwrite the width and height to the phpbb logo's width
2281
                        // and height here if the contents of the site_logo file are
2282
                        // really equal to the phpbb_logo
2283
                        // This allows us to change the dimensions of the phpbb_logo without
2284
                        // modifying the imageset.cfg and causing a conflict for everyone
2285
                        // who modified it for their custom logo on updating
2286
                        if ($img == 'site_logo' && file_exists($phpbb_root_path . $path))
2287
                        {
2288
                                global $cache;
2289
2290
                                $img_file_hashes = $cache->get('imageset_site_logo_md5');
2291
2292
                                if ($img_file_hashes === false)
2293
                                {
2294
                                        $img_file_hashes = array();
2295
                                }
2296
2297
                                $key = $this->theme['imageset_path'] . '::' . $this->img_array[$img]['image_lang'];
2298
                                if (!isset($img_file_hashes[$key]))
2299
                                {
2300
                                        $img_file_hashes[$key] = md5(file_get_contents($phpbb_root_path . $path));
2301
                                        $cache->put('imageset_site_logo_md5', $img_file_hashes);
2302
                                }
2303
2304
                                $phpbb_logo_hash = '0c461a32cd3621643105f0d02a772c10';
2305
2306
                                if ($phpbb_logo_hash == $img_file_hashes[$key])
2307
                                {
2308
                                        $img_data['width'] = '149';
2309
                                        $img_data['height'] = '52';
2310
                                }
2311
                        }
2312
                }
2313
2314
                $alt = (!empty($this->lang[$alt])) ? $this->lang[$alt] : $alt;
2315
2316
                switch ($type)
2317
                {
2318
                        case 'src':
2319
                                return $img_data['src'];
2320
                        break;
2321
2322
                        case 'width':
2323
                                return ($width === false) ? $img_data['width'] : $width;
2324
                        break;
2325
2326
                        case 'height':
2327
                                return $img_data['height'];
2328
                        break;
2329
2330
                        default:
2331
                                $use_width = ($width === false) ? $img_data['width'] : $width;
2332
2333
                                return '<img src="' . $img_data['src'] . '"' . (($use_width) ? ' width="' . $use_width . '"' : '') . (($img_data['height']) ? ' height="' . $img_data['height'] . '"' : '') . ' alt="' . $alt . '" title="' . $alt . '" />';
2334
                        break;
2335
                }
2336
        }
2337
2338
        /**
2339
        * Get option bit field from user options.
2340
        *
2341
        * @param int $key option key, as defined in $keyoptions property.
2342
        * @param int $data bit field value to use, or false to use $this->data['user_options']
2343
        * @return bool true if the option is set in the bit field, false otherwise
2344
        */
2345
        function optionget($key, $data = false)
2346
        {
2347
                $var = ($data !== false) ? $data : $this->data['user_options'];
2348
                return phpbb_optionget($this->keyoptions[$key], $var);
2349
        }
2350
2351
        /**
2352
        * Set option bit field for user options.
2353
        *
2354
        * @param int $key Option key, as defined in $keyoptions property.
2355
        * @param bool $value True to set the option, false to clear the option.
2356
        * @param int $data Current bit field value, or false to use $this->data['user_options']
2357
        * @return int|bool If $data is false, the bit field is modified and
2358
        *                  written back to $this->data['user_options'], and
2359
        *                  return value is true if the bit field changed and
2360
        *                  false otherwise. If $data is not false, the new
2361
        *                  bitfield value is returned.
2362
        */
2363
        function optionset($key, $value, $data = false)
2364
        {
2365
                $var = ($data !== false) ? $data : $this->data['user_options'];
2366
2367
                $new_var = phpbb_optionset($this->keyoptions[$key], $value, $var);
2368
2369
                if ($data === false)
2370
                {
2371
                        if ($new_var != $var)
2372
                        {
2373
                                $this->data['user_options'] = $new_var;
2374
                                return true;
2375
                        }
2376
                        else
2377
                        {
2378
                                return false;
2379
                        }
2380
                }
2381
                else
2382
                {
2383
                        return $new_var;
2384
                }
2385
        }
2386
2387
        /**
2388
        * Funtion to make the user leave the NEWLY_REGISTERED system group.
2389
        * @access public
2390
        */
2391
        function leave_newly_registered()
2392
        {
2393
                global $db;
2394
2395
                if (empty($this->data['user_new']))
2396
                {
2397
                        return false;
2398
                }
2399
2400
                if (!function_exists('remove_newly_registered'))
2401
                {
2402
                        global $phpbb_root_path, $phpEx;
2403
2404
                        include($phpbb_root_path . 'includes/functions_user.' . $phpEx);
2405
                }
2406
                if ($group = remove_newly_registered($this->data['user_id'], $this->data))
2407
                {
2408
                        $this->data['group_id'] = $group;
2409
2410
                }
2411
                $this->data['user_permissions'] = '';
2412
                $this->data['user_new'] = 0;
2413
2414
                return true;
2415
        }
2416
2417
        /**
2418
        * Returns all password protected forum ids the user is currently NOT authenticated for.
2419
        *
2420
        * @return array                Array of forum ids
2421
        * @access public
2422
        */
2423
        function get_passworded_forums()
2424
        {
2425
                global $db;
2426
2427
                $sql = 'SELECT f.forum_id, fa.user_id
2428
                        FROM ' . FORUMS_TABLE . ' f
2429
                        LEFT JOIN ' . FORUMS_ACCESS_TABLE . " fa
2430
                                ON (fa.forum_id = f.forum_id
2431
                                        AND fa.session_id = '" . $db->sql_escape($this->session_id) . "')
2432
                        WHERE f.forum_password <> ''";
2433
                $result = $db->sql_query($sql);
2434
2435
                $forum_ids = array();
2436
                while ($row = $db->sql_fetchrow($result))
2437
                {
2438
                        $forum_id = (int) $row['forum_id'];
2439
2440
                        if ($row['user_id'] != $this->data['user_id'])
2441
                        {
2442
                                $forum_ids[$forum_id] = $forum_id;
2443
                        }
2444
                }
2445
                $db->sql_freeresult($result);
2446
2447
                return $forum_ids;
2448
        }
2449
}
2450
2451
?>