1<?php
2/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker: */
3
4/**
5 * Reduced storage driver for use against PEAR DB
6 *
7 * PHP versions 4 and 5
8 *
9 * LICENSE: This source file is subject to version 3.01 of the PHP license
10 * that is available through the world-wide-web at the following URI:
11 * http://www.php.net/license/3_01.txt.  If you did not receive a copy of
12 * the PHP License and are unable to obtain it through the web, please
13 * send a note to license@php.net so we can mail you a copy immediately.
14 *
15 * @category   Authentication
16 * @package    Auth
17 * @author     Martin Jansen <mj@php.net>
18 * @author     Adam Ashley <aashley@php.net>
19 * @copyright  2001-2006 The PHP Group
20 * @license    http://www.php.net/license/3_01.txt  PHP License 3.01
21 * @version    CVS: $Id: DBLite.php 256753 2008-04-04 07:57:02Z aashley $
22 * @link       http://pear.php.net/package/Auth
23 * @since      File available since Release 1.3.0
24 */
25
26/**
27 * Include Auth_Container base class
28 */
29require_once 'Auth/Container.php';
30/**
31 * Include PEAR DB package
32 */
33require_once 'DB.php';
34
35/**
36 * A lighter storage driver for fetching login data from a database
37 *
38 * This driver is derived from the DB storage container but
39 * with the user manipulation function removed for smaller file size
40 * by the PEAR DB abstraction layer to fetch login data.
41 *
42 * @category   Authentication
43 * @package    Auth
44 * @author     Martin Jansen <mj@php.net>
45 * @author     Adam Ashley <aashley@php.net>
46 * @copyright  2001-2006 The PHP Group
47 * @license    http://www.php.net/license/3_01.txt  PHP License 3.01
48 * @version    Release: @package_version@  File: $Revision: 256753 $
49 * @link       http://pear.php.net/package/Auth
50 * @since      Class available since Release 1.3.0
51 */
52class Auth_Container_DBLite extends Auth_Container
53{
54
55    // {{{ properties
56
57    /**
58     * Additional options for the storage container
59     * @var array
60     */
61    var $options = array();
62
63    /**
64     * DB object
65     * @var object
66     */
67    var $db = null;
68    var $dsn = '';
69
70    /**
71     * User that is currently selected from the DB.
72     * @var string
73     */
74    var $activeUser = '';
75
76    // }}}
77    // {{{ Auth_Container_DBLite() [constructor]
78
79    /**
80     * Constructor of the container class
81     *
82     * Initate connection to the database via PEAR::DB
83     *
84     * @param  string Connection data or DB object
85     * @return object Returns an error object if something went wrong
86     */
87    function Auth_Container_DBLite($dsn)
88    {
89        $this->options['table']       = 'auth';
90        $this->options['usernamecol'] = 'username';
91        $this->options['passwordcol'] = 'password';
92        $this->options['dsn']         = '';
93        $this->options['db_fields']   = '';
94        $this->options['cryptType']   = 'md5';
95        $this->options['db_options']  = array();
96        $this->options['db_where']    = '';
97        $this->options['auto_quote']  = true;
98
99        if (is_array($dsn)) {
100            $this->_parseOptions($dsn);
101            if (empty($this->options['dsn'])) {
102                PEAR::raiseError('No connection parameters specified!');
103            }
104        } else {
105            $this->options['dsn'] = $dsn;
106        }
107    }
108
109    // }}}
110    // {{{ _connect()
111
112    /**
113     * Connect to database by using the given DSN string
114     *
115     * @access private
116     * @param  string DSN string
117     * @return mixed  Object on error, otherwise bool
118     */
119    function _connect(&$dsn)
120    {
121        $this->log('Auth_Container_DBLite::_connect() called.', AUTH_LOG_DEBUG);
122        if (is_string($dsn) || is_array($dsn)) {
123            $this->db =& DB::connect($dsn, $this->options['db_options']);
124        } elseif (is_subclass_of($dsn, "db_common")) {
125            $this->db =& $dsn;
126        } else {
127            return PEAR::raiseError("Invalid dsn or db object given");
128        }
129
130        if (DB::isError($this->db) || PEAR::isError($this->db)) {
131            return PEAR::raiseError($this->db->getMessage(), $this->db->getCode());
132        } else {
133            return true;
134        }
135    }
136
137    // }}}
138    // {{{ _prepare()
139
140    /**
141     * Prepare database connection
142     *
143     * This function checks if we have already opened a connection to
144     * the database. If that's not the case, a new connection is opened.
145     *
146     * @access private
147     * @return mixed True or a DB error object.
148     */
149    function _prepare()
150    {
151        if (!DB::isConnection($this->db)) {
152            $res = $this->_connect($this->options['dsn']);
153            if (DB::isError($res) || PEAR::isError($res)) {
154                return $res;
155            }
156        }
157        if ($this->options['auto_quote'] && $this->db->dsn['phptype'] != 'sqlite') {
158            if (strpos('.', $this->options['table']) === false) {
159                $this->options['final_table'] = $this->db->quoteIdentifier($this->options['table']);
160            } else {
161                $t = explode('.', $this->options['table']);
162                for ($i = 0, $count = count($t); $i < $count; $i++)
163                    $t[$i] = $this->db->quoteIdentifier($t[$i]);
164                $this->options['final_table'] = implode('.', $t);
165            }
166            $this->options['final_usernamecol'] = $this->db->quoteIdentifier($this->options['usernamecol']);
167            $this->options['final_passwordcol'] = $this->db->quoteIdentifier($this->options['passwordcol']);
168        } else {
169            $this->options['final_table'] = $this->options['table'];
170            $this->options['final_usernamecol'] = $this->options['usernamecol'];
171            $this->options['final_passwordcol'] = $this->options['passwordcol'];
172        }
173        return true;
174    }
175
176    // }}}
177    // {{{ _parseOptions()
178
179    /**
180     * Parse options passed to the container class
181     *
182     * @access private
183     * @param  array
184     */
185    function _parseOptions($array)
186    {
187        foreach ($array as $key => $value) {
188            if (isset($this->options[$key])) {
189                $this->options[$key] = $value;
190            }
191        }
192    }
193
194    // }}}
195    // {{{ _quoteDBFields()
196
197    /**
198     * Quote the db_fields option to avoid the possibility of SQL injection.
199     *
200     * @access private
201     * @return string A properly quoted string that can be concatenated into a
202     * SELECT clause.
203     */
204    function _quoteDBFields()
205    {
206        if (isset($this->options['db_fields'])) {
207            if (is_array($this->options['db_fields'])) {
208                if ($this->options['auto_quote']) {
209                    $fields = array();
210                    foreach ($this->options['db_fields'] as $field) {
211                        $fields[] = $this->db->quoteIdentifier($field);
212                    }
213                    return implode(', ', $fields);
214                } else {
215                    return implode(', ', $this->options['db_fields']);
216                }
217            } else {
218                if (strlen($this->options['db_fields']) > 0) {
219                    if ($this->options['auto_quote']) {
220                        return $this->db->quoteIdentifier($this->options['db_fields']);
221                    } else {
222                        $this->options['db_fields'];
223                    }
224                }
225            }
226        }
227
228        return '';
229    }
230
231    // }}}
232    // {{{ fetchData()
233
234    /**
235     * Get user information from database
236     *
237     * This function uses the given username to fetch
238     * the corresponding login data from the database
239     * table. If an account that matches the passed username
240     * and password is found, the function returns true.
241     * Otherwise it returns false.
242     *
243     * @param   string Username
244     * @param   string Password
245     * @return  mixed  Error object or boolean
246     */
247    function fetchData($username, $password)
248    {
249        $this->log('Auth_Container_DBLite::fetchData() called.', AUTH_LOG_DEBUG);
250        // Prepare for a database query
251        $err = $this->_prepare();
252        if ($err !== true) {
253            return PEAR::raiseError($err->getMessage(), $err->getCode());
254        }
255
256        // Find if db_fields contains a *, if so assume all col are selected
257        if (is_string($this->options['db_fields'])
258            && strstr($this->options['db_fields'], '*')) {
259            $sql_from = "*";
260        } else {
261            $sql_from = $this->options['final_usernamecol'].
262                ", ".$this->options['final_passwordcol'];
263
264            if (strlen($fields = $this->_quoteDBFields()) > 0) {
265                $sql_from .= ', '.$fields;
266            }
267        }
268
269        $query = "SELECT ".$sql_from.
270                " FROM ".$this->options['final_table'].
271                " WHERE ".$this->options['final_usernamecol']." = ".$this->db->quoteSmart($username);
272
273        // check if there is an optional parameter db_where
274        if ($this->options['db_where'] != '') {
275            // there is one, so add it to the query
276            $query .= " AND ".$this->options['db_where'];
277        }
278
279        $this->log('Running SQL against DB: '.$query, AUTH_LOG_DEBUG);
280
281        $res = $this->db->getRow($query, null, DB_FETCHMODE_ASSOC);
282
283        if (DB::isError($res)) {
284            return PEAR::raiseError($res->getMessage(), $res->getCode());
285        }
286        if (!is_array($res)) {
287            $this->activeUser = '';
288            return false;
289        }
290        if ($this->verifyPassword(trim($password, "\r\n"),
291                                  trim($res[$this->options['passwordcol']], "\r\n"),
292                                  $this->options['cryptType'])) {
293            // Store additional field values in the session
294            foreach ($res as $key => $value) {
295                if ($key == $this->options['passwordcol'] ||
296                    $key == $this->options['usernamecol']) {
297                    continue;
298                }
299
300                $this->log('Storing additional field: '.$key, AUTH_LOG_DEBUG);
301
302                // Use reference to the auth object if exists
303                // This is because the auth session variable can change so a static call to setAuthData does not make sence
304                if (is_object($this->_auth_obj)) {
305                    $this->_auth_obj->setAuthData($key, $value);
306                } else {
307                    Auth::setAuthData($key, $value);
308                }
309            }
310            $this->activeUser = $res[$this->options['usernamecol']];
311            return true;
312        }
313        $this->activeUser = $res[$this->options['usernamecol']];
314        return false;
315    }
316
317    // }}}
318
319}
320?>
321