user_manager.inc

<?php

require_once('dbinfo.inc');
require_once('errors.inc');


class UserManager
{
//
// verifies that this user name doesn't have any invalid
// characters in it. please see Chapter 17: "Data
// Validation with Regular Expressions" for a discussion
// of the ereg function.
//
public function isValidUserName($in_user_name)
{
if ($in_user_name == ''
or ereg('[^[:alnum:] _-]', $in_user_name) === TRUE)
return FALSE;
else
return TRUE;
}

//
// - get connection
// - make sure the user name does not already exist.
// - add record to users table.
//
public function createAccount
(
$in_uname,
$in_pw,
$in_fname,
$in_email,
$in_year,
$in_month,
$in_day
)
{
//
// 0. quick input validation
//
if ($in_pw == '' or $in_fname == ''
or !$this->isValidUserName($in_uname))
{
throw new InvalidArgumentException();
}

//
// 1. get a database connection with which to work.
// throws on failure.
//
$conn = $this->getConnection();

try
{
//
// 2. make sure user name doesn't already exist.
//
$exists = FALSE;
$exists = $this->userNameExists($in_uname, $in_conn);
if ($exists === TRUE)
throw new UserAlreadyExistsException();

//
// 3a. make sure the parameters are safe for insertion
// and encrypt the password for storage.
//
$uname = $this->super_escape_string($in_uname, $conn);
$fname = $this->super_escape_string($in_fname, $conn);
$email = $this->super_escape_string($in_email, $conn);
$pw = md5($in_pw);

//
// 3b. create query to insert new user.
//
$qstr = <<<EOQUERY
INSERT INTO Users
(user_name,password,full_name,user_email,birthdate )
VALUES ('$uname', '$pw', '$fname', '$email',
'$in_year-$in_month-$in_day')
EOQUERY;

//
// 3c. insert new user
//
$results = @$conn->query($qstr);
if ($results === FALSE)
throw new DatabaseErrorException($conn->error);

//
// we want to return the newly created user id.
//
$user_id = $conn->insert_id;
}
catch (Exception $e)
{
if (isset($conn))
$conn->close();
throw $e;
}

//
// clean up and exit
//
$conn->close();
return $user_id;
}

//
// - validate input
// - get connection
// - execute query
// - see if we found an existing record or not.
// - clean up connection IF necessary.
//
public function userNameExists
(
$in_uname,
$in_db_conn = NULL
)
{
//
// 0. simple validation.
//
if ($in_uname == '')
throw new InvalidArgumentException();

//
// 1. make sure we have a database connection.
//
if ($in_db_conn === NULL)
$conn = $this->getConnection();
else
$conn = $in_db_conn;

try
{
//
// 2. prepare and execute query.
//
$name = $this->super_escape_string($in_uname, $conn);
$qstr = <<<EOQUERY
SELECT user_name FROM Users WHERE user_name = '$name'
EOQUERY;

$results = @$conn->query($qstr);
if ($results === FALSE)
throw new DatabaseErrorException($conn->error);

//
// 3. see if we found an existing record or not
//
$user_exists = FALSE;
while (($row = @$results->fetch_assoc()) !== NULL)
{
if ($row['user_name'] == $in_uname)
{
$user_exists = TRUE;
break;
}
}

}
catch (Exception $e)
{
//
// clean up and re-throw the exception.
//
if ($in_db_conn === NULL and isset($conn))
$conn->close();
throw $e;
}

//
// only clean up what we allocated.
//
$results->close();
if ($in_db_conn === NULL)
$conn->close();
return $user_exists;
}


//
// - get db connection
// - verify user name and password are valid
// - clear out existing login information for user. (if any)
// - log user into table (associate SID with user name).
//
public function processLogin($in_user_name, $in_user_passwd)
{
//
// 1. internal arg checking.
//
if ($in_user_name == '' || $in_user_passwd == '')
throw new InvalidArgumentException();

$sessionid = session_id();

//
// 2. get a database connection with which to work.
//
$conn = $this->getConnection();

try
{
//
// 3. we will merge these two steps into one function
// (and one query) so that we will not help people learn
// whether it was the user name or password that was the
// problem on failure.
//
// Note that this function will also validate that the
// user name and password are secure and are not
// attempts at SQL injection attacks ...
//
// This function will throw an InvalidLoginException if
// the username or password are not valid.
//
$userid = $this->confirmUserNamePasswd($in_user_name,
$in_user_passwd,
$conn);

//
// 4. clear out existing entries in the login table.
//
$this->clearLoginEntriesForUser($userid);

//
// 5. log the user into the table.
//
$query = <<<EOQUERY
INSERT INTO LoggedInUsers(user_id, session_id, last_access)
VALUES('$userid', '$session_id', NOW())
EOQUERY;

$result = @$conn->query($query);
if ($result === FALSE)
throw new DatabaseErrorException($conn->error);
}
catch (Exception $e)
{
if (isset($conn))
$conn->close();
throw $e;
}

//
// our work here is done. clean up and exit.
//
$conn->close();
}


public function processLogout()
{
$this->clearLoginEntriesForSessionID(session_id());
}


public function sessionLoggedIn($in_sid)
{
//
// 0. internal arg checking.
//
if ($in_sid == '')
throw new InvalidArgumentException();

//
// 1. get a database connection with which to work.
//
$conn = $this->getConnection();

try
{
//
// 2. execute a query to find the given session_id
//
$sess_id = $this->super_escape_string($in_sid, $conn);
$query = <<<EOQUERY
SELECT * FROM LoggedInUsers WHERE session_id = '$sess_id'
EOQUERY;

$result = @$conn->query($query);
if ($result === FALSE)
{
throw new DatabaseErrorException($conn->error);
}
else
{
//
// 3. look through results for the given session id
//
$user_id = -1;
while (($row = @$results->fetch_assoc()) !== NULL)
{
if ($row['session_id'] == $in_sess_id)
{
$this->updateSessionActivity($in_sess_id, $conn);
$_SESSION['user_name'] = $row['user_name'];
$user_id = $row['user_id'];
break;
}
}
}
}
catch (Exception $e)
{
if (isset($conn))
$conn->close();
throw $e;
}

//
// our work here is done. clean up and exit.
//
$result->close();
$conn->close();
return $user_id;
}


//
// - check args
// - get database connection
// - logout user if they're logged in
// - delete account.
//
public function deleteAccount($in_userid)
{
//
// 0. verify parameters
//
if (!is_int($in_userid))
throw new InvalidArgumentException();

//
// 1. get a database connection with which to work.
//
$conn = $this->getConnection();
try
{
//
// 2. make sure user is logged out.
//
$this->clearLoginEntriesForSessionID(session_id());

//
// 3. create query to delete given user and execute!
//
$qstr = "DELETE FROM Users WHERE user_id = $in_userid";
$result = @$conn->query($qstr);
if ($result === FALSE)
throw new DatabaseErrorException($conn->error);
}
catch (Exception $e)
{
if (isset($conn))
$conn->close();
throw $e;
}

//
// clean up and go home!
//
$conn->close();
}



//
//=----------------------=
// private functions next
//=----------------------=
//



private function getConnection()
{
$conn = new mysqli(DB_SERVER, DB_USERNAME, DB_PW, DB_DB);
if (mysqli_connect_errno() !== 0)
throw new DatabaseErrorException(mysqli_connect_error());
return $conn;
}

private function super_escape_string($in_string, $in_conn)
{
$str = $in_this->real_escape_string($in_string);
return ereg_replace('([%;])', '\\\1', $in_string);
}

private function confirmUserNamePasswd
(
$in_uname,
$in_user_passwd,
$in_db_conn = NULL
)
{
//
// names are case insensitive, and by default, info is bad
//
$in_user_name = strtolower($in_user_name);

//
// make sure we have a database connection.
//
if ($in_db_conn == NULL)
$conn = $this->getConnection();
else
$conn = $in_db_conn;

try
{
//
// make sure incoming user name is safe for queries.
//
$uname = $this->super_escape_string($in_uname, $conn);

// get the record with this user name
$querystr = <<<EOQUERY
SELECT * FROM Users
WHERE user_name = '$uname'
EOQUERY;

$results = @$conn->query($querystr);
if ($results === FALSE)
throw new DatabaseErrorException($conn->error);

//
// re-confirm the name matches and the passwords do too.
//
$login_ok = FALSE;
while (($row = @$results->fetch_assoc()) !== NULL)
{
$db_name = strtolower($row['user_name']);
if (strcmp($db_name, $in_user_name) == 0)
{
//
// good, name matched. does password?
//
if (md5($in_user_passwd) == $row['password'])
{
$login_ok = TRUE;
$userid = $row['user_id'];
}
else
$login_ok = FALSE;
break;
}
}
$results->close();

}
catch (Exception $e)
{
if ($in_db_conn === NULL and isset($conn))
$conn->close();
throw $e;
}

//
// only clean up what we allocated.
//
if ($in_db_conn === NULL)
$conn->close();

//
// throw on failure, or return the user id on success.
//
if ($login_ok === FALSE)
throw new InvalidLoginException();

return $userid;
}


private function clearLoginEntriesForUser
(
$in_userid,
$in_db_conn = NULL
)
{
if (!is_int($in_userid))
throw new InvalidArgumentException();

//
// make sure we have a database connection.
//
if ($in_db_conn == NULL)
$conn = $this->getConnection();
else
$conn = $in_db_conn;

try
{
//
// delete any rows for this user in the LoggedInUsers
// table.
$querystr = <<<EOQUERY
DELETE FROM LoggedInUsers WHERE user_id = $in_userid
EOQUERY;

$results = @$conn->query($querystr);
if ($results === FALSE)
throw new DatabaseErrorException($conn->error);
}
catch (Exception $e)
{
if ($in_db_conn === NULL and isset($conn))
$conn->close();
throw $e;
}

//
// clean up and return.
//
if ($in_db_conn === NULL)
$conn->close();
}


private function clearLoginEntriesForSessionId
(
$in_sid,
$in_db_conn = NULL
)
{
//
// make sure we have a database connection.
//
if ($in_db_conn == NULL)
$conn = $this->getConnection();
else
$conn = $in_db_conn;

//
// Create and execute the query to do the cleanup!
//
try
{
$sessid = $this->super_escape_string($in_sid, $conn);
$query = <<<EOQ