diff --git a/RunServerTest b/RunServerTest
index 99661763ad292bc23cad04d6e1a649ecab6f80f3..0029d1aad1b33d4ad74c24d1ed72e40b14bc6e3b 100755
--- a/RunServerTest
+++ b/RunServerTest
@@ -3,7 +3,7 @@
 ARGS="--itemsfile items.cfg -p 11020"
 ARGS=$ARGS" --cokeport /dev/ttyUSB0"
 
-if [ "x$1" != "x" ]; then 
+if [ "x$1" == "xdbg" ]; then 
 	LD_LIBRARY_PATH=. gdb --args ./dispsrv $ARGS
 else
 	LD_LIBRARY_PATH=. ./dispsrv $ARGS
diff --git a/src/client/main.c b/src/client/main.c
index 361d62dbe27fce61d5c89670197eccbc42276add..3dd5e2f6b06328b3f1d46e8e09f6069a414fabc0 100644
--- a/src/client/main.c
+++ b/src/client/main.c
@@ -38,8 +38,12 @@ typedef struct sItem {
 void	PrintAlign(int Row, int Col, int Width, const char *Left, char Pad1, const char *Mid, char Pad2, const char *Right, ...);
 
  int	sendf(int Socket, const char *Format, ...);
+
  int	OpenConnection(const char *Host, int Port);
  int	Authenticate(int Socket);
+void	PopulateItemList(int Socket);
+ int	DispenseItem(int Socket, int ItemID);
+
 char	*trim(char *string);
  int	RunRegex(regex_t *regex, const char *string, int nMatches, regmatch_t *matches, const char *errorMessage);
 void	CompileRegex(regex_t *regex, const char *pattern, int flags);
@@ -51,18 +55,20 @@ tItem	*gaItems;
  int	giNumItems;
 regex_t	gArrayRegex, gItemRegex, gSaltRegex;
 
+char	*gsOverrideUser;
+
 // === CODE ===
 int main(int argc, char *argv[])
 {
 	 int	sock;
-	 int	i, responseCode, len;
+	 int	i;
 	char	buffer[BUFSIZ];
 	
 	// -- Create regular expressions
 	// > Code Type Count ...
 	CompileRegex(&gArrayRegex, "^([0-9]{3})\\s+([A-Za-z]+)\\s+([0-9]+)", REG_EXTENDED);	//
 	// > Code Type Ident Price Desc
-	CompileRegex(&gItemRegex, "^([0-9]{3})\\s+(.+?)\\s+(.+?)\\s+([0-9]+)\\s+(.+)$", REG_EXTENDED);
+	CompileRegex(&gItemRegex, "^([0-9]{3})\\s+([A-Za-z]+)\\s+([A-Za-z0-9:]+?)\\s+([0-9]+)\\s+(.+)$", REG_EXTENDED);
 	// > Code 'SALT' salt
 	CompileRegex(&gSaltRegex, "^([0-9]{3})\\s+(.+)\\s+(.+)$", REG_EXTENDED);
 	
@@ -70,165 +76,73 @@ int main(int argc, char *argv[])
 	sock = OpenConnection(gsDispenseServer, giDispensePort);
 	if( sock < 0 )	return -1;
 
-	// Determine what to do
-	if( argc > 1 )
+	// Authenticate
+	Authenticate(sock);
+
+	// Parse Arguments
+	for( i = 1; i < argc; i ++ )
 	{
-		if( strcmp(argv[1], "acct") == 0 )
-		{
+		char	*arg = argv[i];
+		
+		if( arg[0] == '-' ) {
+					
+			switch(arg[1])
+			{
+			case 'u':	// Override User
+				gsOverrideUser = argv[++i];
+				break;
+			}
+
+			continue;
+		}
+		if( strcmp(argv[1], "acct") == 0 ) {
 			// Alter account
 			// List accounts
 			return 0;
 		}
-	}
-
-	// Ask server for stock list
-	send(sock, "ENUM_ITEMS\n", 11, 0);
-	len = recv(sock, buffer, BUFSIZ-1, 0);
-	buffer[len] = '\0';
-	
-	trim(buffer);
-	
-	printf("Output: %s\n", buffer);
-	
-	responseCode = atoi(buffer);
-	if( responseCode != 201 )
-	{
-		fprintf(stderr, "Unknown response from dispense server (Response Code %i)\n", responseCode);
-		return -1;
-	}
-	
-	// Get item list
-	{
-		char	*itemType, *itemStart;
-		 int	count;
-		regmatch_t	matches[4];
-		
-		// Expected format: 201 Items <count> <item1> <item2> ...
-		RunRegex(&gArrayRegex, buffer, 4, matches, "Malformed server response");
-		
-		itemType = &buffer[ matches[2].rm_so ];	buffer[ matches[2].rm_eo ] = '\0';
-		count = atoi( &buffer[ matches[3].rm_so ] );
-		
-		// Check array type
-		if( strcmp(itemType, "Items") != 0 ) {
-			// What the?!
-			fprintf(stderr, "Unexpected array type, expected 'Items', got '%s'\n",
-				itemType);
-			return -1;
-		}
-		
-		itemStart = &buffer[ matches[3].rm_eo ];
-		
-		gaItems = malloc( count * sizeof(tItem) );
-		
-		for( giNumItems = 0; giNumItems < count && itemStart; giNumItems ++ )
-		{
-			char	*next = strchr( ++itemStart, ' ' );
-			if( next )	*next = '\0';
-			gaItems[giNumItems].Ident = strdup(itemStart);
-			itemStart = next;
-		}
-	}
-	
-	// Get item information
-	for( i = 0; i < giNumItems; i ++ )
-	{
-		regmatch_t	matches[6];
-		
-		// Print item Ident
-		printf("%2i %s\t", i, gaItems[i].Ident);
-		
-		// Get item info
-		sendf(sock, "ITEM_INFO %s\n", gaItems[i].Ident);
-		len = recv(sock, buffer, BUFSIZ-1, 0);
-		buffer[len] = '\0';
-		trim(buffer);
-		
-		responseCode = atoi(buffer);
-		if( responseCode != 202 ) {
-			fprintf(stderr, "Unknown response from dispense server (Response Code %i)\n", responseCode);
-			return -1;
+		else {
+			// Item name / pattern
 		}
-		
-		RunRegex(&gItemRegex, buffer, 6, matches, "Malformed server response");
-		
-		buffer[ matches[3].rm_eo ] = '\0';
-		
-		gaItems[i].Price = atoi( buffer + matches[4].rm_so );
-		gaItems[i].Desc = strdup( buffer + matches[5].rm_so );
-		
-		printf("%3i %s\n", gaItems[i].Price, gaItems[i].Desc);
 	}
-	
-	// and choose what to dispense
+
+	// Get items
+	PopulateItemList(sock);
 	
 	#if USE_NCURSES_INTERFACE
-	i = ShowNCursesUI();
+		i = ShowNCursesUI();
 	#else
-	
-	for(;;)
-	{
-		char	*buf;
-		
-		fgets(buffer, BUFSIZ, stdin);
-		
-		buf = trim(buffer);
-		
-		if( buf[0] == 'q' )	break;
-		
-		i = atoi(buf);
-		
-		printf("buf = '%s', atoi(buf) = %i\n", buf, i);
-		
-		if( i != 0 || buf[0] == '0' )
+		for( i = 0; i < giNumItems; i ++ ) {		
+			printf("%2i %s\t%3i %s\n", i, gaItems[i].Ident, gaItems[i].Price, gaItems[i].Desc);
+		}
+		printf(" q Quit\n");
+		for(;;)
 		{
-			printf("i = %i\n", i);
+			char	*buf;
+			
+			i = -1;
+			
+			fgets(buffer, BUFSIZ, stdin);
+			
+			buf = trim(buffer);
 			
-			if( i < 0 || i >= giNumItems ) {
-				printf("Bad item (should be between 0 and %i)\n", giNumItems);
-				continue;
+			if( buf[0] == 'q' )	break;
+			
+			i = atoi(buf);
+			
+			if( i != 0 || buf[0] == '0' )
+			{
+				if( i < 0 || i >= giNumItems ) {
+					printf("Bad item %i (should be between 0 and %i)\n", i, giNumItems);
+					continue;
+				}
+				break;
 			}
-			break;
 		}
-	}
 	#endif
 	
-	// Check for a valid item ID and if so, authenticate
-	if( i >= 0 && Authenticate(sock) )
-	{	
-		// Dispense!
-		sendf(sock, "DISPENSE %s\n", gaItems[i].Ident);
-		
-		len = recv(sock, buffer, BUFSIZ-1, 0);
-		buffer[len] = '\0';
-		trim(buffer);
-		
-		responseCode = atoi(buffer);
-		switch( responseCode )
-		{
-		case 200:
-			printf("Dispense OK\n");
-			break;
-		case 401:
-			printf("Not authenticated\n");
-			break;
-		case 402:
-			printf("Insufficient balance\n");
-			break;
-		case 406:
-			printf("Bad item name, bug report\n");
-			break;
-		case 500:
-			printf("Item failed to dispense, is the slot empty?\n");
-			break;
-		case 501:
-			printf("Dispense not possible (slot empty/permissions)\n");
-			break;
-		default:
-			printf("Unknown response code %i ('%s')\n", responseCode, buffer);
-			break;
-		}
-	}
+	// Check for a valid item ID
+	if( i >= 0 )
+		DispenseItem(sock, i);
 
 	close(sock);
 
@@ -269,7 +183,7 @@ int ShowNCursesUI(void)
 	 int	ch;
 	 int	i, times;
 	 int	xBase, yBase;
-	const int	displayMinWidth = 34;
+	const int	displayMinWidth = 40;
 	const int	displayMinItems = 8;
 	char	*titleString = "Dispense";
 	 int	itemCount = displayMinItems;
@@ -606,6 +520,125 @@ int Authenticate(int Socket)
 	return 0;	// Seems OK
 }
 
+void PopulateItemList(int Socket)
+{
+	char	buffer[BUFSIZ];
+	 int	len;
+	 int	responseCode;
+	
+	char	*itemType, *itemStart;
+	 int	count, i;
+	regmatch_t	matches[4];
+	
+	// Ask server for stock list
+	send(Socket, "ENUM_ITEMS\n", 11, 0);
+	len = recv(Socket, buffer, BUFSIZ-1, 0);
+	buffer[len] = '\0';
+	
+	trim(buffer);
+	
+	//printf("Output: %s\n", buffer);
+	
+	responseCode = atoi(buffer);
+	if( responseCode != 201 ) {
+		fprintf(stderr, "Unknown response from dispense server (Response Code %i)\n", responseCode);
+		exit(-1);
+	}
+	
+	// - Get item list -
+	
+	// Expected format: 201 Items <count> <item1> <item2> ...
+	RunRegex(&gArrayRegex, buffer, 4, matches, "Malformed server response");
+		
+	itemType = &buffer[ matches[2].rm_so ];	buffer[ matches[2].rm_eo ] = '\0';
+	count = atoi( &buffer[ matches[3].rm_so ] );
+		
+	// Check array type
+	if( strcmp(itemType, "Items") != 0 ) {
+		// What the?!
+		fprintf(stderr, "Unexpected array type, expected 'Items', got '%s'\n",
+			itemType);
+		exit(-1);
+	}
+		
+	itemStart = &buffer[ matches[3].rm_eo ];
+		
+	gaItems = malloc( count * sizeof(tItem) );
+		
+	for( giNumItems = 0; giNumItems < count && itemStart; giNumItems ++ )
+	{
+		char	*next = strchr( ++itemStart, ' ' );
+		if( next )	*next = '\0';
+		gaItems[giNumItems].Ident = strdup(itemStart);
+		itemStart = next;
+	}
+	
+	// Fetch item information
+	for( i = 0; i < giNumItems; i ++ )
+	{
+		regmatch_t	matches[6];
+		
+		// Get item info
+		sendf(Socket, "ITEM_INFO %s\n", gaItems[i].Ident);
+		len = recv(Socket, buffer, BUFSIZ-1, 0);
+		buffer[len] = '\0';
+		trim(buffer);
+		
+		responseCode = atoi(buffer);
+		if( responseCode != 202 ) {
+			fprintf(stderr, "Unknown response from dispense server (Response Code %i)\n", responseCode);
+			exit(-1);
+		}
+		
+		RunRegex(&gItemRegex, buffer, 6, matches, "Malformed server response");
+		
+		buffer[ matches[3].rm_eo ] = '\0';
+		
+		gaItems[i].Price = atoi( buffer + matches[4].rm_so );
+		gaItems[i].Desc = strdup( buffer + matches[5].rm_so );
+	}
+}
+
+int DispenseItem(int Socket, int ItemID)
+{
+	 int	len, responseCode;
+	char	buffer[BUFSIZ];
+	
+	if( ItemID < 0 || ItemID > giNumItems )	return -1;
+	
+	// Dispense!
+	sendf(Socket, "DISPENSE %s\n", gaItems[ItemID].Ident);
+	len = recv(Socket, buffer, BUFSIZ-1, 0);
+	buffer[len] = '\0';
+	trim(buffer);
+	
+	responseCode = atoi(buffer);
+	switch( responseCode )
+	{
+	case 200:
+		printf("Dispense OK\n");
+		return 0;
+	case 401:
+		printf("Not authenticated\n");
+		return 1;
+	case 402:
+		printf("Insufficient balance\n");
+		return 1;
+	case 406:
+		printf("Bad item name, bug report\n");
+		return 1;
+	case 500:
+		printf("Item failed to dispense, is the slot empty?\n");
+		return 1;
+	case 501:
+		printf("Dispense not possible (slot empty/permissions)\n");
+		return 1;
+	default:
+		printf("Unknown response code %i ('%s')\n", responseCode, buffer);
+		return -2;
+	}
+}
+
 char *trim(char *string)
 {
 	 int	i;
diff --git a/src/cokebank_basic/main.c b/src/cokebank_basic/main.c
index ae111d4d88ecd2e226f6c118705247fc5771ecde..e635b916ed2b4e08fbfc00eef8c7ad50955e233e 100644
--- a/src/cokebank_basic/main.c
+++ b/src/cokebank_basic/main.c
@@ -33,6 +33,9 @@ char	*GetUserName(int User);
  int	GetUserID(const char *Username); 
  int	GetUserAuth(const char *Username, const char *Password);
 
+// === GLOBALS ===
+FILE	*gBank_LogFile;
+
 // === CODE ===
 /**
  * \brief Load the cokebank database
@@ -47,6 +50,9 @@ void Init_Cokebank(const char *Argument)
 		perror("Opening coke bank");
 	}
 
+	gBank_LogFile = fopen("cokebank.log", "a");
+	if( !gBank_LogFile )	gBank_LogFile = stdout;
+
 	fseek(gBank_File, 0, SEEK_END);
 	giBank_NumUsers = ftell(gBank_File) / sizeof(gaBank_Users[0]);
 	fseek(gBank_File, 0, SEEK_SET);
@@ -64,12 +70,16 @@ void Init_Cokebank(const char *Argument)
  */
 int Transfer(int SourceUser, int DestUser, int Ammount, const char *Reason)
 {
-	if( Bank_GetUserBalance(SourceUser) - Ammount < Bank_GetMinAllowedBalance(SourceUser) )
+	 int	srcBal = Bank_GetUserBalance(SourceUser);
+	 int	dstBal = Bank_GetUserBalance(DestUser);
+	if( srcBal - Ammount < Bank_GetMinAllowedBalance(SourceUser) )
 		return 1;
-	if( Bank_GetUserBalance(DestUser) + Ammount < Bank_GetMinAllowedBalance(DestUser) )
+	if( dstBal + Ammount < Bank_GetMinAllowedBalance(DestUser) )
 		return 1;
 	Bank_AlterUserBalance(DestUser, Ammount);
 	Bank_AlterUserBalance(SourceUser, -Ammount);
+	fprintf(gBank_LogFile, "ACCT #%i{%i} -= %ic [to #%i] (%s)\n", SourceUser, srcBal, Ammount, DestUser, Reason);
+	fprintf(gBank_LogFile, "ACCT #%i{%i} += %ic [from #%i] (%s)\n", DestUser, dstBal, Ammount, SourceUser, Reason);
 	return 0;
 }
 
diff --git a/src/server/common.h b/src/server/common.h
index 4fad81cb735ad2992e4e75efc1862d5a0abb38dc..2704696e8bd83d2612874ca1cc4c1554a2c49783 100644
--- a/src/server/common.h
+++ b/src/server/common.h
@@ -72,6 +72,7 @@ extern int	giDebugLevel;
 extern void	CompileRegex(regex_t *Regex, const char *Pattern, int Flags);
 extern int	RunRegex(regex_t *regex, const char *string, int nMatches, regmatch_t *matches, const char *errorMessage);
 extern int	InitSerial(const char *Path, int BaudRate);
+extern char	*mkstr(const char *Format, ...);
 
 // --- Dispense ---
 extern int	DispenseItem(int User, tItem *Item);
diff --git a/src/server/dispense.c b/src/server/dispense.c
index 09b2c4fdb60409d67e2055b0c691a377ee3577ad..ae6d42ed3b96653009ec6d95a36f07c83f03baa2 100644
--- a/src/server/dispense.c
+++ b/src/server/dispense.c
@@ -14,6 +14,7 @@ int DispenseItem(int User, tItem *Item)
 	 int	ret;
 	tHandler	*handler;
 	char	*username;
+	char	*reason;
 	
 	handler = Item->Handler;
 	
@@ -22,11 +23,12 @@ int DispenseItem(int User, tItem *Item)
 		ret = handler->CanDispense( User, Item->ID );
 		if(ret)	return 1;	// 1: Unable to dispense
 	}
-	
+
 	// Subtract the balance
-	ret = Transfer( User, GetUserID(">sales"), Item->Price, "" );
-	// What value should I use for this error?
-	// AlterBalance should return the final user balance
+	reason = mkstr("Dispense - %s:%i %s", handler->Name, Item->ID, Item->Name);
+	if( !reason )	reason = Item->Name;	// TODO: Should I instead return an error?
+	ret = Transfer( User, GetUserID(">sales"), Item->Price, reason);
+	free(reason);
 	if(ret)	return 2;	// 2: No balance
 	
 	// Get username for debugging
@@ -45,7 +47,7 @@ int DispenseItem(int User, tItem *Item)
 	}
 	
 	// And log that it happened
-	Log_Info("Dispensed %s (%i:%i) for %s [cost %i, balance %i cents]",
+	Log_Info("Dispensed %s (%s:%i) for %s [cost %i, balance %i cents]",
 		Item->Name, handler->Name, Item->ID,
 		username, Item->Price, GetBalance(User)
 		);
diff --git a/src/server/logging.c b/src/server/logging.c
index 51cc8d22c1b2d0a6d041a4292351e65ab9c90749..ac1e603334f196adad2271e34236acd764b6bba9 100644
--- a/src/server/logging.c
+++ b/src/server/logging.c
@@ -5,15 +5,29 @@
  */
 #include <stdlib.h>
 #include <stdio.h>
+#include <stdarg.h>
 #include "common.h"
 
 // === CODE ==
 void Log_Error(const char *Format, ...)
 {
-	
+	va_list	args;
+
+	va_start(args, Format);
+	fprintf(stderr, "Error: ");
+	vfprintf(stderr, Format, args);
+	fprintf(stderr, "\n");
+	va_end(args);
 }
 
 void Log_Info(const char *Format, ...)
 {
+	va_list	args;
+	
+	va_start(args, Format);
+	printf("Info : ");
+	vprintf(Format, args);
+	printf("\n");
+	va_end(args);
 }
 
diff --git a/src/server/main.c b/src/server/main.c
index b612f6fcc8864b52e5dbb7fd51795ded979ac5d1..8abb9847cc1c89a3863afab9ab11e6df6f4821e2 100644
--- a/src/server/main.c
+++ b/src/server/main.c
@@ -16,6 +16,7 @@
 #include <unistd.h>
 #include <sys/stat.h>
 #include <fcntl.h>
+#include <stdarg.h>
 
 // === IMPORTS ===
 extern void	Init_Cokebank(const char *Argument);	// cokebank.c
@@ -151,3 +152,26 @@ int InitSerial(const char *File, int BaudRate)
 }
 
 
+/**
+ * \brief Create a formatted heap string
+ */
+char *mkstr(const char *Format, ...)
+{
+	va_list	args;
+	 int	len;
+	char	*ret;
+
+	va_start(args, Format);
+	len = vsnprintf(NULL, 0, Format, args);
+	va_end(args);
+
+	ret = malloc( len + 1 );
+	if(!ret)	return NULL;
+
+	va_start(args, Format);
+	vsprintf(ret, Format, args);
+	va_end(args);
+	
+	return ret;
+}
+