1<?php 2/* 3V5.10 10 Nov 2009 (c) 2000-2009 John Lim (jlim#natsoft.com). All rights reserved. 4 Released under both BSD license and Lesser GPL library license. 5 Whenever there is any discrepancy between the two licenses, 6 the BSD license will take precedence. See License.txt. 7 Set tabs to 4 for best viewing. 8 9 Latest version is available at http://adodb.sourceforge.net 10 11 Original Authors: Martin Jansen <mj#php.net> 12 Richard Tango-Lowy <richtl#arscognita.com> 13*/ 14 15require_once 'Auth/Container.php'; 16require_once 'adodb.inc.php'; 17require_once 'adodb-pear.inc.php'; 18require_once 'adodb-errorpear.inc.php'; 19 20/** 21 * Storage driver for fetching login data from a database using ADOdb-PHP. 22 * 23 * This storage driver can use all databases which are supported 24 * by the ADBdb DB abstraction layer to fetch login data. 25 * See http://php.weblogs.com/adodb for information on ADOdb. 26 * NOTE: The ADOdb directory MUST be in your PHP include_path! 27 * 28 * @author Richard Tango-Lowy <richtl@arscognita.com> 29 * @package Auth 30 * @version $Revision: 1.3 $ 31 */ 32class Auth_Container_ADOdb extends Auth_Container 33{ 34 35 /** 36 * Additional options for the storage container 37 * @var array 38 */ 39 var $options = array(); 40 41 /** 42 * DB object 43 * @var object 44 */ 45 var $db = null; 46 var $dsn = ''; 47 48 /** 49 * User that is currently selected from the DB. 50 * @var string 51 */ 52 var $activeUser = ''; 53 54 // {{{ Constructor 55 56 /** 57 * Constructor of the container class 58 * 59 * Initate connection to the database via PEAR::ADOdb 60 * 61 * @param string Connection data or DB object 62 * @return object Returns an error object if something went wrong 63 */ 64 function Auth_Container_ADOdb($dsn) 65 { 66 $this->_setDefaults(); 67 68 if (is_array($dsn)) { 69 $this->_parseOptions($dsn); 70 71 if (empty($this->options['dsn'])) { 72 PEAR::raiseError('No connection parameters specified!'); 73 } 74 } else { 75 // Extract db_type from dsn string. 76 $this->options['dsn'] = $dsn; 77 } 78 } 79 80 // }}} 81 // {{{ _connect() 82 83 /** 84 * Connect to database by using the given DSN string 85 * 86 * @access private 87 * @param string DSN string 88 * @return mixed Object on error, otherwise bool 89 */ 90 function _connect($dsn) 91 { 92 if (is_string($dsn) || is_array($dsn)) { 93 if(!$this->db) { 94 $this->db = ADONewConnection($dsn); 95 if( $err = ADODB_Pear_error() ) { 96 return PEAR::raiseError($err); 97 } 98 } 99 100 } else { 101 return PEAR::raiseError('The given dsn was not valid in file ' . __FILE__ . ' at line ' . __LINE__, 102 41, 103 PEAR_ERROR_RETURN, 104 null, 105 null 106 ); 107 } 108 109 if(!$this->db) { 110 return PEAR::raiseError(ADODB_Pear_error()); 111 } else { 112 return true; 113 } 114 } 115 116 // }}} 117 // {{{ _prepare() 118 119 /** 120 * Prepare database connection 121 * 122 * This function checks if we have already opened a connection to 123 * the database. If that's not the case, a new connection is opened. 124 * 125 * @access private 126 * @return mixed True or a DB error object. 127 */ 128 function _prepare() 129 { 130 if(!$this->db) { 131 $res = $this->_connect($this->options['dsn']); 132 } 133 return true; 134 } 135 136 // }}} 137 // {{{ query() 138 139 /** 140 * Prepare query to the database 141 * 142 * This function checks if we have already opened a connection to 143 * the database. If that's not the case, a new connection is opened. 144 * After that the query is passed to the database. 145 * 146 * @access public 147 * @param string Query string 148 * @return mixed a DB_result object or DB_OK on success, a DB 149 * or PEAR error on failure 150 */ 151 function query($query) 152 { 153 $err = $this->_prepare(); 154 if ($err !== true) { 155 return $err; 156 } 157 return $this->db->query($query); 158 } 159 160 // }}} 161 // {{{ _setDefaults() 162 163 /** 164 * Set some default options 165 * 166 * @access private 167 * @return void 168 */ 169 function _setDefaults() 170 { 171 $this->options['db_type'] = 'mysql'; 172 $this->options['table'] = 'auth'; 173 $this->options['usernamecol'] = 'username'; 174 $this->options['passwordcol'] = 'password'; 175 $this->options['dsn'] = ''; 176 $this->options['db_fields'] = ''; 177 $this->options['cryptType'] = 'md5'; 178 } 179 180 // }}} 181 // {{{ _parseOptions() 182 183 /** 184 * Parse options passed to the container class 185 * 186 * @access private 187 * @param array 188 */ 189 function _parseOptions($array) 190 { 191 foreach ($array as $key => $value) { 192 if (isset($this->options[$key])) { 193 $this->options[$key] = $value; 194 } 195 } 196 197 /* Include additional fields if they exist */ 198 if(!empty($this->options['db_fields'])){ 199 if(is_array($this->options['db_fields'])){ 200 $this->options['db_fields'] = join($this->options['db_fields'], ', '); 201 } 202 $this->options['db_fields'] = ', '.$this->options['db_fields']; 203 } 204 } 205 206 // }}} 207 // {{{ fetchData() 208 209 /** 210 * Get user information from database 211 * 212 * This function uses the given username to fetch 213 * the corresponding login data from the database 214 * table. If an account that matches the passed username 215 * and password is found, the function returns true. 216 * Otherwise it returns false. 217 * 218 * @param string Username 219 * @param string Password 220 * @return mixed Error object or boolean 221 */ 222 function fetchData($username, $password) 223 { 224 // Prepare for a database query 225 $err = $this->_prepare(); 226 if ($err !== true) { 227 return PEAR::raiseError($err->getMessage(), $err->getCode()); 228 } 229 230 // Find if db_fields contains a *, i so assume all col are selected 231 if(strstr($this->options['db_fields'], '*')){ 232 $sql_from = "*"; 233 } 234 else{ 235 $sql_from = $this->options['usernamecol'] . ", ".$this->options['passwordcol'].$this->options['db_fields']; 236 } 237 238 $query = "SELECT ".$sql_from. 239 " FROM ".$this->options['table']. 240 " WHERE ".$this->options['usernamecol']." = " . $this->db->Quote($username); 241 242 $ADODB_FETCH_MODE = ADODB_FETCH_ASSOC; 243 $rset = $this->db->Execute( $query ); 244 $res = $rset->fetchRow(); 245 246 if (DB::isError($res)) { 247 return PEAR::raiseError($res->getMessage(), $res->getCode()); 248 } 249 if (!is_array($res)) { 250 $this->activeUser = ''; 251 return false; 252 } 253 if ($this->verifyPassword(trim($password, "\r\n"), 254 trim($res[$this->options['passwordcol']], "\r\n"), 255 $this->options['cryptType'])) { 256 // Store additional field values in the session 257 foreach ($res as $key => $value) { 258 if ($key == $this->options['passwordcol'] || 259 $key == $this->options['usernamecol']) { 260 continue; 261 } 262 // Use reference to the auth object if exists 263 // This is because the auth session variable can change so a static call to setAuthData does not make sence 264 if(is_object($this->_auth_obj)){ 265 $this->_auth_obj->setAuthData($key, $value); 266 } else { 267 Auth::setAuthData($key, $value); 268 } 269 } 270 271 return true; 272 } 273 274 $this->activeUser = $res[$this->options['usernamecol']]; 275 return false; 276 } 277 278 // }}} 279 // {{{ listUsers() 280 281 function listUsers() 282 { 283 $err = $this->_prepare(); 284 if ($err !== true) { 285 return PEAR::raiseError($err->getMessage(), $err->getCode()); 286 } 287 288 $retVal = array(); 289 290 // Find if db_fileds contains a *, i so assume all col are selected 291 if(strstr($this->options['db_fields'], '*')){ 292 $sql_from = "*"; 293 } 294 else{ 295 $sql_from = $this->options['usernamecol'] . ", ".$this->options['passwordcol'].$this->options['db_fields']; 296 } 297 298 $query = sprintf("SELECT %s FROM %s", 299 $sql_from, 300 $this->options['table'] 301 ); 302 $res = $this->db->getAll($query, null, DB_FETCHMODE_ASSOC); 303 304 if (DB::isError($res)) { 305 return PEAR::raiseError($res->getMessage(), $res->getCode()); 306 } else { 307 foreach ($res as $user) { 308 $user['username'] = $user[$this->options['usernamecol']]; 309 $retVal[] = $user; 310 } 311 } 312 return $retVal; 313 } 314 315 // }}} 316 // {{{ addUser() 317 318 /** 319 * Add user to the storage container 320 * 321 * @access public 322 * @param string Username 323 * @param string Password 324 * @param mixed Additional information that are stored in the DB 325 * 326 * @return mixed True on success, otherwise error object 327 */ 328 function addUser($username, $password, $additional = "") 329 { 330 if (function_exists($this->options['cryptType'])) { 331 $cryptFunction = $this->options['cryptType']; 332 } else { 333 $cryptFunction = 'md5'; 334 } 335 336 $additional_key = ''; 337 $additional_value = ''; 338 339 if (is_array($additional)) { 340 foreach ($additional as $key => $value) { 341 $additional_key .= ', ' . $key; 342 $additional_value .= ", '" . $value . "'"; 343 } 344 } 345 346 $query = sprintf("INSERT INTO %s (%s, %s%s) VALUES ('%s', '%s'%s)", 347 $this->options['table'], 348 $this->options['usernamecol'], 349 $this->options['passwordcol'], 350 $additional_key, 351 $username, 352 $cryptFunction($password), 353 $additional_value 354 ); 355 356 $res = $this->query($query); 357 358 if (DB::isError($res)) { 359 return PEAR::raiseError($res->getMessage(), $res->getCode()); 360 } else { 361 return true; 362 } 363 } 364 365 // }}} 366 // {{{ removeUser() 367 368 /** 369 * Remove user from the storage container 370 * 371 * @access public 372 * @param string Username 373 * 374 * @return mixed True on success, otherwise error object 375 */ 376 function removeUser($username) 377 { 378 $query = sprintf("DELETE FROM %s WHERE %s = '%s'", 379 $this->options['table'], 380 $this->options['usernamecol'], 381 $username 382 ); 383 384 $res = $this->query($query); 385 386 if (DB::isError($res)) { 387 return PEAR::raiseError($res->getMessage(), $res->getCode()); 388 } else { 389 return true; 390 } 391 } 392 393 // }}} 394} 395 396function showDbg( $string ) { 397 print " 398-- $string</P>"; 399} 400function dump( $var, $str, $vardump = false ) { 401 print "<H4>$str</H4><pre>"; 402 ( !$vardump ) ? ( print_r( $var )) : ( var_dump( $var )); 403 print "</pre>"; 404} 405?> 406