diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..95f960b5e3e8a958299cf714f38ce195f01fae21
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+sessionids
diff --git a/README.md b/README.md
index 6fcf4b600cbc7dd0113fdf52259f7ae42da65a6b..0f3fe37201fbb7f9e94e3aa8f17d071ac5ceee1c 100644
--- a/README.md
+++ b/README.md
@@ -2,3 +2,53 @@ webdispense
 ===========
 
 Web interface for Dispense
+
+[BOB] 20100501
+How to make the pictures work in the dispense interface:
+-there are mappings from slot names to file names in the index.html, just upload a picture for it into the icons directory and add a mapping
+-the pictures are 130 pixels wide, 49 high
+-don't forget to make a 'dark-' version of the image for when the slot is empty - there is a black50alpha layer in the folder that you can use
+
+Example Crontab
+---------------
+
+This clears the sessions
+
+* * * * * cd /services/webdispense/ && /usr/bin/python /services/webdispense/purge.py
+
+Apache Config
+-------------
+
+Example config
+
+<VirtualHost *:80>
+    Redirect / https://dispense.ucc.asn.au/
+    ServerName dispense.ucc.asn.au
+    Serveralias dispense.ucc.gu.uwa.edu.au
+</virtualhost>
+
+<VirtualHost *:443>
+    ServerName dispense.ucc.asn.au
+    ServerAlias dispense.ucc.gu.uwa.edu.au
+    
+    ServerAdmin wheel@ucc.gu.uwa.edu.au
+
+    SSLCertificateKeyFile /etc/letsencrypt/live/wildcard.ucc/privkey.pem
+    SSLCertificateFile /etc/letsencrypt/live/wildcard.ucc/fullchain.pem
+
+    Alias /favicon.ico /services/wiki/htdocs/favicon.ico
+
+    # Rewrite urls
+    RewriteEngine On
+
+    Alias / /services/webdispense/
+    Alias /dispense /services/webdispense/
+
+    # Dispense WebUI
+    <Directory /services/webdispense>
+      #Options +ExecCGI -Indexes
+      Options +ExecCGI +Indexes
+      Order deny,allow
+      Allow from all
+    </Directory>
+</VirtualHost>
diff --git a/apple-touch-icon.png b/apple-touch-icon.png
new file mode 100644
index 0000000000000000000000000000000000000000..924cbf60e52a85e54dc41db299d5a55048e0e6b0
Binary files /dev/null and b/apple-touch-icon.png differ
diff --git a/balance.py b/balance.py
index 1b3f72f876b35a9bc0c498b6ab522021370f6f3a..d16176f523aae3270f00823d3a67abe1786576a9 100755
--- a/balance.py
+++ b/balance.py
@@ -5,35 +5,40 @@ import commands,re
 print "Content-type: text/json"
 print
 import cgi,cgitb
-import os, Cookie
+import os
 cgitb.enable ()
+import sys, syslog
+
+#import getpass
 
 result = {}
 form = cgi.FieldStorage ()
-cookies = Cookie.SimpleCookie(os.environ.get("HTTP_COOKIE",""))
 
 def checkdata ():
-	if not (cookies.has_key("username") and cookies.has_key("pin")):
-		result = {"result" : "Missing information!"}
-		return result
+	if not (form.has_key("username") and form.has_key("sid")):
+		return {"result" : "Missing information!"}
 	expr_name = re.compile ("^\w*$")
-	expr_pin = re.compile ("^\d{4}$")
-	username = cookies["username"].value.lower ()
-	pin = cookies["pin"].value
-	if (not expr_name.match (cookies["username"].value)) or (not expr_pin.match (cookies["pin"].value)):
-		 result = {"result" : "Incorrect login!"}
-		 return result
-	output = commands.getoutput ("sudo checkpin " + username + " " + pin)
-	if output == "False":
-		result = {"result" : "Authentication failed!"}
-		return result
-	if not output == "True":
-		result = {"result" : "PIN not set up."}
-		return result
+	username = form["username"].value.lower ()
+	sid = form["sid"].value
+	if (not expr_name.match (username)):
+		return {"result" : "Invalid login!"}
+	file = open("sessionids", "r")
+        success = False
+        for line in file:
+                if (":" + username + ":" + sid + "\n") in line:
+                        success = True
+        file.close()
+	if not success:
+                log = "Authentication failure for user " + username + " from " + cgi.escape(os.environ["REMOTE_ADDR"]) + "\n"
+		syslog.syslog((syslog.LOG_NOTICE | syslog.LOG_AUTH), log)
+                #logfile = open("auth.log", "a")
+                #logfile.write(log)
+                #logfile.close()
+                return {"result" : "Authentication Failed"}
 	output = commands.getoutput ("dispense acct " + username);
 	expr_bal = re.compile ("(?P<bal>\d*\.\d*)")
 	balance = expr_bal.search (output).groups()[0]
-
+	
 	return {"result" : "success", "balance" : balance}
 
 
diff --git a/cookies.py b/cookies.py
deleted file mode 100755
index 7749cbc084b0fddd6977f1306deea782f43e44bf..0000000000000000000000000000000000000000
--- a/cookies.py
+++ /dev/null
@@ -1,12 +0,0 @@
-#!/usr/bin/env python
-import cgi, os, Cookie
-
-print "Content-type: text/html"
-print
-
-#cgi.print_environ_usage()
-#cgi.print_environ()
-
-cookies = Cookie.SimpleCookie(os.environ.get("HTTP_COOKIE",""))
-
-print "<pre>%s %s</pre>" % (cookies['username'].value, cookies['pin'].value)
diff --git a/dispense.css b/dispense.css
index faa82772064b738bae3920f2ab2335543e07d12c..79504e3a96914d13c975933a339d49240591653b 100644
--- a/dispense.css
+++ b/dispense.css
@@ -35,9 +35,12 @@ div.button {
 	margin-bottom:3px;
 	border:1px solid black;
 	padding:9px;
-	font-family:helvetica;
+	font-family:"Courier",monospace;
 	font-weight:bold;
-	color:white;
+	font-size:10pt;
+	color:rgb(0,255,0);
+	text-align:right;
+	line-height:100%
 }
 div.button:hover {
 	border:1px solid white;
@@ -68,7 +71,7 @@ div.button:hover {
 #logout:hover {
 	border:1px solid white;
 }
-#username, #pin {
+#username, #passwd {
 	width:100px;
 	height:25px;
 	margin-bottom:10px;
@@ -82,6 +85,7 @@ div.button:hover {
 	width:130px;
 	height:73px;
 	padding:6px;
-	font-family:monospace;
+	font-family:"Courier",monospace;
+	font-size:12pt;
 	color:rgb(0,255,0);
 }
diff --git a/dispense.py b/dispense.py
index a85e45e5fdefb74665f39025605c1be0356292fe..61cb3438db5531347c880117caf0b206bc60af78 100755
--- a/dispense.py
+++ b/dispense.py
@@ -5,37 +5,42 @@ import commands,re
 print "Content-type: text/json"
 print
 import cgi,cgitb
+import os
 cgitb.enable ()
-import os, Cookie
+import sys, syslog
+
+#import getpass
 
 result = {}
 form = cgi.FieldStorage ()
-cookies = Cookie.SimpleCookie(os.environ.get("HTTP_COOKIE",""))
 
 def checkdata ():
-	if not (cookies.has_key("username") and cookies.has_key("pin") and form.has_key("slot")):
-		result = {"result" : "Missing information!"}
-		return result
-	expr_name = re.compile ("^\w*$")
-	expr_pin = re.compile ("^\d{4}$")
+        if not (form.has_key("username") and form.has_key("sid")):
+                return {"result" : "Missing information!"}
+        expr_name = re.compile ("^\w*$")
 	expr_slot = re.compile ("^\d$")
-	username = cookies["username"].value.lower ()
-	pin = cookies["pin"].value
+        username = form["username"].value.lower ()
+        sid = form["sid"].value
 	slot = form["slot"].value
-	if (not expr_name.match (cookies["username"].value)) or (not expr_pin.match (cookies["pin"].value)):
-		 result = {"result" : "Incorrect login!"}
-		 return result
-	output = commands.getoutput ("sudo checkpin " + username + " " + pin)
-	if output == "False":
-		result = {"result" : "Authentication failed!"}
-		return result
-	if not output == "True":
-		result = {"result" : "PIN not set up."}
-		return result
+        if (not expr_name.match (username) or not expr_slot.match (slot)):
+                return {"result" : "Invalid login!"}
+	file = open("sessionids", "r")
+        success = False
+        for line in file:
+                if (":" + username + ":" + sid + "\n") in line:
+                        success = True
+        file.close()
+        if not success:
+		log = "Authentication failure for user " + username + " from " + cgi.escape(os.environ["REMOTE_ADDR"]) + "\n"
+                syslog.syslog((syslog.LOG_NOTICE | syslog.LOG_AUTH), log)
+                #logfile = open("auth.log", "a")
+                #logfile.write(log)
+                #logfile.close()
+                return {"result" : "Authentication Failed"}
 	output = commands.getoutput ("dispense -u " + username + " " + slot);
-	expr_disp = re.compile ("^Dispensing a.*")
+	expr_disp = re.compile (".*coke:.*")
 	if expr_disp.match (output):
 		return {"result" : "success"}
-	return {"result" : "Dispense failed."}
+	return {"result" : output}
 
 print str(checkdata ())
diff --git a/door.py b/door.py
index 44b5cb2a3de537a64c2674164f5bbc90b0061e4a..72a918eecad4124472990a67b60588f4c4a26841 100755
--- a/door.py
+++ b/door.py
@@ -5,34 +5,40 @@ import commands,re
 print "Content-type: text/json"
 print
 import cgi,cgitb
+import os
 cgitb.enable ()
+import sys, syslog
+
+#import getpass
 
 result = {}
 form = cgi.FieldStorage ()
 
 def checkdata ():
-	if not (form.has_key("username") and form.has_key("pin")):
-		result = {"result" : "Missing information!"}
-		return result
-	expr_name = re.compile ("^\w*$")
-	expr_pin = re.compile ("^\d{4}$")
-	username = form["username"].value
-	pin = form["pin"].value
-	if (not expr_name.match (form["username"].value)) or (not expr_pin.match (form["pin"].value)):
-		 result = {"result" : "Incorrect login!"}
-		 return result
-	output = commands.getoutput ("sudo checkpin " + username + " " + pin)
-	if output == "False":
-		result = {"result" : "Authentication failed!"}
-		return result
-	if not output == "True":
-		result = {"result" : "PIN not set up."}
-		return result
-	output = commands.getoutput ("dispense -u " + username + " opendoor");
-	expr_bal = re.compile ("(?P<bal>\d*\.\d*)")
-	balance = expr_bal.search (output).groups()[0]
-
-	return {"result" : "success", "balance" : balance}
+        if not (form.has_key("username") and form.has_key("sid")):
+                return {"result" : "Missing information!"}
+        expr_name = re.compile ("^\w*$")
+        username = form["username"].value.lower ()
+        sid = form["sid"].value
+        if (not expr_name.match (username)):
+                return {"result" : "Invalid login!"}
+	file = open("sessionids", "r")
+        success = False
+        for line in file:
+                if (":" + username + ":" + sid + "\n") in line:
+                        success = True
+        file.close()
+        if not success:
+		log = "Authentication failure for user " + username + " from " + cgi.escape(os.environ["REMOTE_ADDR"]) + "\n"
+                syslog.syslog((syslog.LOG_NOTICE | syslog.LOG_AUTH), log)
+                #logfile = open("auth.log", "a")
+                #logfile.write(log)
+                #logfile.close()
+                return {"result" : "Authentication Failed"}
+	output = commands.getoutput ("dispense -u " + username + " door");
+	if not "Dispense OK" in output:
+		return {"result" : "Door failed"}
+	return {"result" : "success"}
 	
 
 
diff --git a/icons/black50alpha.png b/icons/black50alpha.png
new file mode 100644
index 0000000000000000000000000000000000000000..4a2676534320ec34020196f7aea80aaac004f35a
Binary files /dev/null and b/icons/black50alpha.png differ
diff --git a/icons/dark-default.png b/icons/dark-default.png
new file mode 100644
index 0000000000000000000000000000000000000000..10c7d114c5e055453b5eb3abe23c44f83856f534
Binary files /dev/null and b/icons/dark-default.png differ
diff --git a/icons/dark-default.png.white b/icons/dark-default.png.white
new file mode 100644
index 0000000000000000000000000000000000000000..031eca9c77b91d71def6edb7e8a0563888a45e1b
Binary files /dev/null and b/icons/dark-default.png.white differ
diff --git a/icons/dark-dietcoke.png b/icons/dark-dietcoke.png
new file mode 100644
index 0000000000000000000000000000000000000000..e147554d4c785d284771183069a36542edf3ed3c
Binary files /dev/null and b/icons/dark-dietcoke.png differ
diff --git a/icons/dark-gingerbeer.png b/icons/dark-gingerbeer.png
new file mode 100644
index 0000000000000000000000000000000000000000..321ba9be10334fbad6a4306cdf5c34be83a0b2a2
Binary files /dev/null and b/icons/dark-gingerbeer.png differ
diff --git a/icons/dark-greensolo.png b/icons/dark-greensolo.png
new file mode 100644
index 0000000000000000000000000000000000000000..8c2801e089b06502a1c1a56613ab169a4a379724
Binary files /dev/null and b/icons/dark-greensolo.png differ
diff --git a/icons/dark-lemonfoo.png b/icons/dark-lemonfoo.png
new file mode 100644
index 0000000000000000000000000000000000000000..c7f72d8fc6fd55136b35c5e2718c6668aca0e563
Binary files /dev/null and b/icons/dark-lemonfoo.png differ
diff --git a/icons/dark-orangefoo.png b/icons/dark-orangefoo.png
new file mode 100644
index 0000000000000000000000000000000000000000..134fdc934ea8ac6b62019eb4f1ed89fecdb748f8
Binary files /dev/null and b/icons/dark-orangefoo.png differ
diff --git a/icons/dark-pasito.png b/icons/dark-pasito.png
new file mode 100644
index 0000000000000000000000000000000000000000..0efbeca9f99b16f8327ae8477e9e8a37ee2c1c9e
Binary files /dev/null and b/icons/dark-pasito.png differ
diff --git a/icons/dark-randomdrink.gif b/icons/dark-randomdrink.gif
new file mode 100644
index 0000000000000000000000000000000000000000..672dec4fcf49d80d2a40002036664e511474af4f
Binary files /dev/null and b/icons/dark-randomdrink.gif differ
diff --git a/icons/dark-redcreamingsoda.png b/icons/dark-redcreamingsoda.png
new file mode 100644
index 0000000000000000000000000000000000000000..40b862ffbef3b3c13ae969987d5f2920df55ffac
Binary files /dev/null and b/icons/dark-redcreamingsoda.png differ
diff --git a/icons/dark-sprite.png b/icons/dark-sprite.png
new file mode 100644
index 0000000000000000000000000000000000000000..efed205f6790036d237389ad0c25bf10b97b1b11
Binary files /dev/null and b/icons/dark-sprite.png differ
diff --git a/icons/dark-vanillacoke.png b/icons/dark-vanillacoke.png
new file mode 100644
index 0000000000000000000000000000000000000000..a94411da6c41267c88ca5b9ed5cb20bd79d6c813
Binary files /dev/null and b/icons/dark-vanillacoke.png differ
diff --git a/icons/dark-vblack.png b/icons/dark-vblack.png
new file mode 100644
index 0000000000000000000000000000000000000000..b428c2a4f0feba33cf0a82fefd8c03d034bf3a72
Binary files /dev/null and b/icons/dark-vblack.png differ
diff --git a/icons/dark-vgreen.png b/icons/dark-vgreen.png
new file mode 100644
index 0000000000000000000000000000000000000000..c0c0a00c2d50dca4265560c456ce418a6c9fdda3
Binary files /dev/null and b/icons/dark-vgreen.png differ
diff --git a/icons/default.png b/icons/default.png
new file mode 100644
index 0000000000000000000000000000000000000000..10c7d114c5e055453b5eb3abe23c44f83856f534
Binary files /dev/null and b/icons/default.png differ
diff --git a/icons/default.png.white b/icons/default.png.white
new file mode 100644
index 0000000000000000000000000000000000000000..031eca9c77b91d71def6edb7e8a0563888a45e1b
Binary files /dev/null and b/icons/default.png.white differ
diff --git a/icons/dietcoke.png b/icons/dietcoke.png
new file mode 100644
index 0000000000000000000000000000000000000000..2638c6bf2599d2cdc6bf8de1f0fecb38cbb1915d
Binary files /dev/null and b/icons/dietcoke.png differ
diff --git a/icons/gingerbeer.png b/icons/gingerbeer.png
new file mode 100644
index 0000000000000000000000000000000000000000..15c7f6ca93d13b3004016dd29e9ae7d1450f7e8d
Binary files /dev/null and b/icons/gingerbeer.png differ
diff --git a/icons/greensolo.png b/icons/greensolo.png
new file mode 100644
index 0000000000000000000000000000000000000000..678e8a307cadf7862d86c85b7bfe498d89cfb4dd
Binary files /dev/null and b/icons/greensolo.png differ
diff --git a/icons/lemonfoo.png b/icons/lemonfoo.png
new file mode 100644
index 0000000000000000000000000000000000000000..9a8d7e5dd116ff47493852515dc628da304c3a73
Binary files /dev/null and b/icons/lemonfoo.png differ
diff --git a/icons/orangefoo.png b/icons/orangefoo.png
new file mode 100644
index 0000000000000000000000000000000000000000..639ebd6923349f07ce0f1002fa8b96ced5dcbb59
Binary files /dev/null and b/icons/orangefoo.png differ
diff --git a/icons/pasito.png b/icons/pasito.png
new file mode 100644
index 0000000000000000000000000000000000000000..11efa5e90aa8f27989bd68990fa16a4fc4f14935
Binary files /dev/null and b/icons/pasito.png differ
diff --git a/icons/randomdrink.gif b/icons/randomdrink.gif
new file mode 100644
index 0000000000000000000000000000000000000000..e9837248966af4367445fd1c4efc9f48974da380
Binary files /dev/null and b/icons/randomdrink.gif differ
diff --git a/icons/redcreamingsoda.png b/icons/redcreamingsoda.png
new file mode 100644
index 0000000000000000000000000000000000000000..975cab49c25553f8818cf80c1d6ca4d124877f19
Binary files /dev/null and b/icons/redcreamingsoda.png differ
diff --git a/icons/sprite.png b/icons/sprite.png
new file mode 100644
index 0000000000000000000000000000000000000000..4c0648b0aab8b0333732b82c9f7f5e5fe63f343c
Binary files /dev/null and b/icons/sprite.png differ
diff --git a/icons/vanillacoke.png b/icons/vanillacoke.png
new file mode 100644
index 0000000000000000000000000000000000000000..7567b55afb3ca171ce6e6b988e0e6704e3f1d772
Binary files /dev/null and b/icons/vanillacoke.png differ
diff --git a/icons/vblack.png b/icons/vblack.png
new file mode 100644
index 0000000000000000000000000000000000000000..9100cf3cb9c61a1142f195ee45a9893965c03314
Binary files /dev/null and b/icons/vblack.png differ
diff --git a/icons/vgreen.png b/icons/vgreen.png
new file mode 100644
index 0000000000000000000000000000000000000000..01ddf0bc06232ded738f3cf04c42ff0b07025ba2
Binary files /dev/null and b/icons/vgreen.png differ
diff --git a/icons/white50alpha.png b/icons/white50alpha.png
new file mode 100644
index 0000000000000000000000000000000000000000..e8e6bc4e3b20bf2f6808d0b9a49625c6ae29045f
Binary files /dev/null and b/icons/white50alpha.png differ
diff --git a/index.html b/index.html
index 9c5304e1af76040f4c884770eb5f60e1eb4209de..201bc857668f3f05d1624c64b4797ea0353f7faa 100644
--- a/index.html
+++ b/index.html
@@ -3,109 +3,174 @@
 <head>
 <title>Dispense @ UCC</title>
 <link rel="stylesheet" type="text/css" href="dispense.css" />
+<link rel="apple-touch-icon" href="/dispense/apple-touch-icon.png" />
 <meta name="viewport" content="initial-scale = 1.0, user-scalable = no, width=device-width">
 <meta name="apple-mobile-web-app-capable" content="yes" />
 <meta name="apple-mobile-web-app-status-bar-style" content="hidden" />
 <script type="text/javascript" src="jquery.js"></script>
 <script type="text/javascript">
-drink_icons = {	"Sunkist" : "sunkist.png",
-		"lemony fizz"	: "lemonade.png",
+drink_icons = {	"coke" : "coke.png",
+		"fanta" : "fanta.png",
+		"diet coke" : "dietcoke.png",
+		"diet_coke" : "dietcoke.png",
+		"green solo" : "greensolo.png",
+		"green_solo" : "greensolo.png",
+		"ginger beer" : "gingerbeer.png",
+		"ginger_beer" : "gingerbeer.png",
 		"han solo" : "solo.png",
+		"kolebeer" : "kolebeer.png",
+              "lemonade" : "lemonade.png",
+		"lemon foo" : "lemonfoo.png",
+		"lemon_foo" : "lemonfoo.png",
+		"lemon lime solo" : "greensolo.png",
 		"lemony aide" : "lemonade.png",
-		"coke" : "coke.png",
+		"lemony fizz"	: "lemonade.png",
+		"lift" : "lemonfoo.png",
+		"mountain dew" : "mountaindew.png",
+		"mountain_dew" : "mountaindew.png",		
+		"null coke" : "cokezero.png",
+              "null_coke" : "cokezero.png",
+		"orange foo" : "orangefoo.png",
+		"orange_foo" : "orangefoo.png",
+		"pasito" : "pasito.png",
+		"pepsi" : "pepsi.png",
 		"pepsi max" : "pepsimax.png",
-		"null coke" : "cokezero.png"
+		"red creaming sod" : "redcreamingsoda.png",
+		"red creaming soda" : "redcreamingsoda.png",
+		"red_creaming_sod" : "redcreamingsoda.png",
+		"random" : "randomdrink.gif",
+		"random 200ml" : "randomdrink.gif",
+              "solo" : "solo.png",
+              "sprite" : "sprite.png",
+              "sunkist" : "sunkist.png",
+		"Sunkist" : "sunkist.png",
+		"vanilla coke" : "vanillacoke.png",
+		"vanilla_coke" : "vanillacoke.png",
+		"v black" : "vblack.png",
+		"v green" : "vgreen.png"
 };
+
 function dispense (slot) {
-	$("#slot" + slot).css ("background", "no-repeat url('loading.gif') center center");
-	$.getJSON ("dispense.py?slot=" + slot, {}, function (data) {
-		result = data["result"];
+	function DispenseResult (data) {
+		var result = data["result"];
 		if (result != "success") {
 			alert ("Error: " + result);
 		} else {
 			alert ("Dispense successful!");
 		}
 		updateStatus ();
-	});
+	}
+	$("#slot" + slot).css ("background", "no-repeat url('loading.gif') center center");
+	$.post ("dispense.py", { "username" : escape (username) , "sid" : escape (sid) , "slot" : slot }, DispenseResult, 'json');
 }
 
-// Loads machine status, then displays it.
-function updateStatus () {
-	$.getJSON ("query.py", {}, function (data) {
-	$.getJSON ("balance.py", {}, function (baldata) {
-		result = baldata["result"];
+function opendoor() {
+	function DoorResult (data) {
+		var result = data["result"];
 		if (result != "success") {
 			alert ("Error: " + result);
-			$('#login').css ("background", "no-repeat url('login.png')");
-			return;
+		} else {
+			alert ("Door open!");
 		}
-		window.balance = baldata["balance"];
-		showScreen ("main_menu");
-		$("#lcdusername").text (window.username);
-		$("#lcdbalance").text (window.balance);
-		for (i=0;i<=6;i++) {
-			drink = data["slot" + i][0];
-			available = "";
-			if (!data["slot" + i][2]) available = "dark-";
-			if (drink in drink_icons) icon = available + drink_icons[drink];
-			else icon = available + "default.png";
-			$("#slot" + i).html (data["slot" + i][1] + "c");
-			$("#slot" + i).css ("background", "no-repeat url('icons/" + icon + "') center center");
+		updateStatus ();
+	}
+	$.post ("door.py", { "username" : escape (username) , "sid" : escape (sid) }, DoorResult, 'json');
+}
+
+// Loads machine status, then displays it.
+function updateStatus () {
+	function GotDrinks (data) {
+		function GotBalance (baldata) {
+			var result = baldata["result"];
+			if (result != "success") {
+				alert ("Error: " + result);
+				$('#login').css ("background", "no-repeat url('login.png')");
+				logout();
+				return;
+			}
+			window.balance = baldata["balance"];
+			showScreen ("main_menu");
+			$("#lcdusername").text (window.username);
+			$("#lcdbalance").text (window.balance);
+			for (i=0;i<=6;i++) {
+				var drink = data["slot" + i][0].toLowerCase();
+				var available = "";
+				if (drink in drink_icons) {
+					var strSize = (drink.indexOf("200ml") == -1)?"":"200mL "
+					if (data["slot" + i][2] == "true") {
+						$("#slot" + i).html ("<FONT style=\"BACKGROUND-COLOR: rgba(0, 0, 0, 0.5)\">" + strSize + data["slot" + i][1] + "c </FONT>");
+					} else {
+						available = "dark-";
+						$("#slot" + i).html ("<FONT style=\"BACKGROUND-COLOR: rgba(0, 0, 0, 0.5)\">SLD " + strSize + data["slot" + i][1] + "c </FONT>");
+					}
+					icon = available + drink_icons[drink];
+				} else {
+					if (data["slot" + i][2] == "true") {
+						$("#slot" + i).html (drink + ": " + data["slot" + i][1] + "c");
+					} else {
+						$("#slot" + i).html ("SLD " + drink + ": " + data["slot" + i][1] + "c");
+					}
+					icon = "default.png";
+				}
+				//$("#slot" + i).html (data["slot" + i][1] + "c");
+				$("#slot" + i).css ("background", "no-repeat url('icons/" + icon + "') center center");
+			}
+			$('#login').css ("background", "no-repeat url('login.png')");
 		}
-		$('#login').css ("background", "no-repeat url('login.png')");
-	});
-	});
+		$.post ("balance.py", { "username" : escape (username) , "sid" : escape (sid) }, GotBalance, 'json');
+	}
+	$.getJSON ("query.py", {}, GotDrinks);
 }
 
 function load () {
-	window.username = getCookie ("username");
-	window.pin = getCookie ("pin");
-	$('#username').val (window.username);
-	$('#pin').val (window.pin);
+	$('#username').val ("");
+	$('#passwd').val ("");
 	window.current_screen = "login_screen";
-	if (window.username != "" && window.pin != ""){
-		$('#login').css ("background", "no-repeat url('loading.gif') center center");
-		updateStatus ();
-	}
 }
+
 function login () {
 	$('#login').css ("background", "no-repeat url('loading.gif') center center");
 	window.username = $("#username").val ();
-	window.pin = $("#pin").val ();
-	document.cookie = "username=" + escape (username) + ";";
-	document.cookie = "pin=" + escape (pin) + ";";
-	updateStatus ();
+	function LoginResult (data) {
+		if (data["result"] != "success") {
+			alert ("Login Error: " + data["result"]);
+		} else {
+			window.sid = data["sid"];
+			updateStatus ();
+		}
+	}
+	$.post ("login.py", { "username" : escape (username) , "passwd" : $('#passwd').val () }, LoginResult, 'json');
+	$('#passwd').val ("");
+	$('#login').css ("background", "no-repeat url('login.png')");
 }
+
 function logout () {
-	document.cookie = "username=";
-	document.cookie = "pin=";
+	function LogoutResult (data) {
+		if (data["result"] != "success") {
+			alert ("Error: " + data["result"]);
+		}
+	}
+	$.post ("logout.py", { "username" : escape (username) , "sid" : escape (sid) }, LogoutResult, 'json');
+	window.sid = "";
+	$('#username').val (window.username);
+	$('#passwd').val ("");
 	showScreen ("login_screen");
 }
+
 function showScreen (name) {
 	$("#" + window.current_screen).css ("display","none");
         $("#" + name).css ("display", "block");
 	window.current_screen = name;
 }
-function getCookie(c_name) {
-	if (document.cookie.length>0) {
-		c_start=document.cookie.indexOf(c_name + "=");
-		if (c_start!=-1) {
-			c_start=c_start + c_name.length+1;
-			c_end=document.cookie.indexOf(";",c_start);
-			if (c_end==-1) c_end=document.cookie.length;
-			return unescape(document.cookie.substring(c_start,c_end));
-		}
-	}
-	return "";
-}
+
 </script>
 </head>
 <body onload="load();">
 <div class="screen" id="login_screen">
 UCC Account Name:<br />
-<input type="text" id="username" /><br />
-Dispense PIN:<br />
-<input type="password" id="pin" />
+<input type="text" name="username" id="username" onkeypress="{if (event.keyCode==13)login()}"/><br />
+Password:<br />
+<input type="password" name="passwd" id="passwd" onkeypress="{if (event.keyCode==13)login()}"/>
 <div id="login" onclick="login();"></div>
 </div>
 <div class="screen" id="main_menu">
@@ -122,3 +187,4 @@ Dispense PIN:<br />
 </div>
 </body>
 </html>
+
diff --git a/login.py b/login.py
new file mode 100755
index 0000000000000000000000000000000000000000..830f41d4ec37f4d337b53e647374e8850fed933b
--- /dev/null
+++ b/login.py
@@ -0,0 +1,54 @@
+#!/usr/bin/python
+
+import commands,re
+
+print "Content-type: text/json"
+print
+import cgi,cgitb
+cgitb.enable ()
+import os
+import ldap
+import string, random
+import sys, time, syslog
+
+#import getpass
+
+result = {}
+form = cgi.FieldStorage ()
+
+def sid_generator(size=64, chars=string.ascii_uppercase + string.ascii_lowercase + string.digits):
+	return ''.join(random.choice(chars) for c in range(size))
+
+def checkdata ():
+	expr_name = re.compile ("^\w*$")	
+	username = form["username"].value.lower()
+	if (not expr_name.match (username)):
+		return {"result" : "Invalid login!"}
+	passwd = form["passwd"].value
+	#dn = "uid=%s,ou=People,dc=ucc,dc=gu,dc=uwa,dc=edu,dc=au" % username
+	dn = "cn=%s,cn=Users,dc=ad,dc=ucc,dc=gu,dc=uwa,dc=edu,dc=au" % username
+	try:
+		ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT,ldap.OPT_X_TLS_NEVER)
+		l = ldap.initialize("ldaps://ad.ucc.gu.uwa.edu.au")
+		l.simple_bind_s(dn, passwd)
+	except:
+		log = "Authentication failure for user " + username + " from " + cgi.escape(os.environ["REMOTE_ADDR"]) + "\n"
+                syslog.syslog((syslog.LOG_NOTICE | syslog.LOG_AUTH), log)
+                #logfile = open("auth.log", "a")
+                #logfile.write(log)
+                #logfile.close()
+		return {"result" : "Authentication Failed"}
+	file = open("sessionids", "r")
+	output = []
+	for line in file:
+		if not (":" + username + ":") in line:
+			output.append(line)
+	file.close()
+	sid = sid_generator()
+	output.append(str(int(time.time())) + ":" + username + ":" + sid + "\n")
+	file = open("sessionids", "w")
+	file.writelines(output)
+	file.close()
+	return {"result" : "success", "sid" : str(sid)}
+
+print str(checkdata ())
diff --git a/logout.py b/logout.py
new file mode 100755
index 0000000000000000000000000000000000000000..62c1da1b288912c7a8ee68fad3e8ede406f4c393
--- /dev/null
+++ b/logout.py
@@ -0,0 +1,47 @@
+#!/usr/bin/python
+
+import commands,re
+
+print "Content-type: text/json"
+print
+import cgi,cgitb
+cgitb.enable ()
+import os
+import ldap
+import sys
+
+#import getpass
+ 
+result = {}
+form = cgi.FieldStorage ()
+
+def checkdata ():
+	if not (form.has_key("username") and form.has_key("sid")):
+        	return {"result" : "Missing information!"}
+        expr_name = re.compile ("^\w*$")
+        username = form["username"].value.lower ()
+        sid = form["sid"].value
+        if (not expr_name.match (username)):
+                return {"result" : "Invalid login!"}
+	file = open("sessionids", "r")
+	output = []
+	success = False
+	for line in file:
+		if (":" + username + ":" + sid + "\n") in line:
+			success = True
+		else:
+			output.append(line)
+	file.close()
+	if not success:
+		log = "Authentication failure for user " + username + " from " + cgi.escape(os.environ["REMOTE_ADDR"]) + "\n"
+                syslog.syslog((syslog.LOG_NOTICE | syslog.LOG_AUTH), log)
+                #logfile = open("auth.log", "a")
+                #logfile.write(log)
+                #logfile.close()
+                return {"result" : "Authentication Failed"}
+	file = open("sessionids", "w")
+	file.writelines(output)
+	file.close()
+	return {"result" : "success"}
+
+print str(checkdata ())
diff --git a/purge.py b/purge.py
new file mode 100755
index 0000000000000000000000000000000000000000..d1e13010260f16334bc187a805377e09b534fe37
--- /dev/null
+++ b/purge.py
@@ -0,0 +1,15 @@
+#!/usr/bin/python
+
+import sys, time
+
+ts = int(time.time()) - 1200
+file = open("sessionids", "r")
+output = []
+for line in file:
+	fields = line.split(':')
+	if int(fields[0]) > ts:
+		output.append(line)
+file.close()
+file = open("sessionids", "w")
+file.writelines(output)
+file.close()
diff --git a/query.py b/query.py
index f8ddb3a40cc1817502623ad916dcae1d1bdc3915..305515db1e867f562dc0ea4e63d4d44dbcd0a259 100755
--- a/query.py
+++ b/query.py
@@ -1,6 +1,5 @@
 #!/usr/bin/python
-import re
-import urllib
+import commands,re
 
 import cgi, cgitb
 cgitb.enable ()
@@ -8,16 +7,16 @@ cgitb.enable ()
 print "Content-type: text/json"
 print
 
-site = urllib.urlopen("http://ucc.asn.au/cgi-bin/dispense?html.menu")
-lines = site.readlines ()[15:22]
+output = commands.getoutput("dispense finger")
+lines = output.split("\n")[2:9]
 
-
-expr = re.compile ("<STRONG>(?P<price>\d*)c</STRONG>\s*(<A[^>]*>|)(?P<name>[^<]*)(</A\>|)[^>]*>(?P<available>[^<]*)")
+expr = re.compile("^(\d) - (\w+) +(\d+) (.*)")
 
 drinks = {}
 for i, line in enumerate (lines):
+#	print line
 	r = expr.search (line).groups ()
 	available = "true"
-	if r[4] == "empty": available = "false"
-	drinks["slot" + str(i)] = [r[2].rstrip(), r[0], available]
+	if r[1] != "Avail": available = "false"
+	drinks["slot" + str(i)] = [r[3].rstrip(), r[2], available]
 print str (drinks)