diff --git a/itemsfile.txt b/itemsfile.txt
new file mode 100644
index 0000000000000000000000000000000000000000..69ee2d2eef429dad0f21ac9646b5ca6d6b4f6173
--- /dev/null
+++ b/itemsfile.txt
@@ -0,0 +1,24 @@
+== Item Database Format ==
+(Simple Version)
+
+Each line denotes a "slot"
+	<system>\t<code>\t<price>\t<description>
+Comments are denoted by '#' or ';' (with # usually being for info comments
+and ; for commenting out)
+comments are allowed anywhere on a line and act until the end of the line.
+
+
+For example, a coke could be
+	drink	06	96	Coke
+A pseudo-item could le
+	pseudo	01	10	Laserprint
+Or a snack
+	snack	64	128	Mars Bar
+
+The <system> denotes what controller plugin to use for the item.
+Currently there are only three:
+ "drink" is the drinks machine
+ "pseudo" is essentially a no-op, no action is taken on dispense (save
+ for logging)
+ "snack" controls the snack machine, dropping an item on dispense
+
diff --git a/server/src/cokebank.c b/server/src/cokebank.c
index bbf4244c914cb86fd7b332a8d9441f87c8688196..00d7d4f1d20def7c1e101fa97873f8548ac12139 100644
--- a/server/src/cokebank.c
+++ b/server/src/cokebank.c
@@ -58,6 +58,6 @@ char *GetUserName(int User)
  */
 int GetUserID(const char *Username)
 {
-	return 0;
+	return -1;
 }
 
diff --git a/server/src/common.h b/server/src/common.h
index f2976242f6d233683cf8730e026887dc464bfa23..a7e319df763118674551959f789cf892d91c195c 100644
--- a/server/src/common.h
+++ b/server/src/common.h
@@ -11,6 +11,7 @@
 
 // === CONSTANTS ===
 #define	DEFAULT_CONFIG_FILE	"/etc/opendispense/main.cfg"
+#define	DEFAULT_ITEM_FILE	"/etc/opendispense/items.cfg"
 
 // === HELPER MACROS ===
 #define _EXPSTR(x)	#x
diff --git a/server/src/dispense.c b/server/src/dispense.c
index ee07bb9383f09ce022faeab00f2efb9ade40e474..5552277b7c93d8fa239ff9cbacbdbb13c131e228 100644
--- a/server/src/dispense.c
+++ b/server/src/dispense.c
@@ -4,6 +4,11 @@
 #include <stdlib.h>
 
 // === CODE ===
+/**
+ * \brief Dispense an item for a user
+ * 
+ * The core of the dispense system, I kinda like it :)
+ */
 int DispenseItem(int User, int Item)
 {
 	 int	ret;
@@ -11,22 +16,28 @@ int DispenseItem(int User, int Item)
 	tHandler	*handler;
 	char	*username;
 	
+	// Sanity check please?
 	if(Item < 0 || Item >= giNumItems)
 		return -1;
 	
+	// Get item pointers
 	item = &gaItems[Item];
 	handler = &gaHandlers[ item->Type ];
 	
-	username = GetUserName(User);
-	
+	// Check if the dispense is possible
 	ret = handler->CanDispense( User, item->ID );
 	if(!ret)	return ret;
 	
+	// Subtract the balance
 	ret = AlterBalance( User, -item->Price );
 	// What value should I use for this error?
 	// AlterBalance should return the final user balance
 	if(ret == 0)	return 1;
 	
+	// Get username for debugging
+	username = GetUserName(User);
+	
+	// Actually do the dispense
 	ret = handler->DoDispense( User, item->ID );
 	if(ret) {
 		Log_Error("Dispense failed after deducting cost (%s dispensing %s - %ic)",
@@ -36,6 +47,7 @@ int DispenseItem(int User, int Item)
 		return 1;
 	}
 	
+	// And log that it happened
 	Log_Info("Dispensed %s (%i:%i) for %s [cost %i, balance %i cents]",
 		item->Name, item->Type, item->ID,
 		username, item->Price, GetBalance(User)
diff --git a/server/src/itemdb.c b/server/src/itemdb.c
index 116b047ee3eb96611fb9c9ba39056930dc987b69..cf8e7f1cbdcac91a5c197854c26f311f9013b32f 100644
--- a/server/src/itemdb.c
+++ b/server/src/itemdb.c
@@ -15,5 +15,49 @@
  int	giNumItems = 0;
 tItem	*gaItems = NULL;
 tHandler	*gaHandlers = NULL;
+char	*gsItemListFile = DEFAULT_ITEM_FILE;
 
 // === CODE ===
+/**
+ * \brief Read the item list from disk
+ */
+void Load_Itemlist(void)
+{
+	FILE	*fp = fopen(gsItemListFile, "r");
+	char	buffer[BUFSIZ];
+	char	*line;
+	
+	// Error check
+	if(!fp) {
+		fprintf(stderr, "Unable to open item file '%s'\n", gsItemListFile);
+		perror("Unable to open item file");
+	}
+	
+	while( fgets(buffer, BUFSIZ, fp) )
+	{
+		char	*tmp;
+		char	*type, *num, *price, *desc;
+		// Remove comments
+		tmp = strchr(buffer, '#');
+		if(tmp)	*tmp = '\0';
+		tmp = strchr(buffer, ';');
+		if(tmp)	*tmp = '\0';
+		
+		// Trim whitespace
+		line = trim(buffer);
+		
+		// Parse Line
+		// - Type
+		type = line;
+		// - Number
+		num = strchr(type, ' ');
+		if(num)		while(*num == ' ' || *num == '\t');
+		if(!num) {
+			fprintf(stderr, "Syntax error on line %i of item file\n", lineNum);
+			continue;
+		}
+		// - Price
+		price = strchr(num, ' ');
+	}
+	
+}
diff --git a/server/src/main.c b/server/src/main.c
index e99a7fc3e3a69c065786cba62b1abc87ed22573b..969ac439dd656429c6f60d2e52d0b0f652437b88 100644
--- a/server/src/main.c
+++ b/server/src/main.c
@@ -51,7 +51,7 @@ int main(int argc, char *argv[])
 	
 	Init_Cokebank();
 	
-	//Load_Itemlist();
+	Load_Itemlist();
 	
 	Server_Start();
 	
diff --git a/server/src/server.c b/server/src/server.c
index e7dbc7078f0513dba01ac022026e641d96287b68..7f0debbd2dd578235e2998ebcc2a9bb62bc3c5fa 100644
--- a/server/src/server.c
+++ b/server/src/server.c
@@ -328,7 +328,7 @@ char *Server_Cmd_AUTOAUTH(tClient *Client, char *Args)
 	
 	// Get UID
 	Client->UID = GetUserID( Args );
-	if( Client->UID <= 0 ) {
+	if( Client->UID < 0 ) {
 		if(giDebugLevel)
 			printf("Client %i: Unknown user '%s'\n", Client->ID, Args);
 		return strdup("401 Auth Failure\n");
@@ -374,3 +374,46 @@ void HexBin(uint8_t *Dest, char *Src, int BufSize)
 	for( ; i < BufSize; i++ )
 		Dest[i] = 0;
 }
+
+/**
+ * \brief Decode a Base64 value
+ */
+int UnBase64(uint8_t *Dest, char *Src, int BufSize)
+{
+	uint32_t	val;
+	 int	i, j;
+	char	*start_src = Src;
+	
+	for( i = 0; i+2 < BufSize; i += 3 )
+	{
+		val = 0;
+		for( j = 0; j < 4; j++, Src ++ ) {
+			if('A' <= *Src && *Src <= 'Z')
+				val |= (*Src - 'A') << ((3-j)*6);
+			else if('a' <= *Src && *Src <= 'z')
+				val |= (*Src - 'a' + 26) << ((3-j)*6);
+			else if('0' <= *Src && *Src <= '9')
+				val |= (*Src - '0' + 52) << ((3-j)*6);
+			else if(*Src == '+')
+				val |= 62 << ((3-j)*6);
+			else if(*Src == '/')
+				val |= 63 << ((3-j)*6);
+			else if(!*Src)
+				break;
+			else if(*Src != '=')
+				j --;	// Ignore invalid characters
+		}
+		Dest[i  ] = (val >> 16) & 0xFF;
+		Dest[i+1] = (val >> 8) & 0xFF;
+		Dest[i+2] = val & 0xFF;
+		if(j != 4)	break;
+	}
+	
+	// Finish things off
+	if(i   < BufSize)
+		Dest[i] = (val >> 16) & 0xFF;
+	if(i+1 < BufSize)
+		Dest[i+1] = (val >> 8) & 0xFF;
+	
+	return Src - start_src;
+}