1<?php
2/**
3 * Authentication Plugin for authphpbb3.
4 *
5 * @license  GPL 2 (http://www.gnu.org/licenses/gpl.html)
6 * @author   Eole <eole.dev@outlook.com>
7 */
8
9if (!defined('DOKU_INC')) {
10    die();
11}
12
13/**
14 * phpBB 3.x Authentication class.
15 */
16class auth_plugin_authphpbb3 extends DokuWiki_Auth_Plugin {
17    // @var object  phpBB database connection.
18    protected $_phpbb_db_link = null;
19    // @var array   phpBB configuration (cached).
20    protected $_phpbb_conf = array(
21        // @var string  phpBB root path.
22        'root_path'     => '',
23        // @var string  phpBB URL.
24        'url'           => '',
25        // @var string  php extension.
26        'phpEx'         => '',
27        // @var string  phpBB database's driver to use.
28        'dbms'          => '',
29        // @var string  phpBB database's host.
30        'dbhost'        => '',
31        // @var string  phpBB database's port.
32        'dbport'        => '',
33        // @var string  phpBB database's name.
34        'dbname'        => '',
35        // @var string  phpBB database's user.
36        'dbuser'        => '',
37        // @var string  phpBB database's password.
38        'dbpasswd'      => '',
39        // @var string  phpBB database's table prefix.
40        'table_prefix'  => '',
41        // @var string  phpBB cookie's name.
42        'cookie_name'   => ''
43    );
44    // @var int     phpBB user ID.
45    protected $_phpbb_user_id = 0;
46    // @var int     phpBB user session ID.
47    protected $_phpbb_user_session_id = '';
48    // @var int     phpBB user type (0 = normal, 1 = inactive, 2 = bot, 3 = founder).
49    protected $_phpbb_user_type = 0;
50    // @var string  phpBB user name.
51    protected $_phpbb_username = '';
52    // @var string  phpBB user mail.
53    protected $_phpbb_user_email = '';
54    // @var array   phpBB user's groups.
55    protected $_phpbb_groups = array();
56    // @var long    phpBB user session time.
57    protected $_phpbb_sessiontime = 0;
58    // @var cache   DokuWiki cache object.
59    protected $_cache = null;
60    // @var int     Cache duration.
61    protected $_cache_duration = 0;
62    // @var int     Cache extension file name.
63    protected $_cache_ext_name = '.phpbb3cache';
64    // @var int     Cache unit constant (in seconds).
65    CONST CACHE_DURATION_UNIT = 86400; /* 60 * 60 * 24 = 1 day */
66
67    /**
68     * Constructor.
69     */
70    public function __construct() {
71        if (method_exists(get_parent_class($this), '__construct')) {
72            parent::__construct();
73        }
74        // Set capabilities accordingly.
75        $this->cando['addUser']     = false;    // Can Users be created?
76        $this->cando['delUser']     = false;    // Can Users be deleted?
77        $this->cando['modLogin']    = false;    // Can login names be changed?
78        $this->cando['modPass']     = false;    // Can passwords be changed?
79        $this->cando['modName']     = false;    // Can real names be changed?
80        $this->cando['modMail']     = false;    // Can emails be changed?
81        $this->cando['modGroups']   = false;    // Can groups be changed?
82        $this->cando['getUsers']    = true;     // Can a (filtered) list of users be retrieved?
83        $this->cando['getUserCount']= true;     // Can the number of users be retrieved?
84        $this->cando['getGroups']   = true;     // Can a list of available groups be retrieved?
85        $this->cando['external']    = true;     // Does the module do external auth checking?
86        $this->cando['logout']      = true;     // Can the user logout again?
87        // Check database connection requirement.
88        if (!class_exists('PDO')) {
89            $this->dbglog('PDO extension for PHP not found.');
90            $this->success = false;
91        } else {
92            // Load plugin configuration.
93            $this->success = $this->load_configuration();
94        }
95        if (!$this->success) {
96            msg($this->getLang('config_error'), -1);
97        }
98    }
99
100    /**
101     * Destructor.
102     */
103    public function __destruct() {
104        $this->phpbb_disconnect();
105        $this->_cache = null;
106        if (method_exists(get_parent_class($this), '__destruct')) {
107            parent::__destruct();
108        }
109    }
110
111    /**
112     * Writes debug informations.
113     *
114     * @param    string  $msg    Message to write.
115     */
116    public function dbglog($msg) {
117        $class_name = @get_class($this);
118
119        if ($class_name !== false) {
120            $msg = $class_name . ': ' . $msg;
121        }
122        dbglog($msg);
123    }
124
125    /**
126     * Sanitizes a given username.
127     *
128     * @param    string  $username   Username to clean.
129     * @return   string              Clean username.
130     */
131    public function clean_username($username) {
132        $username = preg_replace('#(?:[\x00-\x1F\x7F]+|(?:\xC2[\x80-\x9F])+)#', '', $username);
133        $username = preg_replace('# {2,}#', ' ', $username);
134        $username = trim($username);
135        return strtolower($username);
136    }
137
138    /**
139     * Gets phpBB URL.
140     *
141     * @return   string|false    phpBB URL if success, false otherwise.
142     */
143    public function get_phpbb_url() {
144        if (!empty($this->_phpbb_conf['url'])) {
145            return $this->_phpbb_conf['url'];
146        }
147        if ($this->use_phpbb_cache()) {
148            $result = unserialize($this->_cache->retrieveCache(false));
149            if (is_array($result) && array_key_exists('url', $result)) {
150                $this->_phpbb_conf['url'] = $result['url'];
151            }
152        }
153        if (!empty($this->_phpbb_conf['url'])) {
154            return $this->_phpbb_conf['url'];
155        }
156        if (!$this->phpbb_connect()) {
157            return false;
158        }
159        $query = "SELECT config_name, config_value
160                  FROM {$this->_phpbb_conf['table_prefix']}config
161                  WHERE config_name IN ('server_protocol', 'server_name', 'script_path', 'server_port')";
162        $result = $this->_phpbb_sql_link->prepare($query);
163        if ($result === false) {
164            $this->dbglog('error while preparing query for phpBB URL');
165            return false;
166        }
167        if ($result->execute() === false) {
168            $this->dbglog('error while executing query for phpBB URL');
169            return false;
170        }
171        $server_protocol = '';
172        $server_name = '';
173        $script_path = '';
174        $server_port = '';
175        while ($row = $result->fetch(PDO::FETCH_ASSOC)) {
176            switch ($row['config_name']) {
177                case 'server_protocol':
178                    $server_protocol = strtolower(trim($row['config_value']));
179                    break;
180                case 'server_name':
181                    $server_name = rtrim(trim($row['config_value']), '/');
182                    break;
183                case 'script_path':
184                    $script_path = trim($row['config_value']);
185                    break;
186                case 'server_port':
187                    $server_port = intval($row['config_value']);
188                    break;
189                default:
190                    break;
191            }
192        }
193        if (empty($server_port)) {
194            $server_port = '80';
195            if ($server_protocol === 'https://') {
196                $server_port = '443';
197            }
198        }
199        $server_name = rtrim($server_protocol . $server_name . ':' . $server_port . $script_path, '/');
200        $this->_phpbb_conf['url'] = $server_name;
201        $result->closeCursor();
202        $result = null;
203        return $this->_phpbb_conf['url'];
204    }
205
206    /**
207     * Authenticates the user. Called on every page loaded.
208     *
209     * @param    string  $user   Case sensitive user name.
210     * @param    string  $pass   Plain text password for the user.
211     * @param    boolean $sticky Remember login?
212     * @return   boolean         True for match, false for everything else.
213     */
214    public function trustExternal($user, $pass, $sticky = false) {
215        global $USERINFO;
216        $b = false;
217
218        $this->_phpbb_username = '';
219        $this->_phpbb_user_email = '';
220        $this->_phpbb_user_session_id = '';
221        if (empty($user)) {
222            $b = $this->do_login_cookie();
223        }
224        if (!$b ||
225            empty($this->_phpbb_username) ||
226            empty($this->_phpbb_user_email)) {
227            return false;
228        }
229        $USERINFO['name'] = utf8_encode($this->_phpbb_username);
230        $USERINFO['mail'] = $this->_phpbb_user_email;
231        $USERINFO['grps'] = $this->_phpbb_groups;
232        $_SERVER['REMOTE_USER'] = $USERINFO['name'];
233        $_SESSION[DOKU_COOKIE]['auth']['user'] = $USERINFO['name'];
234        $_SESSION[DOKU_COOKIE]['auth']['info'] = $USERINFO;
235        return true;
236    }
237
238    /**
239     * Fetchs user details from phpBB.
240     *
241     * @param   string          $user           Case sensitive username.
242     * @param   boolean         $requireGroups  Whether or not the returned data must include groups.
243     * @return  array/boolean                   False for error conditions and an array for success.
244     *                                          array['name']           string  User's name.
245     *                                          array['username']       string  User's name.
246     *                                          array['mail']           string  User's email address.
247     *                                          array['phpbb_user_id']  string  User's ID.
248     *                                          array['phpbb_profile']  string  User's link to profile.
249     *                                          array['grps']           array   Group names the user belongs to.
250     */
251    public function getUserData($user, $requireGroups = true) {
252        if (empty($user)) {
253            return false;
254        }
255        $this->_cache_duration = intval($this->getConf('phpbb_cache'));
256        $depends = array('age' => self::CACHE_DURATION_UNIT * $this->_cache_duration);
257        $cache = new cache('authphpbb3_getUserData_' . $user, $this->_cache_ext_name);
258        $user_data = false;
259
260        if (($this->_cache_duration > 0) && $cache->useCache($depends)) {
261            $user_data = unserialize($cache->retrieveCache(false));
262        } else {
263            $cache->removeCache();
264            if (!$this->phpbb_connect()) {
265                return false;
266            }
267            $query = "SELECT user_id, username, username_clean, user_email, user_type
268                      FROM {$this->_phpbb_conf['table_prefix']}users
269                      WHERE username_clean = ?";
270            $result = $this->_phpbb_sql_link->prepare($query);
271            if ($result === false) {
272                $this->dbglog('error while preparing query for user data');
273                return false;
274            }
275            if (!$result->execute(array($this->clean_username($user)))) {
276                $this->dbglog('error while executing query for user data');
277                return false;
278            }
279            $row = $result->fetch(PDO::FETCH_ASSOC);
280            $this->_phpbb_user_type = (int)$row['user_type'];
281            $this->_phpbb_user_id = (int)$row['user_id'];
282            $this->_phpbb_username = $row['username'];
283            $this->_phpbb_user_email = $row['user_email'];
284            $result->closeCursor();
285            $result = null;
286            $this->get_phpbb_user_groups();
287            $this->get_phpbb_url();
288            $user_data = array(
289                'name'          => $this->_phpbb_username,
290                'username'      => $this->_phpbb_username,
291                'mail'          => $this->_phpbb_user_email,
292                'phpbb_user_id' => $this->_phpbb_user_id,
293                'phpbb_profile' => $this->_phpbb_conf['url'] . '/memberlist.php?mode=viewprofile&u=' .
294                                   $this->_phpbb_user_id,
295                'grps'          => $this->_phpbb_groups
296            );
297            $cache->storeCache(serialize($user_data));
298        }
299        $cache = null;
300        return $user_data;
301    }
302
303    /**
304    * Bulk retrieval of users' data (does not use cache system).
305    *
306    * @param    int             $start  Index of first user to be returned.
307    * @param    int             $limit  Maximum number of users to be returned.
308    * @param    array           $filter Array of field/pattern pairs.
309    * @return   array/boolean           List of arrays returned by getUserData function.
310    */
311    public function retrieveUsers($start = 0, $limit = 0, $filter = array()) {
312        if (!$this->phpbb_connect()) {
313            return false;
314        }
315        $start = intval($start);
316        if ($start < 0) {
317            $start = 0;
318        }
319        $limit = intval($limit);
320        if ($limit <= 0) {
321            // Arbitrary limit.
322            $limit = 10000;
323        }
324        if (is_null($filter)) {
325            $filter = array();
326        }
327        if (isset($filter['grps'])) {
328            $filter['group'] = $filter['grps'];
329        }
330        foreach (array('user', 'name', 'mail') as $key) {
331            $tmp = '%';
332            if (isset($filter[$key])) {
333                $tmp = str_replace('%', '', $filter[$key]);
334                if (!is_string($tmp)) {
335                    $tmp = '';
336                }
337                if (empty($tmp)) {
338                    $tmp = '%';
339                } else {
340                    $tmp = '%' . $tmp . '%';
341                }
342            }
343            $filter[$key] = $tmp;
344        }
345        $filter['start'] = (int)$start;
346        $filter['end'] = (int)($start + $limit);
347        $filter['limit'] = (int)$limit;
348        // Gets users.
349        $query = "SELECT user_id, username, username_clean, user_email
350                  FROM {$this->_phpbb_conf['table_prefix']}users
351                  WHERE (username like :name OR username like :user OR
352                        username_clean like :nameclean OR username_clean like :userclean) AND
353                        user_email like :mail
354                  LIMIT :limit OFFSET :start";
355        $result = $this->_phpbb_sql_link->prepare($query);
356        if ($result === false) {
357            $this->dbglog('error while preparing query for users data');
358            return false;
359        }
360        $result->bindValue(':name', $filter['name'], PDO::PARAM_STR);
361        $result->bindValue(':user', $filter['user'], PDO::PARAM_STR);
362        $result->bindValue(':nameclean', $filter['name'], PDO::PARAM_STR);
363        $result->bindValue(':userclean', $filter['user'], PDO::PARAM_STR);
364        $result->bindValue(':mail', $filter['mail'], PDO::PARAM_STR);
365        $result->bindValue(':limit', (int)$filter['limit'], PDO::PARAM_INT);
366        $result->bindValue(':start', (int)$filter['start'], PDO::PARAM_INT);
367        if (!$result->execute()) {
368            $this->dbglog('error while executing query for users data');
369            return false;
370        }
371        $users = array();
372        // Gets users' groups.
373        while ($row = $result->fetch(PDO::FETCH_ASSOC)) {
374            $user_data = array(
375                'name'          => $row['username'],
376                'username'      => $row['username_clean'],
377                'mail'          => $row['user_email'],
378                'phpbb_user_id' => $row['user_id'],
379                'phpbb_profile' => $this->_phpbb_conf['url'] . '/memberlist.php?mode=viewprofile&u=' .
380                                   $row['user_id'],
381                'grps'          => array()
382            );
383            $query = "SELECT *
384                      FROM {$this->_phpbb_conf['table_prefix']}groups g,
385                           {$this->_phpbb_conf['table_prefix']}users u,
386                           {$this->_phpbb_conf['table_prefix']}user_group ug
387                      WHERE u.user_id = ug.user_id AND g.group_id = ug.group_id AND u.user_id = ?";
388            $resgrp = $this->_phpbb_sql_link->prepare($query);
389            if (($resgrp === false) || !$resgrp->execute(array($row['user_id']))) {
390                $this->dbglog('error while executing query for ' . $row['user_id'] . ' groups');
391                // If no group filter, we still add the user to the list.
392                if (!isset($filter['group'])) {
393                    $users[$user_data['username']] = $user_data;
394                }
395                $resgrp->closeCursor();
396                $resgrp = null;
397                continue;
398            }
399            $ingroup = false;
400            while ($rowgrp = $resgrp->fetch(PDO::FETCH_ASSOC)) {
401                $user_data['grps'][] = $rowgrp['group_name'];
402                if (!$ingroup &&
403                    isset($filter['group']) &&
404                    (strpos($rowgrp['group_name'], $filter['group']) !== false)) {
405                    $ingroup = true;
406                }
407            }
408            // Apply group's filter.
409            if (!isset($filter['group']) || (isset($filter['group']) && $ingroup)) {
410                $users[$user_data['username']] = $user_data;
411            }
412            $resgrp->closeCursor();
413            $resgrp = null;
414        }
415        $result->closeCursor();
416        $result = null;
417        return $users;
418    }
419
420    /**
421    * Bulk retrieval of groups (does not use cache system).
422    *
423    * @param    int             $start  Index of first group to be returned.
424    * @param    int             $limit  Maximum number of groups to be returned.
425    * @return   array/boolean           List of groups.
426    */
427    public function retrieveGroups($start = 0, $limit = 0) {
428        if (!$this->phpbb_connect()) {
429            return false;
430        }
431        $start = intval($start);
432        if ($start < 0) {
433            $start = 0;
434        }
435        $limit = intval($limit);
436        if ($limit <= 0) {
437            // Arbitrary limit.
438            $limit = 10000;
439        }
440        $query = "SELECT *
441                  FROM {$this->_phpbb_conf['table_prefix']}groups
442                  LIMIT :limit OFFSET :start";
443        $result = $this->_phpbb_sql_link->prepare($query);
444        if ($result === false) {
445            $this->dbglog('error while preparing query for groups data');
446            return false;
447        }
448        $result->bindValue(':limit', (int)$limit, PDO::PARAM_INT);
449        $result->bindValue(':start', (int)$start, PDO::PARAM_INT);
450        if (!$result->execute()) {
451            $this->dbglog('error while executing query for groups data');
452            return false;
453        }
454        $groups = array();
455        while ($row = $result->fetch(PDO::FETCH_ASSOC)) {
456            if (isset($row['group_name']) && !empty($row['group_name'])) {
457                $groups[$row['group_name']] = $row;
458            }
459        }
460        $result->closeCursor();
461        $result = null;
462        ksort($groups);
463        return $groups;
464    }
465
466    /**
467     * Returns the number of users which meet $filter criteria (does not use cache system).
468     *
469     * @param   array   $filter Array of field/pattern pairs, empty array for no filter.
470     * @return  int             Number of users matching criteria.
471     */
472    public function getUserCount($filter = array()) {
473        $users = $this->retrieveUsers(0, 0, $filter);
474
475        if ($users === false) {
476            return 0;
477        }
478        return count($users);
479    }
480
481    /**
482     * Logs off the user.
483     */
484    public function logOff() {
485        if (method_exists(get_parent_class($this), 'logOff')) {
486            parent::logOff();
487        }
488        if (empty($this->_phpbb_user_session_id)) {
489            $this->get_phpbb_cookie_name();
490            if (empty($this->_phpbb_conf['cookie_name'])) {
491                return ;
492            }
493            $phpbb_cookie_sid_name = $this->_phpbb_conf['cookie_name'] . '_sid';
494            if (array_key_exists($phpbb_cookie_sid_name, $_COOKIE)) {
495                $this->_phpbb_user_session_id = $_COOKIE[$phpbb_cookie_sid_name];
496            }
497        }
498        if (!empty($this->_phpbb_user_session_id) &&
499            ($this->get_phpbb_url() !== false) &&
500            $this->_phpbb_user_id) {
501            //global $ID;
502
503            $url = $this->_phpbb_conf['url'] . '/ucp.php?mode=logout&sid=' . $this->_phpbb_user_session_id;
504            // phpBB doesn't natively support the logout redirection yet.
505            //$url .= '&redirect=' . urlencode(wl($ID, '', true));
506            send_redirect($url);
507        }
508    }
509
510    /**
511     * Loads the plugin's configuration.
512     *
513     * @return   boolean   True on success, false otherwise.
514     */
515    private function load_configuration() {
516        if ($this->use_phpbb_cache()) {
517            $this->_phpbb_conf = unserialize($this->_cache->retrieveCache(false));
518        } else {
519            $this->_cache->removeCache();
520            $this->_phpbb_conf['root_path'] = DOKU_INC . rtrim(trim($this->getConf('phpbb_root_path')), '/') . '/';
521            $this->_phpbb_conf['phpEx'] = substr(strrchr(__FILE__, '.'), 1);
522            if (!@file_exists($this->_phpbb_conf['root_path'] . 'config.' . $this->_phpbb_conf['phpEx'])) {
523                $this->dbglog('phpBB installation not found');
524                return false;
525            }
526            include($this->_phpbb_conf['root_path'] . 'config.' . $this->_phpbb_conf['phpEx']);
527            $this->_phpbb_conf['dbms'] = $dbms;
528            $this->_phpbb_conf['dbhost'] = empty($dbhost) ? '127.0.0.1' : $dbhost;
529            $this->_phpbb_conf['dbport'] = $dbport;
530            $this->_phpbb_conf['dbname'] = $dbname;
531            $this->_phpbb_conf['dbuser'] = $dbuser;
532            $this->_phpbb_conf['dbpasswd'] = $dbpasswd;
533            $this->_phpbb_conf['table_prefix'] = $table_prefix;
534            foreach (array('dbms', 'dbhost', 'dbname', 'dbuser') as $member) {
535                if (empty($this->_phpbb_conf[$member])) {
536                    $this->dbglog("phpBB config variable {$member} not set");
537                    return false;
538                }
539            }
540            if ($this->get_phpbb_url() === false) {
541                $this->dbglog('cannot get phpBB URL');
542                return false;
543            }
544            if (!$this->get_phpbb_cookie_name()) {
545                $this->dbglog('cannot get phpBB cookie name');
546                return false;
547            }
548            $this->_cache->storeCache(serialize($this->_phpbb_conf));
549        }
550        return (!empty($this->_phpbb_conf['url']) &&
551                !empty($this->_phpbb_conf['cookie_name']));
552    }
553
554    /**
555     * Gets the phpBB configuration cache.
556     *
557     * @return object    Cache of the phpBB configuration.
558     */
559    private function get_phpbb_cache() {
560        if ($this->_cache === null) {
561            $this->_cache = new cache('authphpbb3', $this->_cache_ext_name);
562        }
563        return $this->_cache;
564    }
565
566    /**
567     * Can use the phpBB configuration cache.
568     *
569     * @return object    Cache of the phpBB configuration.
570     */
571    private function use_phpbb_cache() {
572        $depends = array();
573
574        $this->get_phpbb_cache();
575        $this->_cache_duration = intval($this->getConf('phpbb_cache'));
576        if ($this->_cache_duration > 0) {
577            $depends['age'] = self::CACHE_DURATION_UNIT * $this->_cache_duration;
578        } else {
579            $depends['purge'] = true;
580        }
581        return $this->_cache->useCache($depends);
582    }
583
584    /**
585     * Connects to phpBB database.
586     *
587     * @return   boolean True on success, false otherwise.
588     */
589    private function phpbb_connect() {
590        if (!$this->_phpbb_db_link) {
591            $host = strtolower(end(explode('\\', $this->_phpbb_conf['dbms'])));
592            $port = '';
593            $dsn = '';
594
595            if (!empty($this->_phpbb_conf['dbport'])) {
596                $port = ';port=' . intval($this->_phpbb_conf['dbport']);
597            }
598            $dsn = ':host=' . $this->_phpbb_conf['dbhost'] . $port .
599                   ';dbname=' . $this->_phpbb_conf['dbname'];
600            try {
601                switch ($host) {
602                    case 'mysql':
603                    case 'mysqli':
604                        $dsn = 'mysql' . $dsn . ';charset=utf8';
605                        $this->_phpbb_sql_link = new PDO(
606                            $dsn,
607                            $this->_phpbb_conf['dbuser'],
608                            $this->_phpbb_conf['dbpasswd']);
609                        break;
610                    case 'postgres':
611                        $dsn = 'pgsql' . $dsn .
612                               ';user=' . $this->_phpbb_conf['dbuser'] .
613                               ';password=' . $this->_phpbb_conf['dbpasswd'];
614                        $this->_phpbb_sql_link = new PDO($dsn);
615                        $this->_phpbb_sql_link->exec("SET NAMES 'UTF8'");
616                        break;
617                    case 'oracle':
618                        $dsn = 'oci' . $dsn . ';charset=utf8';
619                        $this->_phpbb_sql_link = new PDO(
620                            $dsn,
621                            $this->_phpbb_conf['dbuser'],
622                            $this->_phpbb_conf['dbpasswd']);
623                        break;
624                    case 'sqlite':
625                    case 'sqlite3':
626                        if ($host === 'sqlite') {
627                            // SQLite 2 prefix.
628                            $host = 'sqlite2';
629                        } else {
630                            // SQLite 3 prefix.
631                            $host = 'sqlite';
632                        }
633                        $dsn = $host . ':' . $this->_phpbb_conf['root_path'] . $this->_phpbb_conf['dbhost'];
634                        $this->_phpbb_sql_link = new PDO($dsn);
635                        break;
636                    default:
637                        msg($this->getLang('database_support'), -1);
638                        return false;
639                }
640            } catch (PDOException $e) {
641                $this->dbglog('cannot connect to database server (' . $e->getMessage() .')');
642                msg($this->getLang('database_error'), -1);
643                $this->_phpbb_db_link = null;
644                return false;
645            }
646        }
647        return true;
648    }
649
650    /**
651     * Disconnects from phpBB database.
652     */
653    private function phpbb_disconnect() {
654        if ($this->_phpbb_db_link !== null) {
655            $this->_phpbb_db_link = null;
656        }
657    }
658
659    /**
660     * Gets phpBB cookie's name.
661     *
662     * @return   boolean True for success, false otherwise.
663     */
664    private function get_phpbb_cookie_name() {
665        if (!empty($this->_phpbb_conf['cookie_name'])) {
666            return true;
667        }
668        if ($this->use_phpbb_cache()) {
669            $result = unserialize($this->_cache->retrieveCache(false));
670            if (is_array($result) && array_key_exists('cookie_name', $result)) {
671                $this->_phpbb_conf['cookie_name'] = $result['cookie_name'];
672            }
673        }
674        if (!empty($this->_phpbb_conf['cookie_name'])) {
675            return true;
676        }
677        if (!$this->phpbb_connect()) {
678            return false;
679        }
680        // Query for cookie_name.
681        $query = "SELECT config_name, config_value
682                  FROM {$this->_phpbb_conf['table_prefix']}config
683                  WHERE config_name = 'cookie_name'";
684        $result = $this->_phpbb_sql_link->prepare($query);
685        if ($result === false) {
686            $this->dbglog('error while preparing query for cookie.');
687            return false;
688        }
689        if (!$result->execute()) {
690            $this->dbglog('error while executing query for cookie.');
691            return false;
692        }
693        $row = $result->fetch(PDO::FETCH_ASSOC);
694        $this->_phpbb_conf['cookie_name'] = $row['config_value'];
695        $result->closeCursor();
696        $result = null;
697        return true;
698    }
699
700    /**
701     * Gets phpBB user's groups.
702     *
703     * @return   boolean True for success, false otherwise.
704     */
705    private function get_phpbb_user_groups() {
706        $this->_phpbb_groups = array();
707        $this->_phpbb_user_id = filter_var($this->_phpbb_user_id, FILTER_VALIDATE_INT);
708        if (!$this->_phpbb_user_id) {
709            return false;
710        }
711        if (!$this->phpbb_connect()) {
712            return false;
713        }
714        $query = "SELECT *
715                  FROM {$this->_phpbb_conf['table_prefix']}groups g,
716                       {$this->_phpbb_conf['table_prefix']}users u,
717                       {$this->_phpbb_conf['table_prefix']}user_group ug
718                  WHERE u.user_id = ug.user_id AND g.group_id = ug.group_id AND u.user_id = ?";
719        $result = $this->_phpbb_sql_link->prepare($query);
720        if ($result === false) {
721            $this->dbglog('error while preparing query for user\'s groups');
722            return false;
723        }
724        if (!$result->execute(array($this->_phpbb_user_id))) {
725            $this->dbglog('error while executing query for user\'s groups');
726            return false;
727        }
728        while ($row = $result->fetch(PDO::FETCH_ASSOC)) {
729            $this->_phpbb_groups[] = $row['group_name'];
730        }
731        // If the user is a founder.
732        if ($this->_phpbb_user_type === 3) {
733            $this->_phpbb_groups[] = 'admin';
734        }
735        $result->closeCursor();
736        $result = null;
737        return true;
738    }
739
740    /**
741     * Authenticate the user using cookie. Called on every page load.
742     *
743     * @return   boolean True for success, false otherwise.
744     */
745    private function do_login_cookie() {
746        if (!$this->phpbb_connect()) {
747            return false;
748        }
749        if (!$this->get_phpbb_cookie_name()) {
750            return false;
751        }
752        $phpbb_cookie_user_sid = $this->_phpbb_conf['cookie_name'] . '_sid';
753        $phpbb_cookie_user_id = $this->_phpbb_conf['cookie_name'] . '_u';
754        $this->_phpbb_user_session_id =
755            array_key_exists($phpbb_cookie_user_sid, $_COOKIE) ? $_COOKIE[$phpbb_cookie_user_sid] : null;
756        $phpbb_cookie_user_id =
757            array_key_exists($phpbb_cookie_user_id, $_COOKIE) ? intval($_COOKIE[$phpbb_cookie_user_id]) : null;
758        if (empty($this->_phpbb_user_session_id) || !ctype_xdigit($this->_phpbb_user_session_id)) {
759            $this->dbglog('invalid SID in user\'s cookie ' .
760                '(SID=' . $phpbb_cookie_user_sid . ', Value=' . $this->_phpbb_user_session_id . ')');
761            return false;
762        }
763        // Get session data from database.
764        $query = "SELECT session_id, session_user_id
765                  FROM {$this->_phpbb_conf['table_prefix']}sessions
766                  WHERE session_id = ?";
767        $result = $this->_phpbb_sql_link->prepare($query);
768        if ($result === false) {
769            $this->dbglog('error while preparing query for session');
770            return false;
771        }
772        if (!$result->execute(array($this->_phpbb_user_session_id))) {
773            $this->dbglog('error while executing query for session');
774            return false;
775        }
776        $row = $result->fetch(PDO::FETCH_ASSOC);
777        if ($phpbb_cookie_user_id !== (int)$row['session_user_id']) {
778            $this->dbglog('invalid SID/User ID pair');
779            $result->closeCursor();
780            $result = null;
781            return false;
782        }
783        $this->_phpbb_user_id = (int)$row['session_user_id'];
784        $this->_phpbb_sessiontime = $row['session_time'];
785        $result->closeCursor();
786        $result = null;
787        // Update session time.
788        $current_time = time();
789        if ($current_time > $this->_phpbb_sessiontime) {
790            $query = "UPDATE {$this->_phpbb_conf['table_prefix']}sessions
791                      SET session_time = ?
792                      WHERE session_id = ?";
793            $result = $this->_phpbb_sql_link->prepare($query);
794            if ($result === false) {
795                $this->dbglog('error while preparing query for session update');
796                return false;
797            }
798            if (!$result->execute(array($current_time, $this->_phpbb_user_session_id))) {
799                $this->dbglog('error while executing query for session update');
800            }
801            $result = null;
802        }
803        // Check for guest session.
804        if ($this->_phpbb_user_id === 1) {
805            return false;
806        }
807        // Get username from database.
808        $query = "SELECT user_id, username, user_email, user_type
809                  FROM {$this->_phpbb_conf['table_prefix']}users
810                  WHERE user_id = ?";
811        $result = $this->_phpbb_sql_link->prepare($query);
812        if ($result === false) {
813            $this->dbglog('error while preparing query for username');
814            return false;
815        }
816        if (!$result->execute(array($this->_phpbb_user_id))) {
817            $this->dbglog('error while executing query for username');
818            return false;
819        }
820        $row = $result->fetch(PDO::FETCH_ASSOC);
821        $this->_phpbb_user_type = (int)$row['user_type'];
822        $this->_phpbb_username = $row['username'];
823        $this->_phpbb_user_email = $row['user_email'];
824        $result->closeCursor();
825        $result = null;
826        // Get user groups from database.
827        $this->get_phpbb_user_groups();
828        return true;
829    }
830}
831