diff options
author | 2009-06-25 15:22:42 -0400 | |
---|---|---|
committer | 2009-06-25 15:22:42 -0400 | |
commit | a171039a434de5bb44af48c776bc7625a09c7752 (patch) | |
tree | bf9b39c864ee5a01a9f5d5b12173899aeab05897 | |
parent | Cleaned up various unused bits of code; moved finished images to their own di... (diff) | |
download | ingenue-a171039a434de5bb44af48c776bc7625a09c7752.tar.gz ingenue-a171039a434de5bb44af48c776bc7625a09c7752.tar.bz2 ingenue-a171039a434de5bb44af48c776bc7625a09c7752.zip |
Added logout and user self-registration with email confirmation; Updates to sql_row_obj; PDO subclass for debugging
-rw-r--r-- | frontend/include/header.php | 12 | ||||
-rw-r--r-- | frontend/pages/login.php | 24 | ||||
-rw-r--r-- | frontend/pages/logout.php | 16 | ||||
-rw-r--r-- | frontend/pages/register.php | 66 | ||||
-rw-r--r-- | frontend/routing.csv | 4 | ||||
-rw-r--r-- | shared/classes/0sql_row_obj.php | 89 | ||||
-rw-r--r-- | shared/classes/build.php | 2 | ||||
-rw-r--r-- | shared/classes/buildopt.php | 2 | ||||
-rw-r--r-- | shared/classes/pdo.php | 8 | ||||
-rw-r--r-- | shared/classes/registrationtoken.php | 41 | ||||
-rw-r--r-- | shared/classes/session.php | 22 | ||||
-rw-r--r-- | shared/classes/task.php | 2 | ||||
-rw-r--r-- | shared/config.php | 3 | ||||
-rw-r--r-- | shared/functions/xhtmlemail.php (renamed from frontend/functions/xhtmlemail.php) | 5 | ||||
-rw-r--r-- | shared/include/dbinit.php | 2 |
15 files changed, 213 insertions, 85 deletions
diff --git a/frontend/include/header.php b/frontend/include/header.php index 578605a..0822f68 100644 --- a/frontend/include/header.php +++ b/frontend/include/header.php @@ -37,7 +37,17 @@ echo '<li><a href="'.url('logs').'">Log viewer</a></li>'; </div> <div id="top" class="box"> <?php - echo (isset($S['user'])?'<a href="'.url('logout').'">Logout</a>':'<a href="'.url('login').'">Login</a>'); + $links=array(); + if (isset($S['user'])) { + $links['logout'.(strlen($S['request'])?'/'.$S['request']:'')]='Logout'; + } else { + $links['login'.(strlen($S['request'])?'/'.$S['request']:'')]='Login'; + $links['register']='Register'; + } + foreach ($links as $url => $text) { + $links[$url]='<a href="'.url($url).'">'.htmlentities($text).'</a>'; + } + echo implode(' • ', $links); ?> </div> <div id="main" class="box"> diff --git a/frontend/pages/login.php b/frontend/pages/login.php index 881a821..13eeb0a 100644 --- a/frontend/pages/login.php +++ b/frontend/pages/login.php @@ -2,28 +2,16 @@ function init_login() { global $S, $request, $conf; if (isset($S['user'])) { + // Should we let you continue to $request['go'] instead? return 'welcome'; } else { if (isset($request['email']) && isset($request['password'])) { $r=$S['pdo']->query('SELECT * FROM `users` WHERE `email`='.$S['pdo']->quote($request['email']).' AND `passhash`="'.sha1($request['password']).'"'); if ($r->rowCount()) { $S['user']=new sql_user($r->fetch(PDO::FETCH_ASSOC)); - $id=null; - while (!$id) { - $id=randstring(30); - $r=$S['pdo']->query('SELECT * FROM `sessions` WHERE `id`="'.$id.'"'); - if ($r->rowCount()) { - $id=null; - } - } - $session=new sql_session($id, $S['user']->id, time(), $conf['sessionlength']); - debug('setcookie', $conf['cookiename'].'='.$id); - if (setcookie($conf['cookiename'], $session->id, time()+$conf['sessionlength'], $S['cookie_dir'], '', false, true)) { - $session->write(); - $S['login_result']=true; - } + $S['login.result']=sql_session::create(); } else { - $S['login_result']=false; + $S['login.result']=false; } } return array('title' => 'Login'); @@ -35,10 +23,10 @@ function body_login() { $request['go']=$S['request']; echo print_warning('Please sign in to access this page.'); } - if (isset($S['login_result'])) { - if ($S['login_result'] === 'error') { + if (isset($S['login.result'])) { + if ($S['login.result'] === 'error') { echo print_error('An error occurred while signing you in.'); - } elseif ($S['login_result']) { + } elseif ($S['login.result']) { echo print_success('Welcome, '.$S['user']->name); echo '<a href="'.url(isset($request['go'])?$request['go']:'').'">Continue</a>'; die; diff --git a/frontend/pages/logout.php b/frontend/pages/logout.php new file mode 100644 index 0000000..6eccd3d --- /dev/null +++ b/frontend/pages/logout.php @@ -0,0 +1,16 @@ +<?php +function init_logout() { + global $S, $conf, $request; + if (isset($S['session'])) { + $S['session']->delete(); + } + setcookie($conf['cookiename'], '', 1, $S['cookie_dir'], '', false, true); + if (isset($request['go'])) { + header('Location: '.url($request['go'])); + } +} +function body_logout() { + echo print_success('Logged out.'); + echo '<a href="'.url('login').'">Log back in</a>'; +} +?> diff --git a/frontend/pages/register.php b/frontend/pages/register.php new file mode 100644 index 0000000..344ee25 --- /dev/null +++ b/frontend/pages/register.php @@ -0,0 +1,66 @@ +<?php +function init_register() { + global $S; + if (isset($S['user'])) { + header('Location: '.url()); + return 'welcome'; + } + if (isset($request['token']) && preg_match('/^[a-zA-Z0-9]{30}$/', $request['token'])) { + $r=$S['pdo']->query('SELECT * FROM `tokens` WHERE `id`=\''.$request['token'].'\''); + if ($r->rowCount()) { + $S['register.token']=new sql_registrationtoken($r->fetch(PDO::FETCH_ASSOC)); + if (isset($request['password'])) { + $S['register.fail']=''; + if (!isset($request['name']) || !Validate::username($request['name'])) + $S['register.fail'].=print_warning('The username you entered is invalid. Names must be at least two characters long and may contain alphanumeric characters, period, space, underscore, and dash.'); + if (!isset($request['password']) || strlen($request['password']) <= 4) + $S['register.fail'].=print_warning('Please enter a password at least five characters long.'); + if ($S['register.fail']=='') { + $S['user']=new sql_user(null, $S['register.token']->email, $request['name'], sha1($request['password']), ''); + $S['user']->write(); + $S['register.token']->delete(); + unset($S['register.token']); + sql_session::create(); + } + } + } + } + return array('title' => 'Register'); +} +function body_register() { + global $S, $request, $conf; + if (isset($S['user'])) + echo print_success('Account creation complete.'); + elseif (isset($request['email'])) { + if (!Validate::email($request['email'])) + echo print_warning('The email address you entered is invalid.').'<a href="javascript:history.go(-1)">Back</a>'; + // 5.3.0 - goto print form + else { + if ($S['pdo']->query('SELECT COUNT(*) FROM `users` WHERE `email`='.$S['pdo']->quote($request['email']))->fetch(PDO::FETCH_COLUMN)) + echo print_warning('An account already exists with this email address.').'<a href="'.url('login').'">Login</a>'; + else { + if ($token=$S['pdo']->query('SELECT * FROM `registrationtokens` WHERE `email`='.$S['pdo']->quote($request['email']))->fetch(PDO::FETCH_ASSOC)) { + echo print_warning('A confirmation email has already been sent to this email address... sending another email.'); + $token=new sql_registrationtoken($token); + } else { + $token=sql_registrationtoken::create(); + $token->email=$request['email']; + } + $token->expire=time()+24*3600; // 24 Hours before expiration (not implemented) + $token->write(); + xhtmlemail($request['email'], null, $conf['title'].' account creation', 'To complete your account registration, click this link: <a href="'.url('register/'.$token->id).'">'.url('register/'.$token->id).'</a>.'); + echo print_success('You will receive an email soon at '.htmlentities($request['email']).' with instructions to finish creating your account.'); + } + } + } elseif (isset($S['register.token'])) { + if (isset($S['register.fail'])) + echo $S['register.fail']; + else + echo '<h3>Register</h3><form action="'.url('register').'" method="post"><input type="hidden" name="token" value="'.$request['token'].'" />Display name: <input name="name" /><br/>Password: <input type="password" name="password" /><br/><input type="submit" value="Create Account" /></form>'; + } else + echo '<h3>Register</h3><form action="'.url('register').'" method="post"> + E-mail: <input name="email" /><br/> + <input type="submit" value="Create Account" /> + </form>'; +} +?> diff --git a/frontend/routing.csv b/frontend/routing.csv index 841529d..4baf180 100644 --- a/frontend/routing.csv +++ b/frontend/routing.csv @@ -25,7 +25,11 @@ ^download/([a-zA-Z0-9]{6})$ downloadimage build # Session ^login$ login +^login/(.+)$ login go ^logout$ logout +^logout/(.+)$ logout go +# Account stuff +^register$ register # Pass through ^(js)/([0-9a-zA-Z-_]+\.(js))$ passthrough dir file ext ^(images)/([0-9a-zA-Z-_]+\.(gif|jpg|jpeg|ico))$ passthrough dir file ext diff --git a/shared/classes/0sql_row_obj.php b/shared/classes/0sql_row_obj.php index f2f815f..5c55a62 100644 --- a/shared/classes/0sql_row_obj.php +++ b/shared/classes/0sql_row_obj.php @@ -47,7 +47,7 @@ abstract class sql_row_obj { // If the name of this class changes, it must be up // Makes an SQL query using $sql and returns the resulting object private static function sql_query($q) { self::check_pdo_obj(); - // echo $q."\n"; + debug('sql_row_obj::query', $q); return self::$pdo->query($q); } public static function sql_quote_string($s) { @@ -535,60 +535,34 @@ class sql_col { public function __construct($array=null) { if (is_array($array)) { // TODO this should probably be a switch inside a foreach - if (isset($array['type'])) { + if (isset($array['type'])) $this->type=strtoupper($array['type']); // To allow for lower-case types - } - if (isset($array['length'])) { + if (isset($array['length'])) $this->length=$array['length']; - } elseif (isset($array['len'])) { - $this->length=$array['len']; - } - if (isset($array['unsigned'])) { + if (isset($array['unsigned'])) $this->unsigned=$array['unsigned']?true:false; // To allow for non-booleans that can evaluate to boolean - } elseif (isset($array['signed'])) { - $this->unsigned=$array['signed']?false:true; - } - if (isset($array['charset'])) { + if (isset($array['charset'])) $this->charset=$array['charset']; - } - if (isset($array['collate'])) { + if (isset($array['collate'])) $this->collate=$array['collate']; - } - if (isset($array['not null'])) { - $this->not_null=$array['not null']?true:false; - } elseif (isset($array['not_null'])) { + if (isset($array['not_null'])) $this->not_null=$array['not_null']?true:false; - } elseif (isset($array['null'])) { - $this->not_null=$array['null']?false:true; - } - if ($this->is_numeric()) { - if (isset($array['auto_increment'])) { - $this->auto_increment=$array['auto_increment']?true:false; - } elseif (isset($array['auto increment'])) { - $this->auto_increment=$array['auto increment']?true:false; - } elseif (isset($array['ai'])) { - $this->auto_increment=$array['ai']?true:false; - } - } - if (isset($array['default'])) { + if ($this->is_numeric() && isset($array['auto_increment'])) + $this->auto_increment=$array['auto_increment']?true:false; + if (isset($array['default'])) $this->default=$array['default']; - } elseif ($this->not_null) { // TODO add the default non-null val for the rest of types (http://dev.mysql.com/doc/refman/5.1/en/data-type-defaults.html) - if ($this->is_numeric() && !$this->auto_increment) { + elseif ($this->not_null) { // TODO add the default non-null val for the rest of types (http://dev.mysql.com/doc/refman/5.1/en/data-type-defaults.html) + if ($this->is_numeric() && !$this->auto_increment) $this->default=0; - } elseif ($this->type == 'ENUM') { + elseif ($this->type == 'ENUM') { // $this->default=$this->length; // TODO finish this - } elseif ($this->is_string()) { + } elseif ($this->is_string()) $this->default=''; - } } - if (isset($array['unique'])) { + if (isset($array['unique'])) $this->unique=$array['unique']?true:false; - } - if (isset($array['refers to'])) { - $this->refers_to=$array['refers to']; - } elseif (isset($array['refersto'])) { - $this->refers_to=$array['refersto']; - } + if (isset($array['refers_to'])) + $this->refers_to=$array['refers_to']; } elseif (is_string($array)) { $line=$array; list($type, $line)=explode(' ', $line, 2); @@ -654,7 +628,11 @@ class sql_col { $this->default=$string; break; case 'COMMENT': - $this->comment=$string; + if (preg_match('/^refers to: ([a-zA-Z0-9_$]+\.[a-zA-Z0-9_$]+)$/', $string, $match)) { + $this->refers_to=$match[1]; + } else { + $this->comment=$string; + } break; } break; @@ -752,30 +730,25 @@ class sql_col { // Returns the row used to create this column in the CREATE statement public function describe() { $d=$this->type.($this->has_length()?'('.$this->get_length().')':''); - if ($this->is_numeric() && $this->unsigned) { + if ($this->is_numeric() && $this->unsigned) $d.=' UNSIGNED'; - } if ($this->is_string()) { $d.=' CHARSET '.$this->charset; - if (isset($this->collate)) { + if (isset($this->collate)) $d.=' COLLATE '.$this->collate; - } } - if ($this->not_null) { + if ($this->not_null) $d.=' NOT NULL'; - } - if (isset($this->default)) { + if (isset($this->default)) $d.=' DEFAULT '.$this->sql_value($this->default); - } - if ($this->is_numeric() && $this->auto_increment) { + if ($this->is_numeric() && $this->auto_increment) $d.=' AUTO_INCREMENT'; - } - if ($this->unique === true) { + if ($this->unique === true) $d.=' UNIQUE'; - } - if (isset($this->comment)) { + if (isset($this->comment)) $d.=' COMMENT '.sql_row_obj::sql_quote_string($this->comment); - } + elseif (isset($this->refers_to)) + $d.=' COMMENT '.sql_row_obj::sql_quote_string('refers to: '.$this->refers_to); return $d; } // Returns the array necessary to generate this column using the constructor diff --git a/shared/classes/build.php b/shared/classes/build.php index 54631ee..369425e 100644 --- a/shared/classes/build.php +++ b/shared/classes/build.php @@ -13,7 +13,7 @@ class sql_build extends sql_row_obj { 'unsigned' => true, 'not_null' => true, 'default' => 0, - 'refers to' => 'users.id' + 'refers_to' => 'users.id' ), 'name' => array ( 'type' => 'VARCHAR', diff --git a/shared/classes/buildopt.php b/shared/classes/buildopt.php index 92d24fd..b4b5226 100644 --- a/shared/classes/buildopt.php +++ b/shared/classes/buildopt.php @@ -6,7 +6,7 @@ class sql_buildopt extends sql_row_obj { 'length' => 6, 'not_null' => true, 'default' => '', - 'refers to' => 'builds.id' + 'refers_to' => 'builds.id' ), 'name' => array ( 'type' => 'VARCHAR', diff --git a/shared/classes/pdo.php b/shared/classes/pdo.php new file mode 100644 index 0000000..daf71ba --- /dev/null +++ b/shared/classes/pdo.php @@ -0,0 +1,8 @@ +<?php +class pdo_debug extends PDO { + function query($q, $a1=null, $a2=null, $a3=null) { + debug('pdo::query', $q); + return parent::query($q, $a1, $a2, $a3); + } +} +?> diff --git a/shared/classes/registrationtoken.php b/shared/classes/registrationtoken.php new file mode 100644 index 0000000..86e8d44 --- /dev/null +++ b/shared/classes/registrationtoken.php @@ -0,0 +1,41 @@ +<?php +class sql_registrationtoken extends sql_row_obj { + protected $table='registrationtokens', $primary_key=array('id'), $columns=array( + 'id' => array ( + 'type' => 'CHAR', + 'length' => 30, + 'not_null' => true, + 'default' => '' + ), + 'owner' => array ( + 'type' => 'INT', + 'length' => 10, + 'unsigned' => true + ), + 'email' => array ( + 'type' => 'VARCHAR', + 'length' => 255, + 'not_null' => true, + 'unique' => true + ), + 'expire' => array ( + 'type' => 'INT', + 'length' => 10, + 'unsigned' => true, + 'not_null' => true, + 'default' => 0 + ) + + ); + static function create() { + global $S; + $id=null; + do { + $id=randstring(30); + if ($S['pdo']->query('SELECT COUNT(*) FROM `registrationtokens` WHERE `id`=\''.$id.'\'')->fetch(PDO::FETCH_COLUMN)) + $id=null; + } while ($id==null); + return new sql_registrationtoken($id, null, null, null); + } +} +?> diff --git a/shared/classes/session.php b/shared/classes/session.php index 19575fe..e422b21 100644 --- a/shared/classes/session.php +++ b/shared/classes/session.php @@ -13,7 +13,7 @@ class sql_session extends sql_row_obj { 'unsigned' => true, 'not_null' => true, 'default' => 0, - 'refers to' => 'users.id' + 'refers_to' => 'users.id' ), 'atime' => array ( 'type' => 'INT', @@ -31,5 +31,25 @@ class sql_session extends sql_row_obj { ) ); + // Creates a new session for the user at $S['user'] with a unique id, sends a cookie to the user and returns true for success, false for failure + static function create() { + global $S, $conf; + $id=null; + while (!$id) { + $id=randstring(30); + $r=$S['pdo']->query('SELECT * FROM `sessions` WHERE `id`="'.$id.'"'); + if ($r->rowCount()) { + $id=null; + } + } + $S['session']=new sql_session($id, $S['user']->id, time(), $conf['sessionlength']); + debug('setcookie', $conf['cookiename'].'='.$id); + if (setcookie($conf['cookiename'], $S['session']->id, time()+$conf['sessionlength'], $S['cookie_dir'], '', false, true)) { + $S['session']->write(); + return true; + } else { + return false; + } + } } ?> diff --git a/shared/classes/task.php b/shared/classes/task.php index 30adb73..73bde43 100644 --- a/shared/classes/task.php +++ b/shared/classes/task.php @@ -13,7 +13,7 @@ class sql_task extends sql_row_obj { 'length' => 6, 'not_null' => true, 'default' => '', - 'refers to' => 'builds.id' + 'refers_to' => 'builds.id' ), 'command' => array ( 'type' => 'TEXT', diff --git a/shared/config.php b/shared/config.php index a8719ed..fb61824 100644 --- a/shared/config.php +++ b/shared/config.php @@ -10,9 +10,10 @@ $conf['cookiename']='ingenueid'; // Name of the cookie to send for keeping sessi $conf['sessionlength']=1814400; // Time in seconds before sessions are purged $conf['timezone']=10800; // Time difference in seconds between UTC and the default timezone $conf['mod_rewrite']=true; // Use mod_rewrite for pretty URLs +$conf['emailfrom']='noreply@gentoo.org'; // Used as the From: field in emails $conf['check_email_dns']=true; // Use DNS to check the domain of submitted emails for validity $conf['pkgdir_root']='/home/eitan/soc/tinderbox'; // The directory to recursively search for pkgdirs in $conf['emerge_default_opts']='-t -v -K --color=y --root-deps=rdeps'; // DON'T CHANGE UNLESS YOU KNOW WHAT YOU'RE DOING $conf['portdir']='/usr/portage'; // The directory conatining the portage tree to use (/usr/portage unless you have a reason to think otherwise) -$conf['logview_max']=1000; +$conf['logview_max']=1000; // The maximum number of log entries shown on one page (1000 is a good start) ?> diff --git a/frontend/functions/xhtmlemail.php b/shared/functions/xhtmlemail.php index 753a6dc..f07c99a 100644 --- a/frontend/functions/xhtmlemail.php +++ b/shared/functions/xhtmlemail.php @@ -8,11 +8,12 @@ function xhtmlemail($to,$from,$subj,$cont,$inheads=null) { $heads='MIME-Version: 1.0' . "\r\n"; $heads.='Content-type: text/html; charset=utf-8' . "\r\n"; $heads.='From: '.$from."\r\n"; - $heads.='X-Mailer: MosBlog/'.MOSBLOG_VERSION."\r\n"; + $heads.='X-Mailer: PHP/'.$conf['title']."\r\n"; if ($inheads!==null) { - $heads.="\r\n".$inheads; + $heads.=$inheads."\r\n"; } $cont='<?xml version="1.0" encoding="utf-8"?>'."\n".'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">'."\n".'<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">'.$cont.'</html>'."\n"; + $heads.='Content-length: '.strlen($cont)."\r\n"; return mail($to,$subj,$cont,$heads); } ?> diff --git a/shared/include/dbinit.php b/shared/include/dbinit.php index 852b35f..49b5864 100644 --- a/shared/include/dbinit.php +++ b/shared/include/dbinit.php @@ -1,5 +1,5 @@ <?php require_once(dirname(__FILE__).'/../config.php'); // Use __DIR__ in 5.3.0 -$S['pdo']=new PDO('mysql:dbname='.$conf['sqldb'].';host='.$conf['sqlhost'], $conf['sqluser'], $conf['sqlpass']); +$S['pdo']=new pdo_debug('mysql:dbname='.$conf['sqldb'].';host='.$conf['sqlhost'], $conf['sqluser'], $conf['sqlpass']); sql_row_obj::set_pdo_obj($S['pdo']); ?> |