From 262cb8d5bb1398e03a9511ab6f9c8cb649d1384a Mon Sep 17 00:00:00 2001
From: Tim Clark <eclipse@sucs,org>
Date: Tue, 1 Sep 2009 19:42:34 +0000
Subject: [PATCH] UI and validation for the new signup system, actual backend
 functionality is started but mostly missing

---
 components/signup.php     | 124 ++++++++++++++++++++++++++++--
 components/signupajax.php |  80 +++++++++++++++++++
 lib/member_functions.php  |   2 +
 lib/sanitization.php      |   8 ++
 lib/validation.php        | 158 ++++++++++++++++++++++++++++++++++++++
 lib/validationData.php    |  35 +++++++++
 templates/signup.tpl      |  91 ++++++++++++++++++++++
 7 files changed, 493 insertions(+), 5 deletions(-)
 create mode 100644 components/signupajax.php
 create mode 100644 lib/member_functions.php
 create mode 100644 lib/sanitization.php
 create mode 100644 lib/validationData.php
 create mode 100644 templates/signup.tpl

diff --git a/components/signup.php b/components/signup.php
index 56b1571..c53f362 100644
--- a/components/signup.php
+++ b/components/signup.php
@@ -1,10 +1,124 @@
 <?php
-/* Temporary Component to get the signup stuff working with new SUCS site */
+//ob_start();
+//include("../member/signup.php");
+//$output = ob_get_contents();
+//ob_end_clean();
 
-ob_start();
-include("../member/signup.php");
-$output = ob_get_contents();
-ob_end_clean();
+//set defaults
+$mode = 'login';
+
+//login
+if(isset($_REQUEST['signupid'])&&isset($_REQUEST['signuppw'])){
+    //set signup details
+    $signupid = $_REQUEST['signupid'];
+    $signuppw = $_REQUEST['signuppw'];
+    // connect to sucs database
+    $sucsDB = NewADOConnection('postgres8');
+
+    // -------------------------------------------------
+    // TODO: CHANGE THIS TO dbname=sucs BEFORE DEPLOYING
+    // -------------------------------------------------
+    $sucsDB->Connect('dbname=eclipse');
+
+
+    $sucsDB->SetFetchMode(ADODB_FETCH_ASSOC);
+    // get row(s)
+    $query = "SELECT * FROM signup WHERE id=? AND password=?";
+    $array = array($signupid,$signuppw);
+    $data = $sucsDB->GetAll($query,$array);
+    // if data was returned and it was exactly 1 row
+    if(is_array($data)&&sizeof($data)==1){
+	$row=$data[0];
+	// if the id hasnt already been used
+	if(!(isset($row[activated])&&isset($row[username]))){
+	    // pass on the id and passwd
+	    $smarty->assign("signupid",$signupid);
+	    $smarty->assign("signuppw",$signuppw);
+	    $smarty->assign("usertype",$row[type]);
+	    // if accepting the form
+	    if(isset($_REQUEST['username']) && isset($_REQUEST['realname']) && isset($_REQUEST['email']) && isset($_REQUEST['phone'])){
+		require_once("../lib/validation.php");
+		$valid=true;
+		$errors=array();
+		$fields=array();
+		if(!validUsername($_REQUEST['username'])){
+		    $valid=false;
+		    $errors['username']=$error;
+		}
+		$fields['username']=$_REQUEST['username'];
+		if(!validSignupEmail($_REQUEST['email'])){
+		    $valid=false;
+		    $errors['email']=$error;
+		}
+		$fields['email']=$_REQUEST['email'];
+		if(!validPhone($_REQUEST['phone'])){
+		    $valid=false;
+		    $errors['phone']=$error;
+		}
+		$fields['phone']=$_REQUEST['phone'];
+		if($row[type]!=2){
+		    if(!validAddress($_REQUEST['address'])){
+			$valid=false;
+			$errors['address']=$error;
+		    }
+		    $fields['address']=$_POST['address'];
+		    if(!validRealName($_REQUEST['realname'])){
+			$valid=false;
+			$errors['realname']=$error;
+		    }
+		    $fields['realname']=$_REQUEST['realname'];
+		}
+		else{
+		    if(!validRealName($_REQUEST['contact'])){
+			$valid=false;
+			$errors['contact']=$error;
+		    }
+		    $fields['contact']=$_REQUEST['contact'];
+		    if(!validSocName($_REQUEST['realname'])){
+			$valid=false;
+			$errors['realname']=$error;
+		    }
+		    $fields['realname']=$_REQUEST['realname'];
+		}
+		if($row[type]==1){
+			if(!validSID($_REQUEST['studentid'])){
+			    $valid=false;
+			    $errors['studentid']=$error;
+			}
+			$fields['studentid']=$_REQUEST['studentid'];
+		}
+
+		if($valid){
+		    $mode='result';
+		    //TODO: add membership add code here
+		    $smarty->assign("post",$_POST);
+		}
+		else{
+		    //re-show form
+		    $script = "<script language='javascript' type='text/javascript' src='".$baseurl."/js/jquery.js'></script>\n";
+		    $script .= "<script language='javascript' type='text/javascript' src='$baseurl/js/signup.js'></script>\n";
+		    $smarty->assign("fields",$fields);
+		    $smarty->assign("errors",$errors);
+        	    $smarty->append('extra_scripts', $script);
+                    $mode='re-form';	
+		}
+	    }
+	    else{
+		// display the form
+                $script = "<script language='javascript' type='text/javascript' src='".$baseurl."/js/jquery.js'></script>\n";
+		$script .= "<script language='javascript' type='text/javascript' src='$baseurl/js/signup.js'></script>\n";
+		$smarty->append('extra_scripts', $script);
+		$mode='form';
+	    }
+	}
+	else trigger_error("Signup ID already used",E_USER_WARNING);
+    }
+    else trigger_error("Invalid ID or Password", E_USER_WARNING);
+
+}
+//Set smarty Variables
+$smarty->assign("mode", $mode);
+$output =  $smarty->fetch("signup.tpl");
 
 $smarty->assign("title", "Sign Up");
 $smarty->assign("body", $output);
diff --git a/components/signupajax.php b/components/signupajax.php
new file mode 100644
index 0000000..dd45591
--- /dev/null
+++ b/components/signupajax.php
@@ -0,0 +1,80 @@
+<?
+require_once("../lib/validation.php");
+// don't output the site template
+$no_template = TRUE;
+header("Content-type: text/plain");
+if (isset($_GET['key'])){
+	switch($_GET['key']){
+	case "sid":
+		$sid=$_GET['value'];
+		if(validSID($sid)){
+			echo "OK:".lookupSID($_GET['value']);
+		}
+		else{
+			echo "ERROR:".$error;
+		}
+		break;
+	case "postcode":
+		$postcode=implode("", explode(" ", $_GET['value']));
+		echo json_encode(lookup_postcode($postcode));
+		break;
+	case "username":
+		$username = $_GET['value'];
+		if(validUsername($username)){
+			echo "OK";
+		}
+		else{
+			echo $error;
+		}
+		break;
+	case "realname":
+		$realname = $_GET['value'];
+		if(validRealName($realname)){
+			echo "OK";
+		}
+		else{
+			echo $error;
+		}
+		break;
+	case "socname":
+		$socname = $_GET['value'];
+		if(validSocName($socname)){
+			echo "OK";
+		}
+		else{
+			echo $error;
+		}
+		break;
+
+
+        case "address":
+                $address = $_GET['value'];
+                if(validAddress($address)){
+                        echo "OK";
+                }
+                else{   
+                        echo $error;
+                }
+                break;
+
+	case "email":
+		$email = $_GET['value'];
+		if(validSignupEmail($email)){
+			echo "OK";
+		}
+		else{
+			echo $error;
+		}
+		break;
+	case "phone":
+		$phone = $_GET['value'];
+		if(validPhone($phone)){
+			echo "OK";
+		}
+		else{
+			echo $error;
+		}
+		break;
+	}
+}
+?>
diff --git a/lib/member_functions.php b/lib/member_functions.php
new file mode 100644
index 0000000..471f06d
--- /dev/null
+++ b/lib/member_functions.php
@@ -0,0 +1,2 @@
+<?
+?>
diff --git a/lib/sanitization.php b/lib/sanitization.php
new file mode 100644
index 0000000..04c444c
--- /dev/null
+++ b/lib/sanitization.php
@@ -0,0 +1,8 @@
+<?
+function sanitizePhone($phone){
+	return ereg_replace("[ ()]", "", $phone);
+}
+function sanitizeAddress($address){
+	return str_replace(array("\r\n","\r"),array("\n","\n"),$address);
+}
+?>
diff --git a/lib/validation.php b/lib/validation.php
index 43136e9..37ded27 100644
--- a/lib/validation.php
+++ b/lib/validation.php
@@ -1,4 +1,6 @@
 <?
+require_once("validationData.php");
+require_once("sanitization.php");
 /* useful validation functions */
 
 //check for a valid email address
@@ -94,5 +96,161 @@ function weakPassword($password)
 	if ($answer == "") return("Empty password");
 	return $answer;
 }
+// check if username is an alias
+function isAlias($username){
+        $ok=false;
+        // check its not an alias
+        $aliasesfile = file ('/etc/aliases');
+        foreach ($aliasesfile as $aliasline)
+        {
+                if(trim($aliasline) && $aliasline[0]!="#")
+                {
+                        $anAlias = explode(":", trim($aliasline));
+                        if($anAlias[0] && !posix_getpwnam($anAlias[0]) && ($anAlias[0] == $username)){
+                                $ok=true;
+                                return true;
+                        }
+                }
+        }
+	return $ok;
+}
+//check if a user with a sid already exsists
+function sidUsed($sid){
+	$sucsDB = NewADOConnection('postgres8');
+	$sucsDB->Connect('dbname=sucs');
+	$sucsDB->SetFetchMode(ADODB_FETCH_ASSOC);
+	$query = "SELECT * FROM members WHERE sid=?";
+	$data = $sucsDB->GetAll($query,$sid);
+	return (sizeof($data) > 0);
+}
+
+
+function validUsername ($username){
+	global $error;
+	// check if uname is sytactically valid
+	$syntax = ereg("^[a-z][a-z0-9_]*$", $username);
+	if(!$syntax || (strlen($username) < 2)){
+		$error = "Usernames must start with a letter, only contain lowercase letter, numbers 0-9 and underscores (_) and be at least 2 characters.";
+		return false;
+	}
+	// check if the username already exsists
+	elseif(posix_getpwnam($username))
+        {
+		$error = "Username already taken";
+                return false; 
+	}
+	// check if its a mail alias
+	elseif(isAlias($username)){
+        	$error ="Username is a mail alias";
+        	return false;
+	}	
+	else{
+		return true;
+	}
+}
+function validSID($SID){
+	global $error;
+	if(!eregi("^[0-9]*$", $SID) || strlen($SID) != 6){
+		$error = "Invalid student ID";
+		return false;
+	}
+	elseif(sidUsed($SID)){
+		$error = "A user with that student ID already exsists, email <a href=\"mailto:admin@sucs.org\">admin@sucs.org</a> if this is an error.";
+		return false;
+	}
+	elseif(lookupSID($SID)==" "){
+		$error = "Student not found, email<a href=\"mailto:admin@sucs.org\">admin@sucs.org</a> if this is an error.";
+		return false;
+	}
+	else{
+		return true;
+	}
+}
+function validRealName($realName){
+	global $error;
+        //check for enough names for real name (we insist on at least 2
+        if(count(explode(" ",$realName)) < 2)
+        {
+		$error = "Too few names given, please give at least two.";
+		return false;
+        }
+        //check for a sane realname, see comment below
+	elseif (!ereg("^([A-Z]([.]+ +[A-Z])*([\']+[A-Z])*[a-z]+[ -]*)+$", $realName))
+        {
+		$error = "Name incorrectly formated, email <a href=\"mailto:admin@sucs.org\">admin@sucs.org</a> if this is an error.";
+		return false;
+        }
+/* 
+ * This should force sane real names, with capitals for the first letter of each word,
+ * Whist alowing for complex names such as Robin M. O'Leary
+ * 
+ * break down of regexp
+ * 
+ * (
+ * [A-Z]                - start with a single capital
+ * ([.]+ +[A-Z])*       - zero or more of, (at least one "." followed by at least one space then another single capital)  //we dont expect people to have initals at the end of there names so this is alright
+ * ([\']+[A-Z])*        - zero or more of, (at least one "'"s followed by a single capital letter)
+ * [a-z]+               - One or more lower case letters, this forces initals to be followed by a "."
+ *[ -]*           - zero or more " "s or "-"s so double barreled names are supported
+ * )
+ * 
+ * In its current state 
+ * Robin M. O'Leary is valid
+ * Robin M O'Leary is not
+ * Robin M. OLeary is Not
+ * Robin M. O'LeaRy is valid (though its not ment to be.. bad side effect of not requireing at least one space...)
+ * BUT... this alows for McSmith's... which is rather nice :)... and of course delibrate
+ * RObin M O'Leary is not
+ *
+ */	
+	else{
+		return true;
+	}
+}
+function validSocName($socname){
+	global $error;
+	if(!ereg('^[A-Z1-9]',$socname) || strlen($socname) < 2){
+		$error = "Must start with a capital letter or a number and be more than 1 character";
+		return false;
+	}
+	else{
+		return true;
+	}
+}
+
+function validAddress($address){
+	global $error;
+	$address = sanitizeAddress($address);
+        if(!ereg("^([A-Z0-9]([[:alnum:]]|[ .'])*\n)+[A-Z0-9]([[:alnum:]]|[ .'])*$",$address)){
+                $error = "Please supply at least two valid lines of address.";
+                return false;
+        }   
+        else{   
+                return true;
+        }
+}
 
+function validPhone($phone){
+	global $error;
+	$phone=sanitizePhone($phone);
+	if(!ereg("^\+?[0-9-]+$",$phone)){
+		$error = "Must be all numbers";
+		return false;
+	}
+	return true;
+}
+function validSignupEmail($email){
+	global $error;
+	if(ereg('@sucs\.org$',$email)){
+		$error = "SUCS email addresses are not allowed";
+		return false;
+	}
+	elseif(!validEmail($email)){
+		return false;
+	}
+	else{
+		return true;
+	}
+		
+}
 ?>
diff --git a/lib/validationData.php b/lib/validationData.php
new file mode 100644
index 0000000..6221e08
--- /dev/null
+++ b/lib/validationData.php
@@ -0,0 +1,35 @@
+<?
+// lookup real names from sid's useing campus ldap
+function lookupSID($sid) {
+        $ds=ldap_connect("nds-stud.swan.ac.uk");
+        $sr=ldap_search($ds, "ou=Students,o=uws", "cn=".$sid);
+        $info = ldap_get_entries($ds, $sr);
+        ldap_unbind($ds);
+        return ucwords(strtolower($info[0]['givenname'][0]." ".$info[0]['sn'][0]));
+}
+// lookup addresses from postcodes useing the univeritys website
+function lookup_postcode($postcode = "")
+{
+        $url = "https://intranet.swan.ac.uk/common/postcodeLookup.asp?pCode=".$postcode;
+        $referer = "https://intranet.swan.ac.uk/common/postcodeaddresslookup.asp";
+
+        $req = curl_init($url);
+        curl_setopt($req, CURLOPT_HEADER, false);
+        curl_setopt($req, CURLOPT_REFERER, $referer);
+        curl_setopt($req, CURLOPT_RETURNTRANSFER, true);
+        curl_setopt($req, CURLOPT_SSL_VERIFYPEER, false);
+        $page = curl_exec($req);
+        curl_close($req);
+
+        $scrape = explode("returnAddress(\"", $page);
+
+        $addresses = array();
+        for ($i = 1; $i < count($scrape); $i++) {
+                if (preg_match("/^[^,\"].+?\"/", $scrape[$i], $address)) {
+                        $addr = str_replace("<BR>\"", "", $address[0]);
+                        array_push($addresses, str_replace("<BR>", ", ", $addr));
+                }
+        }
+        return $addresses;
+}
+?>
diff --git a/templates/signup.tpl b/templates/signup.tpl
new file mode 100644
index 0000000..57ff3e4
--- /dev/null
+++ b/templates/signup.tpl
@@ -0,0 +1,91 @@
+{if $mode=='login'}
+<form action="{$componentpath}" method="post">
+    <div class="box" style="width: 70%; margin: auto;">
+        <div class="boxhead"><h2>Membership Signup</h2></div>
+        <div class="boxcontent">
+
+            <p>Please enter the details from your signup receipt</p>
+
+            <div class="row">
+                <label for="signupid">Signup ID:</label>
+                <span class="textinput"><input type="text" size="20" name="signupid" id="signupid" /></span>
+            </div>
+            <div class="row">
+                <label for="signuppw">Password:</label>
+                <span class="textinput"><input type="text" size="20" name="signuppw" id="signuppw" /></span>
+            </div>
+            <div class="row"><span class="textinput">
+                <input type="submit" name="submit" value="Sign Up" /></span>
+            </div>
+            <div class="clear"></div>
+            <div class="note">If you wish to renew an existing account instead, please login to <a href="https://sucs.org/Options">Membership Options</a> using your existing account details.</div>
+        </div>
+        <div class="hollowfoot"><div><div></div></div></div>
+    </div>
+</form>
+{elseif $mode=='form' || $mode=="re-form"}
+                <h1>Signup</h1>
+		<form action="{$componentpath}" method="post">
+				{if $usertype==1}
+                                <div class="row" id="studentiddiv">
+                                        <label for="studentid">Student Number</label>
+                                        <span class="textinput"><input type="text" id="studentid" name="studentid" size="30" {if $mode=='re-form'}value='{$fields.studentid}'{/if} /></span>
+					<div id="studentidmessage"{if $mode=='re-form'}{if isset($errors.studentid)} style="color:red">{$errors.studentid}{else} style="color:green">OK{/if}{else}>{/if}</div>
+                                </div>
+				{/if}
+                                <div class="row" id="usernamediv">
+                                        <label for="username">Username</label>
+                                        <span class="textinput"><input type="text" id="username" name="username" size="30" {if $mode=='re-form'}value='{$fields.username}'{/if}/></span>
+					<div id="usernamemessage"{if $mode=='re-form'}{if isset($errors.username)} style="color:red">{$errors.username}{else} style="color:green">OK{/if}{else}>{/if}</div>
+                                </div>
+                                <div class="row" id="realnamediv">
+                                        <label for="realname">{if $usertype!=2}Real Name{else}Society Name{/if}</label>
+                                        <span class="textinput"><input type="text" id="realname" name="realname" size="30" {if $mode=='re-form'}value='{$fields.realname}'{/if}/></span>
+					<div id="realnamemessage"{if $mode=='re-form'}{if isset($errors.realname)} style="color:red">{$errors.realname}{else} style="color:green">OK{/if}{else}>{/if}</div>
+                                </div>
+				{if $usertype!=2}
+                                <div class="row" id="postcodediv" style="display:none">
+                                        <label for="postcode">Post Code</label>
+                                        <span class="textinput"><input type="text" id="postcode" size="10" value=""/></span>
+					<div id="postcodemessage"></div>
+                                </div>
+                                <div class="row" id="addseldiv" style="display:none">
+					<label for="addsel">Address Selector</label>
+					<span class="textinput"><select id="addsel" value=""></select></span>
+                                </div>
+                                <div class="row" id="addressdiv">
+                                        <label for="address">{if $usertype==1}Term Time {/if}Address</label>
+                                        <span class="textinput"><textarea id="address" name="address" cols="35" rows="4">{if $mode=='re-form'}{$fields.address}{/if}</textarea></span>
+					<div id="addressmessage"{if $mode=='re-form'}{if isset($errors.address)} style="color:red">{$errors.address}{else} style="color:green">OK{/if}{else}>{/if}</div>
+                                </div>
+				{else}
+				<div class="row" id="contactdiv">
+					<label for="contact">Contact Name</label>
+					<span class="textinput"><input type="text" id="contact" name="contact" size="30" {if $mode=='re-form'}value='{$fields.contact}'{/if}/></span>
+					<div id="contactmessage"{if $mode=='re-form'}{if isset($errors.contact)} style="color:red">{$errors.contact}{else} style="color:green">OK{/if}{else}>{/if}</div>
+				</div>
+				{/if}
+                                <div class="row" id="emaildiv">
+                                        <label for="email">Email Address</label>
+                                        <span class="textinput"><input type="text" id="email" name="email" size="30" {if $mode=='re-form'}value='{$fields.email}'{/if}/></span>
+					<div id="emailmessage"{if $mode=='re-form'}{if isset($errors.email)} style="color:red">{$errors.email}{else} style="color:green">OK{/if}{else}>{/if}</div>
+                                </div>
+                                <div class="row" id="phonediv">
+                                        <label for="phone">Phone Number</label>
+                                        <span class="textinput"><input type="text" id="phone" name="phone" size="30" {if $mode=='re-form'}value='{$fields.phone}'{/if}/></span>
+					<div id="phonemessage"{if $mode=='re-form'}{if isset($errors.phone)} style="color:red">{$errors.phone}{else} style="color:green">OK{/if}{else}>{/if}</div>
+                                </div>
+			<input type="hidden" id="signupid" name="signupid" value="{$signupid}" />
+			<input type="hidden" id="signuppw" name="signuppw" value="{$signuppw}" />
+                        <div class="row" id="submitdiv"> 
+        			<input type="submit" id="submit" value="Submit" />
+			</div>
+		</form>
+{elseif $mode=='result'}
+<table>
+<tr><th>Key</th><th>Value</th></tr>
+{foreach from=$post key=key item=value}
+<tr><td>{$key}</td><td>{$value}</td></tr>
+{/foreach}
+</table>
+{/if}
-- 
GitLab