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