view resume | view portfolio | view code samples | contact | about

Login class(PHP4) | MySQL class(PHP5) | session handler(PHP5) | python pinger(Python) | Qmail popper(PHP4)

<?php
/**
/**
* @note Login class::PHP4::login-class-php
* @author Stefan Antonowicz
*
* I use the excellent PHPMailer class by Brent R. Matzelle for handling the mailings.
* The MySQL SQL statements to create the appropriate tables are in the comments at the bottom of the page.
* I'm including a session handling class - this is the same session handler linked to above
*/

require_once( 'dbSession.inc.php' );
require_once( 
'PHPMailer.inc.php' );

class 
Login {
    var 
$u$p$uid;
    var 
$errarr = array( );
        var 
$_sql$_aurl;
        var 
$_crypt ''// add private salt
        
var $dsn ''// mysql dsn connection
        
var $webroot "http://localhost/login";
        var 
$_captcha NULL// Optional captcha
        
var $mail_tpl_path ''// Mail template path
        
        
        
        /* Constructor.  Build the DB connection and start the session */
        
function Login( )
        {
            
extractparse_url$this->dsn ) );
            
$conn mysql_connect$host$user$passtrue );
            
$path str_replace'/'''$path );
            
mysql_select_db$path$conn );
            
            
$this->_db $conn;
            
$this->_sess session_start( );
        }
    
        
        
/* Process a login request.  If successful, set the user_id in the session */
        
function process$username$password$remember FALSE )
        {
            
$this->logoutFALSE );
            
$this->$this->clean$username );
            
$this->$this->clean$password );
            
$this->errarr = array( );

            if( 
$this->_check( ) )
            {
                
$_SESSION['uid'] = $this->uid;
                
$_SESSION['uname'] = $this->u;
                
$this->_sess->make_crypt$this->);

                
                
                
setcookie'login'''time() - 3600);
                
                if( 
$remember )
                {
                    
setcookie'login'$this->utime()+60*60*24*365 );
                }
                    
                return( 
true );
            } 
            
            return( 
false );
        }
    
        
        
/* Check to see if someone is logged in. You can add roles management in 
        this method, as well */
        
function check( )
        {
            if(! 
$_SESSION['uid'] || ! $_SESSION['uname'] ) return( false );
            if(! 
$this->_sess->verify$_SESSION['uname'] ) ) return( false );
            return( 
true );
        }        
        
        
/* Wrapper that returns the error array, if it exists */
        
function error( )
        {
            if( 
$this->errarr )  return( $this->errarr );
            return( 
false );            
        }
        
        
        function 
remember( )
        {
            if( 
$_COOKIE['login'] )
            {
                return( 
$_COOKIE['login'] );
            }
            return( 
false );
        }
        
        
        
/*  Logout method; kills the session and any cookies.  I added a "full"
                handler in there to allow for a partial logout without destroying the
                session, like in process() and create()
        */
        
function logout$full TRUE )
        {
            
$_SESSION = array( );
                
            if( 
$full )
            {
                
setcookie'user'''time() - 42000'/' );
                
setcookie'PHPSESSID'''time() - 42000'/' );
                
session_destroy( );
            }
            
            return( 
true );
        }
        
        
/* Processes a request from an activation email */
        
function activate$str )
        {
            
parse_str$str );
            
$this->uid = ( int ) $i;
            
$stamp = ( int ) $t;
            
            
$this->_sql "SELECT a.activity_id, u.user_name, a.activity_hash FROM users u
                                            LEFT JOIN activity a ON u.user_id = a.user_id
                                            WHERE 
                                            u.user_id = $this->uid
                                            AND activity_desc = 'Activation'
                                            AND activity_expired = 0
                                            ORDER BY activity_ts DESC
                                            LIMIT 1
                                            "
;

            
$res $this->_query( );
            if(! 
mysql_num_rows$res ) )
            {
                
$this->_errorLOGIN_FAIL );
                return( 
false );
            }
            
            
$user mysql_fetch_assoc$res );

            if( 
strcmpmd5$user['user_name'] . $stamp ), $user['activity_hash'] ) )
            {
                
$this->_errorBAD_ACTIVATION );
                return( 
false );
            }
            
            
$this->_sql "UPDATE users SET user_confirmed = 1 WHERE user_id = $this->uid LIMIT 1";
            
$this->_query();
            
$this->_sql "UPDATE activity SET activity_expired = 1 WHERE activity_id = {$user['activity_id']}";
            
$this->_query();
            
            return( 
true );
        }
        

        
        
/* Resend a request for an activation email */
        
function request_reactivate$u=''$e='' )
        {
            
// we have a username
            
if( $u )
            {
                
$this->$this->clean$u );
                
$this->_sql "SELECT user_id, user_name, user_email FROM users WHERE user_name= '$this->u' LIMIT 1";                
                
            } elseif( 
$e )
            {
                
$this->email $this->clean$user_email );
                
$this->_sql "SELECT user_id, user_name, user_email FROM users WHERE user_email = '$this->email' LIMIT 1";                
            }
            
            
$res $this->_query( );
            
            if(! 
mysql_num_rows$res ) )
            {
                
$this->_errorLOGIN_FAIL );
                return( 
false );
            }
            
            
$row mysql_fetch_assoc$res );
            
$this->$row['user_name'];
            
$this->uid $row['user_id'];
            
$this->email $row['user_email'];
            
            
// Kill all old versions, so there's no confusion
            
$this->_sql "UPDATE activity SET activity_expired = 1 WHERE activity_desc = 'Activation' AND
                                            user_id = $this->uid"
;
            
$this->_query( );
            
            
// Insert a record into the activity table
            
$code_creation_time time( );
            
$this->_generate_code$code_creation_time );
            
$this->_sql "INSERT INTO activity ( user_id, activity_ts, activity_hash, activity_desc) VALUES
                                        ( $this->uid, $code_creation_time, '$this->_code', 'Activation')"
;
            
$this->_query( );
            
            
// Mail it out
            
$this->_mailIt'reactivate.tpl' );        
        }
        
        
/* Resend a request for an activation email */
        
function request_password$email )
        {
            
$this->email $this->clean$email );
            
$this->_sql "SELECT user_id, user_name, user_email FROM users WHERE user_email = '$this->email' LIMIT 1";                

            
$res $this->_query( );
            
            if(! 
mysql_num_rows$res ) )
            {
                
$this->_errorLOGIN_FAIL );
                return( 
false );
            }
            
            
$row mysql_fetch_assoc$res );
            
$this->$row['user_name'];
            
$this->uid $row['user_id'];
            
$this->email $row['user_email'];
            
            
// Expire other requests for password reset, so only one is active at any given time
            
$this->_sql "UPDATE activity SET activity_expired = 1 WHERE user_id = $this->uid AND activity_desc = 'Password Reset'";
            
$this->_query( );
            
            
// Insert a new record into the db.
            
$code_creation_time time( );
            
$this->_generate_code$code_creation_time );
            
$this->_sql "INSERT INTO activity ( user_id, activity_ts, activity_hash, activity_desc) VALUES
                                        ( $this->uid, $code_creation_time, '$this->_code', 'Password Reset')"
;
            
$this->_query( );
            
            
// Mail it out
            
$this->_mailIt'password.tpl' );        
        }
        
        function 
reset_password$str )
        {
            
parse_str$str );
            
$this->uid = ( int ) $i;
            
$stamp = ( int ) $t;
            
            
$this->_sql "SELECT u.user_name, a.activity_hash FROM users u
                                            LEFT JOIN activity a ON u.user_id = a.user_id
                                            WHERE 
                                            u.user_id = $this->uid
                                            AND activity_desc = 'Password Reset'
                                            AND activity_expired = 0
                                            ORDER BY activity_ts DESC
                                            LIMIT 1
                                            "
;


            
$res $this->_query( );
            if(! 
mysql_num_rows$res ) )
            {
                
$this->_errorLOGIN_FAIL );
                return( 
false );
            }
            
            
$user mysql_fetch_assoc$res );

            if( 
strcmpmd5$user['user_name'] . $stamp ), $user['activity_hash'] ) )
            {
                
$this->_errorBAD_PASSWORD );
                return( 
false );
            }
            
            return( 
true );            
        }
        
        
        
/* Creates a new user.  Plenty of error checking in here */
        
function create$post_array )
        {
            
$clean = array( );
            if( 
$_SESSION['captcha'] )     $this->_captcha $_SESSION['captcha'];
            
$this->logoutFALSE );
            
            
// username
            
$clean['u'] = $this->clean$post_array['user_name'] );
            if( 
strlen$clean['u'] ) < )
            {
                
$this->_errorU_TOO_SHORT );
            } elseif( 
strlen$clean['u'] ) > 15 )
            {
                
$this->_errorU_TOO_LONG );
            } elseif( 
$this->_exists'user'$clean['u'] ) )
            {
                
$this->_errorU_ALREADY_EXISTS );
            } else 
            {
                
$this->$clean['u'];
            }
            
            
// email
            
$clean['e'] = $this->clean$post_array['email'] );
            if(! 
eregi"^[_a-z0-9-]+(\.[_a-z0-9-]+)*@[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,3})$"$clean['e'] ) )
            {
                
$this->_errorBAD_EMAIL );
            } elseif( 
$this->_exists'email'$clean['e'] ) )
            {
                
$this->_errorE_ALREADY_EXISTS );
            } else
            {
                
$this->email $clean['e'];
            }

            
// password
            
$clean['p'] = $post_array['password'];
            
$clean['v'] = $post_array['verify'];
            
            if( 
strlen$clean['p'] ) < )
            {
                
$this->_errorP_TOO_SHORT );
            } elseif( 
strcmp$clean['p'], $clean['v'] ) )
            {
                
$this->_errorP_NO_MATCH );
            } else
            {
                
$this->$clean['p'];
            }

            
// captcha, if set
            
if( $this->_captcha != '' )
            {
                
$clean['cap'] = $this->clean$post_array['captcha'] );
                if( 
strcmp$clean['cap'], $this->_captcha ) )
                {
                    
$this->_error'BAD_CAPTCHA' );
                }
            }
            
            if( 
$this->error( ) ) return( false );
            
            
// You made it!  Create this guy...
            
$this->_create( );
            return( 
true );
        }
        
        
/* A method to clean strings of illegal characters */        
        
function clean$str )
        {
            
// Remove invalid characters: ',",(),&,|
            
$illegal_characters = array( "'"'\\''"''('')''&''|' ); 
            
$str strip_tags$str );
            
$str str_replace$illegal_characters''$str );

            return( 
$str );
        }
        
        function 
save$post_array )
        {
            if(! 
$_SESSION['uid'] ) return( false );
            
$clean = array( );
            
// acceptable things to update in users table using this method
            
$accept = array( 'user_email''user_password' );
            foreach( 
$accept as $field )
            {
                switch( 
$field )
                {
                    case 
'user_email':
                        
$clean['e'] = $this->clean$post_array['email'] );
                        
$clean['ce'] = $this->clean$post_array['email_confirm'] );
                        if( 
$clean['e'] && $clean['ce'] )
                        {
                            if(! 
eregi"^[_a-z0-9-]+(\.[_a-z0-9-]+)*@[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,3})$"$clean['e'] ) )
                            {
                                
$this->_errorBAD_EMAIL );
                            } elseif( 
$this->_exists'email'$clean['e'] ) )
                            {
                                
$this->_errorE_ALREADY_EXISTS );
                            } elseif( 
strcmp$clean['e'], $clean['ce'] ) )
                            {
                                
$this->_errorBAD_EMAIL );
                            } else
                            {
                                
$arr[] = "user_email='{$clean['e']}'";
                            }
                        }
                    break;
                    
                    case 
'user_password':
                        
$clean['p'] = $post_array['password'];
                        
$clean['v'] = $post_array['verify'];
                        
                        if( 
$clean['p'] || $clean['v'] )
                        {
                            if( 
strlen$clean['p'] ) < )
                            {
                                
$this->_errorP_TOO_SHORT );
                            } elseif( 
strcmp$clean['p'], $clean['v'] ) )
                            {
                                
$this->_errorP_NO_MATCH );
                            } else
                            {
                                
$arr[] = "user_password = AES_ENCRYPT( '{clean['p']}', '" $this->$this->_crypt "')";
                            }
                        }
                    break;
                }
            }
        
            if( 
$arr )
            {
                
$this->_sql "UPDATE users SET " implode','$arr ) . " WHERE user_id = " $_SESSION['uid'];
                
$this->_query();
            }
            return( 
true );
        }
            
        
        
/* Generate the captcha code. The captcha itself is used on 
           a different page
        */
        
function captcha$width='120',$height='40',$characters='6' )
        {        
            
// Generate the code first    
            
$possible '23456789bcdfghjkmnpqrstvwxyz';
            
$this->_captcha '';
            
            
$i 0;
            while (
$i $characters) { 
                
$this->_captcha .= substr($possiblemt_rand(0strlen($possible)-1), 1);
                
$i++;
            }
            
$_SESSION['captcha'] = $this->_captcha;
        }    
        
        function 
update_password$pass$uid )
        {
            
            
$this->uid = ( int ) $uid;
            
$this->_sql "SELECT user_name FROM users WHERE user_id = $this->uid";
            
$res $this->_query( );
            
$user mysql_fetch_assoc$res );
            
            
$this->$user['user_name'];

            
$this->_sql "UPDATE users SET user_pass = AES_ENCRYPT('$pass', '" $this->$this->_crypt "' )  WHERE user_id = $this->uid LIMIT 1";
            
$this->_query( );
            
            
$this->_sql "UPDATE activity SET activity_expired = 1 WHERE user_id = $this->uid AND activity_desc = 'Password Reset'";
            
$this->_query( );            
            
            return( 
true );
        }
        
        function 
login_by_id$pass )
        {
            if(! 
$this->uid ) return( false );
            
$this->logoutfalse );
            
            
$this->_sql "SELECT user_name FROM users WHERE user_id = '$this->uid' LIMIT 1";

            
$res $this->_query( );
            if(! 
mysql_num_rows$res ) ) return( false );
            
$name mysql_fetch_assoc$res );
            
            
$this->$name['user_name'];
            
$this->$pass;
            
            return( 
$this->process$this->u$this->p) );
        }
        
        
        
/* PRIVATE METHODS */
        
        /* Reset a single var or all vars to their original attribute definitions */
        
function _reset$var '' )
        {
            
$class_vars get_class_varsget_class$this ) );
            
            if( 
$var )
            {
                
$this->$var $class_vars[$name];
                
            } else {
                foreach( 
$class_vars as $name=>$value )
                {
                    
$this->$name $value;
                }
            }
        }

        
/* Called by the check() public method, checks for the existance of a user in the DB
                with the given username and password
        */
        
        
function _check( )
        {
            
$this->_sql "SELECT user_id, user_confirmed FROM users WHERE users.user_name = '$this->u' 
                            AND users.user_pass = AES_ENCRYPT('$this->p', '" 
$this->$this->_crypt "' ) 
                            LIMIT 1"
;

            
$res $this->_query();
            
            if(! 
mysql_num_rows$res ) )
            {
                
$this->_errorLOGIN_FAIL );
                return( 
false );
            } else
            {
                
$row mysql_fetch_assoc$res );
                if(! 
$row['user_confirmed'] )
                {
                    
$this->_errorLOGIN_NOT_CONFIRMED );
                    return( 
false );
                } else
                {
                    
$this->uid $row['user_id'];
                    
$this->update_saved_products( );
                    return( 
true );
                }
            }
        }
        
        
/*  MySQL query wrapper */
        
function _query( )
        {
            if ( empty( 
$this->_sql ) )  die ( "The SQL query you passed was empty" );
            if(! ( 
$result mysql_query$this->_sql$this->_db ) ) )
                die( 
mysql_errno($this->_db ).' ) 'mysql_error$this->_db ).'<hr />query was: ' $this->_sql ' on DB '$this->_db );
                
            
$this->_reset'_sql' );
            return( 
$result );
        }
            
        
/* Big ol' error method, pushes errors into the errarr stack for display by the public error() method */
        
function _error$type )
        {
            switch( 
$type )
            {
                case 
LOGIN_FAIL:
                    
$this->errarr[] = 'This user was not found';
                break;
                
                case 
U_TOO_SHORT:
                    
$this->errarr[] = 'Your username must be at least 6 characters long';
                break;
                
                case 
U_TOO_LONG:
                    
$this->errarr[] = 'Your username must be less than 16 characters long';
                break;
                
                case 
P_NO_MATCH:
                    
$this->errarr[] = 'Your passwords do not match';
                break;
                
                case 
BAD_BIRTHDATE:
                    
$this->errarr[] = "Your birthday never happened...";
                break;
                
                case 
BAD_EMAIL:
                    
$this->errarr[] = "You must provide a proper email address";
                break;
                
                case 
P_TOO_SHORT:
                    
$this->errarr[] = "Your password must be at least 6 characters long";
                break;
                
                case 
NO_BIRTHDATE:
                    
$this->errarr[] = "Sorry!  You have to enter your birthdate";
                break;
                
                case 
LATE_BIRTHDATE:
                    
$this->errarr[] = "Not even Stephen Hawking could be born after today...";
                break;
                
                case 
U_ALREADY_EXISTS:
                    
$this->errarr[] = "There is already a user with this username.  Sorry!";
                break;
                
                case 
E_ALREADY_EXISTS:
                    
$this->errarr[] = "There is already someone with this email address.  Please try again.";
                break;
                
                case 
BAD_CAPTCHA:
                    
$this->errarr[] = "Whoops!  Looks like you didn't enter the image text correctly. Please try again.";
                break;
                
                case 
BAD_ACTIVATION:
                    
$this->errarr[] = "This activation e-mail is either badly formed or expired.  Please <a href=\"login?reactivate=1&u=$this->u\">click here</a> and we'll send you a new email";
                break;
                
                case 
LOGIN_NOT_CONFIRMED:
                    
$this->errarr[] = "You haven't confirmed your login yet.  Please check your email and follow the link inside.  If you never received this email, <a href=\"login?reactivate=1&u=$this->u\">click here</a> and we'll resend it to you";
                break;
            }
        }
        
        
        
/*  Generate a code to be used for activation.  Takes a timestamp from
                the calling method and truncates it to the username
        */
        
function _generate_code$time )
        {
            
$this->_code md5$this->$time );
            
$this->_aurl $this->webroot '?a=1&t=' $time '&i=' $this->uid;
        }
        
        function 
check_for_user$username )
        {
            
$username $this->clean$username );
            
$this->_sql "SELECT 1 FROM users WHERE user_name = '$username' LIMIT 1";
            
$res $this->_query( );
            if( 
mysql_num_rows$res ) ) return( true );
            return( 
false );
            
        }
        
        
        function 
_create( )
        {
            
$this->_sql "INSERT INTO users (user_name, user_pass, user_email, user_birthdate, user_newsletter, user_confirmed, user_dtecreated ) VALUES ( '$this->u', AES_ENCRYPT('$this->p', '" $this->$this->_crypt "'), '$this->email', '$this->bdate', $this->news, 0, now())";
            
$this->_query( );
            
$this->uid mysql_insert_id$this->_db );
            
            
// Update the activity table - they'll need to activate their account
            
$code_creation_time time( );
            
$this->_generate_code$code_creation_time );
            
$this->_sql "INSERT INTO activity ( user_id, activity_ts, activity_hash, activity_desc) VALUES
                                        ( $this->uid, $code_creation_time, '$this->_code', 'Activation')"
;
            
$this->_query( );
            
            
// And finally put a record in the profile table
            
$this->_sql "INSERT INTO profiles (user_id, profile_private, profile_products_page )
                                            VALUES ($this->uid, $this->mr, 10)"
;
            
$this->_query( );
            
                
            
$this->_mailIt'activate.tpl' );
            return( 
true );
        }
        
        
/*  Check to see if an email address or user_name already exists.  Note
            that this is being passed cleaned values by the create() method
         */
        
function _exists$type$value='' )
        {
            switch( 
$type )
            {
                case 
'user':
                    
$this->_sql "SELECT 1 FROM users WHERE user_name = '" $value "'";
                    
$res $this->_query( );
                    return( 
mysql_num_rows$res ) );
                break;
                
                case 
'email':
                    
$this->_sql "SELECT 1 FROM users WHERE user_email = '" $value "'";
                    
$res $this->_query( );
                    return( 
mysql_num_rows$res ) );
                break;
                
                default:
                    return( 
false );
                break;
            }
        }
        
        
        
        function 
_mailIt$tpl )
        {
            
$mail =& new PHPMailer();
            
$mail->IsMail();
            
$mail->From "security@somewhere.com";
            
$mail->FromName "Somewhere Security";
            
$mail->AddAddress$this->email$this->);
            
            include( 
$this->mail_tpl_path $tpl );
            
            
$mail->Subject $subject;
            
$mail->Body $body;
            
$mail->Send( );
        }
    }

/*
CREATE TABLE `users` (
  `user_id` int(11) NOT NULL auto_increment,
  `user_name` varchar(255) NOT NULL default '',
  `user_pass` blob NOT NULL,
  `user_email` varchar(255) NOT NULL default '',
  `user_birthdate` datetime NOT NULL default '0000-00-00 00:00:00',
  `user_newsletter` tinyint(4) NOT NULL default '1',
  `user_confirmed` tinyint(1) NOT NULL default '0',
  `user_dtecreated` datetime NOT NULL default '0000-00-00 00:00:00',
  `user_wize_points` int(11) NOT NULL default '0',
  `user_role` enum('user','merchant','admin','deity') NOT NULL default 'user',
  `user_views` int(11) NOT NULL default '0',
  PRIMARY KEY  (`user_id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;


CREATE TABLE `activity` (
  `activity_id` int(11) NOT NULL auto_increment,
  `user_id` int(11) NOT NULL default '0',
  `activity_ts` int(11) NOT NULL default '0',
  `activity_hash` varchar(255) default NULL,
  `activity_desc` enum('None','Activation','Password Reset') NOT NULL default 'None',
  `activity_expired` int(11) NOT NULL default '0',
  PRIMARY KEY  (`activity_id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;

CREATE TABLE `sessions` (
  `session_id` varchar(32) NOT NULL default '',
  `session_data` longtext NOT NULL,
  `session_time` int(11) NOT NULL default '0',
  `session_ip` varchar(32) NOT NULL default '0',
  `session_crypt` blob NOT NULL,
  PRIMARY KEY  (`session_id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;

*/
    
    
?>