Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • osian/sucs-site
  • kais58/sucs-site
  • imranh/sucs-site
  • foshjedi2004/sucs-site
  • gigosaurus/sucs-site
  • matstn/sucs-site
  • ripp_/sucs-site
  • eggnog/sucs-site
  • sucssite/sucs-site
  • elbows/sucs-site
  • realitykiller/sucs-site
  • crox/sucs-site
  • vectre/sucs-site
  • welshbyte/sucs-site
  • paperclipman/sucs-site
15 results
Show changes
Showing
with 1108 additions and 461 deletions
<?php
/* mechanism for members to give us feedback about web pages */
// where do website feedback mails go?
$contact = "devel@lists.sucs.org";
if ($session->loggedin) {
if ($_REQUEST["action"] == "feedback" && $_REQUEST["feedback"] != "") {
$feedback = $_REQUEST["feedback"];
$msgbody = "{$session->username} had this to say about the page {$_SERVER['REQUEST_URI']}:\r\n\r\n";
$msgbody .= "\"{$feedback}\"\r\n";
mail($contact, "SUCS Website Feedback", $msgbody);
$smarty->assign("feedbacked", TRUE);
}
$smarty->fetch("feedback.tpl");
}
?>
<?php
function piechart($title, $slice, $itemName, $fsizes = 0)
{
function matchset($xx)
{
$arrx = array_values($xx);
$i = 0;
while (list ($key, $val) = each($arrx)) {
$xy[$i] = $val;
$i++;
}
$cnt = $i;
return $xy;
}
$sliced = matchset($slice);
$countqw = count($sliced);
$ItemNames = matchset($itemName);
// initialize some variables
$sum = 0;
$degrees = Array();
$diameter = 250;
$radius = $diameter / 2;
// calculate sum of slices
for ($x = 0; $x < $countqw; $x++) {
$sum += $sliced[$x];
}
// convert each slice into corresponding percentage of 360-degree circle
$degCount = 0;
for ($y = 0; $y < $countqw; $y++) {
if ((($sliced[$y] / $sum) * 360) > '0') {
$degrees[$degCount] = ($sliced[$y] / $sum) * 360;
$degCount++;
}
}
// set up image and colours
Header("Content-Type: image/png");
$im = ImageCreate(550, 300);
$black = ImageColorAllocateAlpha($im, 0, 0, 0, 0);
$white = ImageColorAllocateAlpha($im, 255, 255, 255, 127);
$hexCode = array("255,153,0", "0,204,153", "204,255,102", "255,102,102", "102,204,255", "204,153,255", "255,0,0", "51,0,255", "255,51,153", "204,0,255", "255,255,51", "51,255,51", "255,102,0");
// fill image with white
ImageFill($im, 0, 0, $white);
// draw baseline
ImageLine($im, 150, 150, 225, 150, $black);
for ($z = 0; $z < $countqw; $z++) {
// calculate and draw arc corresponding to each slice
ImageArc($im, 150, 150, $diameter, $diameter, $last_angle,
($last_angle + $degrees[$z]), $black);
$last_angle = $last_angle + $degrees[$z];
// calculate coordinate of end-point of each arc by obtaining
// length of segment and adding radius
// remember that cos() and sin() return value in radians
// and have to be converted back to degrees!
$end_x = round(150 + ($radius * cos($last_angle * pi() / 180)));
$end_y = round(150 + ($radius * sin($last_angle * pi() / 180)));
// demarcate slice with another line
ImageLine($im, 150, 150, $end_x, $end_y, $black);
}
// this section is meant to calculate the mid-point of each slice
// so that it can be filled with colour
// initialize some variables
$prev_angle = 0;
$pointer = 0;
for ($z = 0; $z < $countqw; $z++) {
// to calculate mid-point of a slice, the procedure is to use an angle
//bisector
// and then obtain the mid-point of that bisector
$pointer = $prev_angle + $degrees[$z];
$this_angle = ($prev_angle + $pointer) / 2;
$prev_angle = $pointer;
// get end-point of angle bisector
$end_x = round(150 + ($radius * cos($this_angle * pi() / 180)));
$end_y = round(150 + ($radius * sin($this_angle * pi() / 180)));
// given start point (150,150) and end-point above, mid-point can be
// calculated with standard mid-point formula
$mid_x = round((150 + ($end_x)) / 2);
$mid_y = round((150 + ($end_y)) / 2);
// depending on which slice, fill with appropriate colour
$hexCodeSplit = explode(',', $hexCode[$z]);
$WedgeColor = ImageColorAllocate($im, $hexCodeSplit[0], $hexCodeSplit[1], $hexCodeSplit[2]);
ImageFillToBorder($im, $mid_x, $mid_y, $black, $WedgeColor);
}
// write string
ImageString($im, 5, 250, 10, "$title", $black);
$red = ImageColorAllocate($im, 255, 153, 153);
$blue = ImageColorAllocate($im, 0, 0, 255);
// Create Color key and slice description
$adjPosition = 40;
for ($z = 0; $z < $degCount; $z++) {
$percent = ($degrees[$z] / 360) * 100;
$percent = round($percent, 2);
$adjPosition = $adjPosition + 15;
$hexCodeSplit = explode(',', $hexCode[$z]);
$percentLen = strlen($percent);
if ($percentLen == '4') {
$percent = " " . "$percent";
}
if ($percentLen == '3') {
$percent = " " . "$percent";
}
if ($percentLen == '2') {
$percent = " " . "$percent";
}
if ($percentLen == '1') {
$percent = " " . "$percent";
}
ImageString($im, 2, 300, ($adjPosition + 1), "$percent%", $black);
$WedgeColor = ImageColorAllocate($im, $hexCodeSplit[0], $hexCodeSplit[1], $hexCodeSplit[2]);
ImageFilledRectangle($im, 340, $adjPosition, 350, ($adjPosition + 10), $black);
ImageFilledRectangle($im, 341, ($adjPosition + 1), 349, ($adjPosition + 9), $WedgeColor);
if ($fsizes) {
if ($sliced[$z] >= "1000" && $sliced[$z] < "1000000") {
$sliced[$z] = $sliced[$z] / 1000;
$sliced[$z] = sprintf("%01.2f", "$sliced[$z]") . "G";
} else
$sliced[$z] = "$sliced[$z]" . "M";
}
$sliceLen = strlen($sliced[$z]);
if ($sliceLen == '5') {
$sliced[$z] = " " . "$sliced[$z]";
}
if ($sliceLen == '4') {
$sliced[$z] = " " . "$sliced[$z]";
}
if ($sliceLen == '3') {
$sliced[$z] = " " . "$sliced[$z]";
}
if ($sliceLen == '2') {
$sliced[$z] = " " . "$sliced[$z]";
}
if ($sliceLen == '1') {
$sliced[$z] = " " . "$sliced[$z]";
}
ImageString($im, 2, 360, ($adjPosition + 1), "$sliced[$z]", $black);
ImageString($im, 2, 410, ($adjPosition + 1), "$ItemNames[$z]", $black);
}
// output to browser
ImagePNG($im);
}
?>
<?php
function sanitizePhone($phone)
{
return preg_replace("/[ ()]/", "", $phone);
}
function sanitizeAddress($address)
{
return str_replace(array("\r\n", "\r"), array("\n", "\n"), $address);
}
function sanitizePostcode($postcode)
{
//force uppercase, remove any possible stupid spaces and add the single space in the correct place
$postcode = strtoupper($postcode);
$postcode = str_replace(" ", "", $postcode);
return substr_replace($postcode, " ", -3, 0);
}
?>
<?
<?php
// To use:
// include_once "session.php"
// $mysession = new Session;
//
//
// $mysession->loggedin is TRUE if they have logged in
//
// other attributes are :
// username - the username they logged in with
// fullname - whatever full name we know for them
// last - unix timestamp for their previous page access
// lastseen - unix timestamp for their previous page access
// data - var/array for persistant data, commit by calling the 'save' method
// Session management and authentication mechanism.
class Session {
public $loggedin=FALSE; // Is this a valid logged in user ?
public $username=''; // Username
public $fullname; // Fullname
public $email=0; // Email waiting?
public $email_forward; // Email forwarded?
public $groups =array(); // users groups
public $data=''; // Var/array for session persistant data
public $token=''; // session identifier
public $last=''; // Time of last page request
private $timeout = 300; // Idle timeout limit in minutes
private $table = "session"; // session storage table (const)
private $datahash=''; // hash of data field
// Create a new session id
private function newsession()
{
global $DB;
$try = 0;
do {
$tt=date("D M d H:i:s Y");
$ip = $_SERVER['REMOTE_ADDR'];
$token = md5("$ip$tt$try");
$old = $DB->GetAll("select hash from session where hash=?", array($token));
}while ($old);
$DB->Execute("insert into session (hash, time, ip) values (?,NOW(),?)", array($token, $ip));
setcookie("session", $token, NULL, "/");
$this->token = $token;
return;
}
// Public Object constructor
function __construct()
{
global $DB;
unset($token);
// The possible form elements
$submit = @$_POST['Login'];
$logout = @$_POST['Logout'];
$session_user = strtolower(@$_POST['session_user']);
$session_pass = @$_POST['session_pass'];
// We havent logged them in yet
$this->loggedin = FALSE;
// Time out any old sessions
$DB->Execute("delete from {$this->table} where time < NOW() - '{$this->timeout} minutes'::reltime");
// Log them out if they ask
if ($logout=="Logout") {
$this->logout();
return;
}
// the possible token data passed from a form
if (isset($_REQUEST['token']))
$token = $_REQUEST['token'];
// Check if we were handed a specific token identifier
// Otherwise use the value from the cookie we gave out
if (!isset($token) && isset($_COOKIE['session']))
$token=@$_COOKIE['session'];
if (isset($token)) $this->token = $token;
// Okay, so we still dont have a session id
// so issue a new one and go back to core
if (!isset($token))
{
$this->newsession();
return;
}
// Is this a login attempt ?
if ($submit != '' && $session_user != '' && $session_pass != '')
{
$this->session_init($session_user, $session_pass);
}
// Retrieve session information
$oldsess=$DB->GetAll("select * from {$this->table} where hash=?", array($this->token));
if (!$oldsess || count($oldsess) < 1) {
$this->errormsg="Session timed out";
$this->newsession();
return;
}
// Extract detail of session for pass-back
$detail = $oldsess[0];
$this->data = unserialize((string)$detail['data']);
$this->last = strtotime($detail['time']);
$this->datahash = md5(serialize($this->data));
// are we actually logged in, fill in more
if ($detail['username']) {
// Are we using HTTPS?
if (!isset($_SERVER['HTTPS'])) {
$this->errormsg = "Insecure Connection";
$this->loggedin = FALSE;
return;
}
// User is valid but they're coming from the wrong IP
if ($detail['ip'] != $_SERVER['REMOTE_ADDR']) {
$this->errormsg = "Your IP address has changed - you have been logged out";
$this->logout();
return;
}
$this->username=$detail['username'];
$this->fetch_detail($detail['username']);
$this->loggedin = TRUE;
}
// update time stamp
$DB->Execute( "update {$this->table} set time=NOW() where hash=?", array($this->token));
}
// Public function: Store the session data away in the database
public function save( )
{
global $DB;
$newhash = md5(serialize($this->data));
if ($newhash == $this->datahash) {
// no change in data, dont save
return;
}
$DB->Execute("update {$this->table} set data=? where hash=?", array(serialize($this->data),$this->token));
}
// Public function: force a logout of the session
public function logout( )
{
global $DB;
$DB->Execute("delete from session where hash=?", array($this->token));
$this->newsession();
$this->loggedin = FALSE;
}
// Fill out any extra details we know about the user
private function fetch_detail( $user )
{
if (!($ldap = @ldap_connect("ldap://localhost"))) {
$this->errormsg="LDAP connect failed";
return FALSE;
}
$info = $this->ldap_getuser($ldap, $user);
if (!$info) return FALSE;
ldap_close($ldap);
// Check the user's email status
$mailstat = @stat("/var/spool/mail/".$user);
if ($mailstat[size]>0) {
if ($mailstat[mtime]>$mailstat[atime]) $this->email = 2;
else $this->email = 1;
}
if (file_exists($info['homedirectory'][0]."/.forward")) {
$forward = file($info['homedirectory'][0]."/.forward");
$this->email_forward = ereg_replace("\n", "", $forward[0]);
}
$this->fullname = $info['cn'][0];
$this->groups = $info['grouplist'];
}
/* check using mod_auth_externals helper
private function check_pass($user, $pass)
{
if ($fd === FALSE) {
$this->errormsg = "Auth system error";
return FALSE;
}
fwrite($fd, "$user\n");
fwrite($fd, "$pass\n");
$ret = pclose($fd);
if ($ret == 0) return TRUE;
$this->autherror = "u='$user' p='$pass' ret=$ret";
$this->errormsg = "Invalid Username or Password";
return FALSE;
}
*/
// Get a users full record from ldap
private function ldap_getuser($ldap, $user)
{
// publically bind to find user
if (!($bind=@ldap_bind($ldap, "", ""))) {
$this->errormsg="LDAP bind failed";
return NULL;
}
// find the user
if (!($search=@ldap_search($ldap, "dc=sucs,dc=org", "(&(uid=$user))"))) {
$this->errormsg="LDAP search fail";
return NULL;
}
$n = ldap_count_entries($ldap, $search);
if ($n < 1) {
$this->errormsg = "Username or Password Incorrect";
return NULL;
}
$info = ldap_get_entries($ldap, $search);
if (($grpsearch=@ldap_search($ldap, "ou=Group,dc=sucs,dc=org", "memberuid=$user"))) {
$gn = ldap_count_entries($ldap,$grpsearch);
$gpile = ldap_get_entries($ldap, $grpsearch);
$glist=array();
for ($i=0;$i<$gn;$i++) {
$glist[ $gpile[$i]['cn'][0] ] = $gpile[$i]['gidnumber'][0];
}
$info[0]['grouplist'] = $glist;
}
return $info[0];
}
/* check using ldap directly */
private function check_pass($user, $pass)
{
// Open connection
if (!($ldap = @ldap_connect("ldap://localhost"))) {
$this->errormsg="LDAP connect failed";
return FALSE;
}
$info = $this->ldap_getuser($ldap, $user);
if (!$info) return FALSE;
$real = @ldap_bind($ldap, $info['dn'], $pass);
ldap_close($ldap);
if ($real) return TRUE;
$this->errormsg="Username or Password Incorrect";
return FALSE;
}
// Private function: process login form
private function session_init($user, $pass)
{
global $DB;
// Check that this is a valid session start
// This prevents replay attacks
$sess = $DB->GetAll("select * from {$this->table} where hash=? and username is NULL", array($this->token));
if (!$sess || count($sess)<1) {
$this->errormsg = "Invalid session, login again.";
return;
}
if (!$this->check_pass($user, $pass)) return;
$this->username = $user;
// Update the session, filling in the blanks
$DB->Execute("update {$this->table} set username=?, time='NOW()', ip=? where hash=?", array($this->username, $_SERVER['REMOTE_ADDR'], $this->token));
// Return back to normal session retrieval
}
class Session
{
public $loggedin = FALSE; // Is this a valid logged in user ?
public $username = ''; // Username
public $fullname; // Fullname
public $email = 0; // Email waiting?
public $email_forward; // Email forwarded?
public $groups = array(); // users groups
public $printbalance; // printer balance
public $data = ''; // Var/array for session persistant data
public $token = ''; // session identifier
public $logintime = ''; // Time which user last gave us credentials
public $lastseen = ''; // Time of last page request
private $timeout = 2880; // Idle timeout limit in minutes (session deleted), 2880 == 48 hours
private $anonymous_timeout = 120; // Idle timeout limit for sessions which aren't logged in (set lower to stop the session table getting swamped)
private $secure_timeout = 30; // Idle timeout limit in minutes (consider session less secure, require reauth for sensitive ops)
private $max_session_length = 11520; // maximum length of a session, 11520 == 8 days
private $table = "session"; // session storage table (const)
private $datahash = ''; // hash of data field
// Create a new (insecure) session
private function newsession()
{
global $DB, $preferred_hostname, $dbname;
$token = $this->genSessionID();
$DB->Execute("insert into {$this->table} (hash, lastseen, ip) values (?,NOW(),?)", array($token, $_SERVER['REMOTE_ADDR']));
setcookie($dbname . "_session", $token, NULL, "/", $preferred_hostname);
// delete loggedin cookie if it exists
setcookie($dbname . "_loggedin", FALSE, time(), "/");
$this->token = $token;
return;
}
public function isSecure()
{
global $DB;
// is user coming from the IP address they were when they logged in?
if ($detail['ip'] != $_SERVER['REMOTE_ADDR']) {
return false;
} elseif (time() > ($this->logintime + $this->secure_timeout)) {
// has it been too long since we last asked for credentials?
return false;
}
}
// Public Object constructor
function __construct()
{
global $DB, $preferred_hostname, $baseurl, $dbname;
unset($token);
// if user requests a page via HTTP and claims to be logged in, bump them to HTTPS
if (!isset($_SERVER['HTTPS']) && (@$_COOKIE[$dbname . '_loggedin'] == "true")) {
header("HTTP/1.0 307 Temporary redirect");
header("Location: https://{$preferred_hostname}{$baseurl}{$_SERVER['PATH_INFO']}");
return;
}
// The possible form elements
$submit = @$_POST['Login'];
$logout = @$_POST['Logout'];
$session_user = strtolower(@$_POST['session_user']);
$session_pass = @$_POST['session_pass'];
// We havent logged them in yet
$this->loggedin = FALSE;
// Time out any old sessions
$DB->Execute(
"delete from {$this->table} where lastseen < NOW() - '{$this->timeout} minutes'::reltime " .
"or logintime < NOW() - '{$this->max_session_length} minutes'::reltime " .
"or (username IS NULL AND lastseen < NOW() - '{$this->anonymous_timeout} minutes'::reltime)"
);
// the possible token data passed from a form
if (isset($_REQUEST['token']))
$token = $_REQUEST['token'];
// Check if we were handed a specific token identifier
// Otherwise use the value from the cookie we gave out
if (!isset($token) && isset($_COOKIE[$dbname . '_session']))
$token = @$_COOKIE[$dbname . '_session'];
if (isset($token)) $this->token = $token;
// Log them out if they ask
if ($logout == "Logout") {
$this->logout();
return;
}
// Okay, so we still dont have a session id
// so issue a new one and go back to core
if (!isset($token)) {
$this->newsession();
return;
}
// Is this a login attempt ?
if ($submit != '' && $session_user != '' && $session_pass != '') {
// filter out everything but A-Z a-z 0-9 . - _ from username
$safeusername = preg_replace("/[^A-Za-z0-9\.\-\_]/", '', $session_user);
if ($safeusername != $session_user) {
trigger_error("Invalid username", E_USER_NOTICE);
$this->newsession();
return;
} elseif ($safeusername == $session_user) {
$this->session_init($safeusername, $session_pass);
}
}
// Retrieve session information
$oldsess = $DB->GetAll("select * from {$this->table} where hash=?", array($this->token));
if (!$oldsess || count($oldsess) < 1) {
trigger_error("Session timed out", E_USER_NOTICE);
$this->newsession();
return;
}
// Extract detail of session for pass-back
$detail = $oldsess[0];
$this->data = unserialize((string)$detail['data']);
$this->lastseen = strtotime($detail['lastseen']);
$this->logintime = strtotime($detail['logintime']);
$this->datahash = md5(serialize($this->data));
// are we actually logged in, fill in more
if ($detail['username']) {
// Are we using HTTPS?
if (!isset($_SERVER['HTTPS'])) {
trigger_error("Insecure Connection", E_USER_NOTICE);
$this->loggedin = FALSE;
return;
}
$this->username = $detail['username'];
$this->fetch_detail($detail['username']);
$this->loggedin = TRUE;
}
// update time stamp
$DB->Execute("update {$this->table} set lastseen=NOW() where hash=?", array($this->token));
// check to see if there any messages stored for this user
if (isset($this->data['messages'])) {
global $messages;
if (is_array($messages)) {
$messages += $this->data['messages'];
} else {
$messages = $this->data['messages'];
}
unset($this->data['messages']);
$this->save();
}
}
// generate a string suitable to be used as a session ID
private function genSessionID()
{
global $DB;
$try = 0;
$tt = date("D M d H:i:s Y");
$ip = $_SERVER['REMOTE_ADDR'];
$nonce = rand(); // this should stop session IDs being (easily) guessable by someone with the algorithm
do {
$token = md5("$ip$tt$nonce" . $try++);
$old = $DB->GetAll("select hash from {$this->table} where hash=?", array($token));
} while ($old);
return $token;
}
// Public function: Store the session data away in the database
public function save()
{
global $DB;
$newhash = md5(serialize($this->data));
if ($newhash == $this->datahash) {
// no change in data, dont save
return;
}
$DB->Execute("update {$this->table} set data=? where hash=?", array(serialize($this->data), $this->token));
}
// Public function: force a logout of the session
public function logout()
{
global $DB, $dbname;
$DB->Execute("delete from {$this->table} where hash=?", array($this->token));
$this->newsession();
$this->loggedin = FALSE;
setcookie($dbname . "_loggedin", FALSE, time(), "/");
}
// Fill out any extra details we know about the user
private function fetch_detail($user)
{
if (!($ldap = @ldap_connect("ldap://localhost"))) {
trigger_error("LDAP connect failed", E_USER_ERROR);
return FALSE;
}
$info = $this->ldap_getuser($ldap, $user);
if (!$info) return FALSE;
ldap_close($ldap);
// Check the user's email status
/*$mailstat = @stat("/var/spool/mail/".$user);
if ($mailstat[size]>0) {
if ($mailstat[mtime]>$mailstat[atime]) $this->email = 2;
else $this->email = 1;
}*/
// a sure-fire way to check to see if the user has any unread email
// the bash script returns 0 for no and 1 for yes, takes one arg, username
$this->email = shell_exec("../plugins/sucsunreadmail $user");
if (file_exists($info['homedirectory'][0] . "/.forward") && ! is_link($info['homedirectory'][0] . "/.forward")) {
$forward = file($info['homedirectory'][0] . "/.forward");
$this->email_forward = preg_replace("/\n/", "", $forward[0]);
}
$this->fullname = $info['cn'][0];
$this->groups = $info['grouplist'];
$db = new SQLite3('/etc/pykota/pykota.db');
$result = $db->query("SELECT balance FROM users WHERE username='$user';");
$this->printbalance = $result->fetchArray()[0];
}
/* check using mod_auth_externals helper
private function check_pass($user, $pass)
{
if ($fd === FALSE) {
$this->errormsg = "Auth system error";
return FALSE;
}
fwrite($fd, "$user\n");
fwrite($fd, "$pass\n");
$ret = pclose($fd);
if ($ret == 0) return TRUE;
$this->autherror = "u='$user' p='$pass' ret=$ret";
$this->errormsg = "Invalid Username or Password";
return FALSE;
}
*/
// Get a users full record from ldap
private function ldap_getuser($ldap, $user)
{
// publically bind to find user
ldap_set_option($ldap, LDAP_OPT_PROTOCOL_VERSION, 3);
if (!($bind = @ldap_bind($ldap))) {
trigger_error("LDAP bind failed", E_USER_ERROR);
return NULL;
}
// find the user
if (!($search = @ldap_search($ldap, "dc=sucs,dc=org", "(&(uid=$user))"))) {
trigger_error("LDAP search fail", E_USER_ERROR);
return NULL;
}
$n = ldap_count_entries($ldap, $search);
if ($n < 1) {
trigger_error("Username or Password Incorrect", E_USER_WARNING);
return NULL;
}
$info = ldap_get_entries($ldap, $search);
if (($grpsearch = @ldap_search($ldap, "ou=Group,dc=sucs,dc=org", "memberuid=$user"))) {
$gn = ldap_count_entries($ldap, $grpsearch);
$gpile = ldap_get_entries($ldap, $grpsearch);
$glist = array();
for ($i = 0; $i < $gn; $i++) {
$glist[$gpile[$i]['cn'][0]] = $gpile[$i]['gidnumber'][0];
}
$info[0]['grouplist'] = $glist;
}
return $info[0];
}
/* check using ldap directly */
public function check_pass($user, $pass)
{
// Open connection
if (!($ldap = @ldap_connect("ldap://localhost"))) {
trigger_error("LDAP connect failed", E_USER_ERROR);
return FALSE;
}
$info = $this->ldap_getuser($ldap, $user);
if (!$info) return FALSE;
$real = @ldap_bind($ldap, $info['dn'], $pass);
ldap_close($ldap);
if ($real) return TRUE;
trigger_error("Username or Password Incorrect", E_USER_WARNING);
return FALSE;
}
// Private function: process login form
private function session_init($user, $pass)
{
global $DB, $preferred_hostname, $dbname;
// Check that this is a valid session start
// This prevents replay attacks
$sess = $DB->GetAll("select * from {$this->table} where hash=? and username is NULL", array($this->token));
if (!$sess || count($sess) < 1) {
trigger_error("Invalid session, login again.", E_USER_WARNING);
return;
}
if (!$this->check_pass($user, $pass)) return;
$this->username = $user;
// the token has likely been used on an insecure connection
// so generate a new one with the secure flag set
$oldtoken = $this->token;
$this->token = $this->genSessionID();
setcookie($dbname . "_session", $this->token, time() + $this->max_session_length * 60, "/", $preferred_hostname, TRUE);
// set a cookie as a hint that we're logged in
// this can be checked for to allow redirecting to SSL to get the secure cookie
setcookie($dbname . "_loggedin", "true", time() + $this->max_session_length * 60, "/");
// Update the session, filling in the blanks
$DB->Execute("update {$this->table} set hash=?, username=?, logintime='NOW()', lastseen='NOW()', ip=? where hash=?",
array($this->token, $this->username, $_SERVER['REMOTE_ADDR'], $oldtoken));
// Return back to normal session retrieval
}
} // end of Class
<?php
// sorry
// gib errars plox
//error_reporting(E_ALL);
//ini_set('display_errors', 1);
// without this the entire thing doesn't work
include_once("../suapiv2-key.php");
//SUCS Org ID According to the SU
$orgid = "6613";
$apibaseurl = "http://su-apiv2.sucs.org/?apikey=${apikey}&orgid=${orgid}";
// Get the shit json the suapiv2 spits out
$ch = curl_init($apibaseurl);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
$raw_data = curl_exec($ch);
curl_close($ch);
$formated_raw_data = json_decode($raw_data, true); //convert it into php arrays
/*
* $membership_data is an array where each type of memebrship is it;s own array
* Insdie each of those arrays the actual arrary of members is under ["Detail_Collection"]["Detail"]
* So $membership_data[0]["Detail_Collection"]["Detail"][0] will get you the array containing the
* first member in the data
*/
$membership_data = $formated_raw_data["table1"]["table1_Product_Collection"]["table1_Product"];
// make a new array that just contains *every* member no matter what they bought
$just_members = array();
function filter_array($data){
global $just_members;
// we have more than one type of member if this is true
if (count($data[0]["Detail_Collection"]["Detail"][0]["@attributes"]) == 7){
//split them up then feed them back
foreach ($data as $membershipType){
filter_array($membershipType);
}
// we have more then 1 of the same type of member if this is true
} else if (count($data["Detail_Collection"]["Detail"][0]["@attributes"]) == 7) {
//wack them onto the used array
foreach ($data["Detail_Collection"]["Detail"] as $member) {
array_push($just_members, $member["@attributes"]);
}
// we have 1 member :(
} else if (count($data["Detail_Collection"]["Detail"]["@attributes"]) == 7) {
array_push($just_members, $data["Detail_Collection"]["Detail"]["@attributes"]);
}
}
filter_array($membership_data);
/* You can now use $just_members to probe member detials. It's an array of arrays which each contain:
* transaction_id (recepit id)
* purchaser (full name)
* textbox6 (under 18 or not) NOT SURE OF THE FORMAT
* card_number (student number)
* shop_name (where they bought sucs memebrship)
* qty (how many sucs memebrships they bought)
* purchase_date (timestamp of when they bought memebrship)4
*/
/*
* Used by /susignup to verify that the stduent number and transaction id combo are valid
* returns true or false
*/
function check_su_sid_and_trans($sid, $transid)
{
global $just_members;
foreach ($just_members as $member) {
if ($sid == $member["card_number"] && $transid == $member["transaction_id"]) {
return true;
}
}
return false;
}
/*
* Used to verify taht a given stduent number has paid for membership via the su system.
* returns false or true
*/
function check_su_sid($sid)
{
global $just_members;
foreach ($just_members as $member) {
if ($sid == $member["card_number"]) {
return true;
}
}
return false;
}
?>
<?
<?php
require_once("validationData.php");
require_once("sanitization.php");
/* useful validation functions */
//check for a valid email address
function validEmail ($email)
function validEmail($email)
{
global $error;
//split user and domain
list($user,$domain) = explode("@", $email);
// check for bad characters, and check for zero length user & domain
if(!eregi("^[_a-z0-9-]+(\.[_a-z0-9-]+)*@[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,3})$",$email) or !$user or !$domain )
{
$error = 'an invalid email address (syntax)';
return false;
}
// Syntax OK
// Check for an mail server
elseif(!getmxrr($domain,$mx) or !gethostbyname($domain))
{
$error = "no mail servers listed for '$domain'";
return false;
}
else
{
// Email address valid from technical point of view
return true;
}
global $error;
// check for valid syntax
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
$error = 'Invalid email address (syntax)';
return false;
}
// Syntax OK
// domain consists of any character after a '@' and cannot contain '@'
// therefore any character after the last '@' is part of the domain
$domain = substr($email, strrpos($email, '@') + 1);
// Check for an mail server
if (!getmxrr($domain, $mx) or !gethostbyname($domain)) {
$error = "No mail servers listed for '$domain'";
return false;
} else {
// Email address valid from technical point of view
return true;
}
}
# Use cracklib to check for weak passwords.
# returns FALSE if the password is good i.e. not weak
# otherwise returns a string saying why its weak
function weakPassword($password)
{
// Try fedora then debian known paths
if (file_exists("/usr/sbin/cracklib-check"))
$cracklib = "/usr/sbin/cracklib-check";
else
if (file_exists("/usr/sbin/crack_testlib"))
$cracklib = "/usr/sbin/crack_testlib";
else
return "Cannot find cracklib";
$proc = proc_open($cracklib, array(0 => array("pipe", "r"), 1 => array("pipe", "w")), $pipes, '/tmp/', NULL);
if (!is_resource($proc)) {
return "Cannot find cracklib";
}
fwrite($pipes[0], $password);
fclose($pipes[0]);
$last = "";
do {
$last = fgets($pipes[1]);
if ($last !== FALSE) $answer = trim($last);
} while ($last !== FALSE);
fclose($pipes[1]);
proc_close($proc);
$answer = substr(strrchr($answer, ":"), 2);
if (strtolower($answer) == "ok") return FALSE;
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 exists
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 syntactically valid
$syntax = preg_match("/^[a-z][a-z0-9_]*$/", $username);
if (!$syntax || (strlen($username) < 2)) {
$error = "Usernames must start with a letter, only contain lowercase letters, numbers 0-9 and underscores (_) and be at least 2 characters.";
return false;
} // check if the username already exists
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;
}
}
// test whether a password is considered Strong Enough
// ideally we'd want to use cracklib or something here, but no RPM for the php bindings :-(
function strongPassword ($pass) {
// you call this a password? my cat could bruteforce this.
if (strlen($pass) < 6) {
return false;
}
// start at 0, and increment for certain features
$score = 0;
// greater than 8 characters
if (strlen($pass) > 8) $score++;
// includes lowercase characters
if (preg_match("/[a-z]/", $pass)) $score++;
// includes uppercase characters
if (preg_match("/[A-Z]/", $pass)) $score++;
// includes digits
if (preg_match("/\d/", $pass)) $score++;
// includes "non-word" characters
if (preg_match("/\W/", $pass)) $score++;
// I reckons if it has at least 3 of the above it should be... adequate
// better if it checked for dictionary words too though
if ($score > 3) {
return true;
} else {
return false;
}
function validSID($SID, $override)
{
global $error;
if ($override) {
if ($SID == "") {
$error = "You MUST provide some sort of student number";
return false;
} else {
return true;
}
} else {
if (!preg_match("/^[0-9]{6,}$/", $SID)) {
$error = "Invalid student ID";
return false;
} elseif (sidUsed($SID)) {
$error = "A user with that student ID already exists, 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 validName($realName, $override)
{
global $error;
if ($override) {
if ($realName == "") {
$error = "You MUST provide some sort of name";
return false;
} else {
return true;
}
} else {
// names can legally be really weird so just check that it is at least 1 visible character
// followed by any number of non-control characters
$realName = trim($realName);
if (!preg_match("/^[[:graph:]][[:print:]]*$/", $realName)) {
$error = "Invalid name";
return false;
} else {
return true;
}
}
}
function validAddress($address)
{
global $error;
$address = sanitizeAddress($address);
// check that they at least entered in something. Address doesn't need to be as strict when the postcode is.
$regex = "/^.{5,}+$/s";
if (!preg_match($regex, $address)) {
$error = "Please supply a valid address.";
return false;
} else {
return true;
}
}
function validPostcode($postcode)
{
$postcode = sanitizePostcode($postcode);
// matches all postcodes following the valid format described in a 2012 government published document
$postcodeRegex = "/^([A-Z](([0-9][0-9]?)|([A-Z][0-9][0-9]?)|([A-Z]?[0-9][A-Z])) ?[0-9][ABD-HJLNP-UW-Z]{2})$/";
if (!preg_match($postcodeRegex, $postcode)) {
return false;
} else {
return $postcode;
}
}
function validPhone($phone)
{
global $error;
$phone = sanitizePhone($phone);
if (!preg_match("/^\+?[0-9-]{10,}$/", $phone)) {
$error = "Must be all numbers";
return false;
}
return true;
}
function validSignupEmail($email)
{
global $error;
if (preg_match('/@sucs\.$/', $email)) {
$error = "SUCS email addresses are not allowed";
return false;
} elseif (!validEmail($email)) {
return false;
} else {
return true;
}
}
?>
<?php
// lookup real names from sid's using campus ldap
function lookupSID($sid)
{
$ds = ldap_connect("192.168.10.16");
ldap_set_option($ds, LDAP_OPT_NETWORK_TIMEOUT, 10); /* 10 second timeout */
ldap_bind($ds);
$sr = ldap_search($ds, "ou=Active,ou=Resources,o=Swansea", "EDUPERSONTARGETEDID=" . $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 using the university's website
function lookup_postcode($postcode)
{
include_once "../paf-key.php";
$url = "http://paf.sucs.org/?apikey=$apikey&postcode=" . rawurlencode($postcode);
$req = curl_init($url);
curl_exec($req);
curl_close($req);
}
?>
<?php
function smarty_function_articlesummary($params, &$smarty) {
$article = $params['article'];
$id = $params['id'];
$title = rawurlencode($params['title']);
function smarty_function_articlesummary($params, &$smarty)
{
$linky = "<span style=\"float: right\"><a href=\"/News/".rawurlencode($title)."\">Read More</a></span>";
$article = $params['article'];
$id = $params['id'];
$title = rawurlencode($params['title']);
$matches = array();
preg_match("/^(<p>.*?<\/p>)/", $article, $matches);
$summary = preg_replace("/<\/p>/","$linky</p>", $matches[0]);
return $summary;
}
$linky = "<span style=\"float: right\"><a href=\"/News/" . rawurlencode($title) . "\">Read More</a></span>";
$matches = array();
preg_match("/^(<p>.*?<\/p>)/s", $article, $matches);
$summary = preg_replace("/<\/p>/", "$linky</p>", $matches[0]);
return $summary;
}
?>
<?php
// map number of bananas (-3 to 3) to a CSS class name
function smarty_function_award_image_class($params, &$smarty) {
$number = $params['number'];
function smarty_function_award_image_class($params, &$smarty)
{
$number = $params['number'];
$image_class = "";
$image_class = "";
switch ($number) {
case -3:
$image_class = "green3";
break;
case -2:
$image_class = "green2";
break;
case -1:
$image_class = "green1";
break;
case 1:
$image_class = "yellow1";
break;
case 2:
$image_class = "yellow2";
break;
case 3:
$image_class = "yellow3";
break;
}
switch ($number) {
case -3:
$image_class = "green3";
break;
case -2:
$image_class = "green2";
break;
case -1:
$image_class = "green1";
break;
case 1:
$image_class = "yellow1";
break;
case 2:
$image_class = "yellow2";
break;
case 3:
$image_class = "yellow3";
break;
}
return $image_class;
return $image_class;
}
?>
<?php
function smarty_function_bananaprint($params, &$smarty) {
$output = "";
$score = $params['score'];
if ($score > 0) {
while ($score >= 50) {
$output .= " <img src=\"/images/bananas/banana-crate.png\" width=\"92\" height=\"64\" alt=\"50\" title=\"Crate of 50 Bananas\" />\n";
$score -= 50;
}
while ($score >= 5) {
$output .= " <img src=\"/images/bananas/banana-bunch.png\" alt=\"5\" width=\"64px\" height=\"64px\" title=\"Bunch of 5 Bananas\" />\n";
$score -= 5;
}
while ($score > 0) {
$output .= " <img src=\"/images/bananas/banana-one.png\" alt=\"1\" width=\"25px\" height=\"64px\" title=\"1 Banana\" />\n";
$score -= 1;
}
}else if ($score == 0) {
$output .= " <img src=\"/images/bananas/banana-zero.png\" alt=\"0\" width=\"25px\" height=\"64px\" title=\"0 Bananas\" />\n";
}else {
while ($score <= -50) {
$output .= " <img src=\"/images/bananas/banana-g-crate.png\" width=\"92\" height=\"64\" alt=\"-50\" title=\"Crate of 50 Green Bananas\" />\n";
$score += 50;
}
while ($score <= -5) {
$output .= " <img src=\"/images/bananas/banana-g-bunch.png\" width=\"64px\" height=\"64px\" alt=\"-5\" title=\"Bunch of 5 Green Bananas\" />\n";
$score += 5;
}
while ($score < 0) {
$output .= " <img src=\"/images/bananas/banana-g-one.png\" width=\"25px\" height=\"64px\" alt=\"-1\" title=\"1 Green Banana\" />\n";
$score += 1;
}
}
return $output;
}
function smarty_function_bananaprint($params, &$smarty)
{
$output = "";
$score = $params['score'];
if ($score > 0) {
while ($score >= 250) {
$output .= " <img src=\"/images/bananas/banana-container.png\" width=\"92\" height=\"64\" alt=\"250\" title=\"Container of 250 Bananas\" />\n";
$score -= 250;
}
while ($score >= 50) {
$output .= " <img src=\"/images/bananas/banana-crate.png\" width=\"92\" height=\"64\" alt=\"50\" title=\"Crate of 50 Bananas\" />\n";
$score -= 50;
}
while ($score >= 5) {
$output .= " <img src=\"/images/bananas/banana-bunch.png\" alt=\"5\" width=\"64px\" height=\"64px\" title=\"Bunch of 5 Bananas\" />\n";
$score -= 5;
}
while ($score > 0) {
$output .= " <img src=\"/images/bananas/banana-one.png\" alt=\"1\" width=\"25px\" height=\"64px\" title=\"1 Banana\" />\n";
$score -= 1;
}
} else if ($score == 0) {
$output .= " <img src=\"/images/bananas/banana-zero.png\" alt=\"0\" width=\"25px\" height=\"64px\" title=\"0 Bananas\" />\n";
} else {
while ($score <= -250) {
$output .= " <img src=\"/images/bananas/banana-g-container.png\" width=\"92\" height=\"64\" alt=\"250\" title=\"Container of 250 Green Bananas\" />\n";
$score += 250;
}
while ($score <= -50) {
$output .= " <img src=\"/images/bananas/banana-g-crate.png\" width=\"92\" height=\"64\" alt=\"-50\" title=\"Crate of 50 Green Bananas\" />\n";
$score += 50;
}
while ($score <= -5) {
$output .= " <img src=\"/images/bananas/banana-g-bunch.png\" width=\"64px\" height=\"64px\" alt=\"-5\" title=\"Bunch of 5 Green Bananas\" />\n";
$score += 5;
}
while ($score < 0) {
$output .= " <img src=\"/images/bananas/banana-g-one.png\" width=\"25px\" height=\"64px\" alt=\"-1\" title=\"1 Green Banana\" />\n";
$score += 1;
}
}
return $output;
}
?>
<?php
function smarty_function_buildpath($params, &$smarty) {
if ($params['item']=="") return "/";
foreach ($params['list'] as $crumb) {
if ($crumb!="") $output.="/".$crumb;
if ($crumb==$params['item']) return $output;
}
function smarty_function_buildpath($params, &$smarty)
{
if ($params['item'] == "") return "/";
foreach ($params['list'] as $crumb) {
if ($crumb != "") $output .= "/" . rawurlencode($crumb);
if ($crumb == $params['item']) return $output;
}
}
}
?>
<?php
function smarty_function_getparams($params, &$smarty) {
function smarty_function_getparams($params, &$smarty)
{
$output = "";
$output = "";
if (isset($params['gets'])) {
$output .= "?";
$i = 0;
foreach ($params['gets'] as $name => $value) {
if ((trim($name) == "") || (trim($value) == ""))
continue;
if (isset($params['gets'])) {
$output .= "?";
$i = 0;
foreach ($params['gets'] as $name => $value) {
if ((trim($name) == "") || (trim($value) == ""))
continue;
if ($i != 0) {
$output .= "&amp;";
} else {
$i++;
}
$output .= "$name="."$value";
}
}
if ($i != 0) {
$output .= "&amp;";
} else {
$i++;
}
$output .= "$name=" . "$value";
}
}
return $output;
return $output;
}
?>
<?php
function smarty_function_logo_of_the_day($params, &$smarty)
{
$logos = glob("../htdocs/images/awesomelogos/*.png");
$logo_of_the_day = $logos[array_rand($logos)];
$logo_size = getimagesize($logo_of_the_day);
$width_height_string = $logo_size[3];
$logo_src = str_replace("../htdocs", "", $logo_of_the_day);
return "<img src=\"$logo_src\" $width_height_string alt=\"SUCS\" />";
}
?>
<?php
function smarty_function_memberwebsitelist($params, &$smarty) {
$list = $params['members'];
$letter = "";
foreach ($list as $item) {
if ($letter != strtoupper(substr($item['uid'], 0, 1))) {
$letter = strtoupper(substr($item['uid'], 0, 1));
}
$members[$letter][] = $item['uid'];
}
foreach ($members as $letter => $lettermembers) {
$memcount = sizeof($lettermembers);
$col1count = round($memcount / 2);
$col1height = $col1count * 1.2;
$output .= "<h4>$letter</h4>\n<ul>\n";
for ($i = 0; $i < $memcount; $i++) {
$member = $lettermembers[$i];
if ($i < $col1count) {
$output .= "<li class=\"column1\">";
} elseif ($i == $col1count) {
$output .= "<li class=\"column2\" style=\"margin-top: -{$col1height}em\">";
} else {
$output .= "<li class=\"column2\">";
}
$output .= "<a href=\"http://sucs.org/~$member\">$member</a></li>\n";
}
$output .= "</ul>\n";
}
return $output;
function smarty_function_memberwebsitelist($params, &$smarty)
{
$list = $params['members'];
$letter = "";
foreach ($list as $item) {
if ($letter != strtoupper(substr($item['uid'], 0, 1))) {
$letter = strtoupper(substr($item['uid'], 0, 1));
}
$members[$letter][] = $item['uid'];
}
foreach ($members as $letter => $lettermembers) {
$memcount = sizeof($lettermembers);
$col1count = round($memcount / 2);
$col1height = $col1count * 1.2;
$output .= "<h4>$letter</h4>\n<ul>\n";
for ($i = 0; $i < $memcount; $i++) {
$member = $lettermembers[$i];
if ($i < $col1count) {
$output .= "<li class=\"column1\">";
} elseif ($i == $col1count) {
$output .= "<li class=\"column2\" style=\"margin-top: -{$col1height}em\">";
} else {
$output .= "<li class=\"column2\">";
}
$output .= "<a href=\"http://sucs.org/~$member\">$member</a></li>\n";
}
$output .= "</ul>\n";
}
return $output;
}
?>
<?php
function smarty_modifier_encodestring($string)
{
$string = rawurlencode($string);
$string = str_replace("_", "%5F", $string);
$string = str_replace("%20", "_", $string);
return $string;
}
?>
#!/bin/bash
if finger -m $1 | grep "New mail" &> /dev/null
then
echo "1"
else
echo "0"
fi
\ No newline at end of file
......@@ -4,5 +4,13 @@ $base = "$BASEDIR/";
$baseurl = "/~$USERNAME/sucssite";
$preferred_hostname = "sucs.org";
$dbname="beta";
$sucsdbname="sucs-dummy";
$dbuser="$USERNAME";
// Settings for current election (god knows why they are here):
define('ELECTION_YEAR', '2015');
define('ELECTION_START', '2014-12-01 00:00');
define('ELECTION_END', '2014-12-07 23:59');
define('ELECTION_NO_MANIFESTO', 'https://sucs.org/~$USERNAME/sucssite/Vote/No%20Manifesto');
?>
......@@ -3,11 +3,7 @@
# Setup script for SUCS website in users' public_html dirs
echo "SUCS website setup script"
# (try and) make sure we're running this from the right place
# SUCS website
# ^ a nasty kludge cause I don't know how to do it more sensibly
# test for the magic phrase on line 7 of this very file
head -n 7 setup.sh 2> /dev/null | tail -n 1 | grep -q "SUCS website"
ls $(pwd)/setup.sh &> /dev/null
if [ $? -ne 0 ]; then echo "Please run this script from its containing directory"; exit; fi
# generate correct .htaccess
......@@ -27,5 +23,4 @@ echo "Done!"
# and you're done!
echo -e "\nYou should now be able to view your local copy of the SUCS site at http://sucs.org/~$USER/sucssite/"
echo "Please contact a member of the admin team if you require use of the development database"
echo "Please contact a member of the admin team to gain access to the development database"
\ No newline at end of file
<p>Sorry, but the page you requested does not exist on our server.</p>
<p>Try searching for it instead using the <em>Search SUCS</em> box.</p>
<p>Mae'r gymdeithas yn darparu rhestr hir o wasanaethau i ei haelodau. Dyma rhestr o wasanaethau:</p>
<ul>
<li><a href="desktop.php">Bwrddgwaith ar Galwad</a>.</li>
<li> Ebost POP3/IMAP gyda <a href="http://spamassassin.org/">SpamAssassin</a> - Gallwch chi cael ato fe wrth <a href="https://sucs.org/webmail">Ebost Gwe Amgryptio</a></li>
<li> Safle Gwe (Mae PHP a PostgreSQL yn ar gael)</li>
<li> Lle disg i chi pan eich cyfrif llyfgell yn wedi ei llanw neu wedi ei doriad</li>
<li> Rhestri Ebost</li>
<li> <a href="/help/advisory">Gwasanaethau cynghori rhaglennu</a></li>
<li> <a href="library.php">Llyfrgell Cyfeiriad</a> o llyfr poblogaidd (including Computer Science recommended course texts)</li>
<li> Hawl gweld 24awr i ein <a href="http://sucs.org/services/room.php">ystafell gyfrifiaduron</a><br />
Mae nodweddion yn cynnwys:<br />
<ul>
<li> Systemau yn wedi ei diweddaru gyda 2 cyfrifiaduron newydd (ac mwy yn dod yn fuan!)</li>
<li> Rhywdwaith yr aelodau i cysylltu eich gliniadur</li>
<li> Cyrchiad rhyngwyd gyflymder LAN</li>
<li> Llosgydd CD</li>
<li> Argraffydd laser mono ac argraffydd ffrwd incio lliwiau (am ddim o fewn rheswm)</li>
</ul>
</li>
</ul>