diff --git a/SetupDatabase b/SetupDatabase
new file mode 100755
index 0000000000000000000000000000000000000000..0a8b5ab7d7dd2a872046e89596636f6ba6ae4e02
--- /dev/null
+++ b/SetupDatabase
@@ -0,0 +1,4 @@
+#!/bin/sh
+sudo ./dispense user add $USER
+sudo ./dispense user type $USER coke
+./dispense add $USER +1000 "initial"
diff --git a/notes/proto.txt b/notes/proto.txt
index dc11783ad527da8606d96b2006b1cac6ce2bd2b3..df3f9ccd2fe70a308fe2ac73492afb9135e85c00 100644
--- a/notes/proto.txt
+++ b/notes/proto.txt
@@ -82,7 +82,7 @@ c	ITEM_INFO <item_id>\n
 s	202 Item <item_id> <price> <description>\n
 
 --- Get Users' Balances ---
-c	ENUM_USERS[ min:<balance>][ max:<balance>][ flags:<flagset>][ lastseen:<unix_timestamp>][ sort:<field>[-desc]]\n
+c	ENUM_USERS[ min_balance:<balance>][ max_balance:<balance>][ flags:<flagset>][ last_seen_before:<unix_timestamp>][ last_seen_after:<unix_timestamp>][ sort:<field>[-desc]]\n
 s	201 Users <count>\n
 s	202 User <username> <balance> <flags>\n
     ...
diff --git a/src/client/main.c b/src/client/main.c
index 711d1a91ec8df67a64b0a5e90c6e57783ff67b19..0aea92673e17e3a220c203ccde7cc0d4947f5384 100644
--- a/src/client/main.c
+++ b/src/client/main.c
@@ -50,6 +50,7 @@ void	PopulateItemList(int Socket);
  int	DispenseItem(int Socket, int ItemID);
  int	Dispense_AlterBalance(int Socket, const char *Username, int Ammount, const char *Reason);
  int	Dispense_Give(int Socket, const char *Username, int Ammount, const char *Reason);
+ int	Dispense_Donate(int Socket, int Ammount, const char *Reason);
  int	Dispense_EnumUsers(int Socket);
  int	Dispense_ShowUser(int Socket, const char *Username);
 void	_PrintUserLine(const char *Line);
@@ -170,7 +171,7 @@ int main(int argc, char *argv[])
 		//
 		// `dispense give`
 		// - "Here, have some money."
-		else if( strcmp(arg, "give") == 0 )
+		if( strcmp(arg, "give") == 0 )
 		{
 			if( i + 3 >= argc ) {
 				fprintf(stderr, "`dispense give` takes three arguments\n");
@@ -197,7 +198,7 @@ int main(int argc, char *argv[])
 		// 
 		// `dispense user`
 		// - User administration (Wheel Only)
-		else if( strcmp(arg, "user") == 0 )
+		if( strcmp(arg, "user") == 0 )
 		{
 			// Check argument count
 			if( i + 1 >= argc ) {
@@ -244,6 +245,31 @@ int main(int argc, char *argv[])
 			}
 			return 0;
 		}
+		
+		// Donation!
+		if( strcmp(arg, "donate") == 0 )
+		{
+			// Check argument count
+			if( i + 2 >= argc ) {
+				fprintf(stderr, "Error: `dispense donate` requires two arguments\n");
+				ShowUsage();
+				exit(1);
+			}
+			
+			// Connect to server
+			sock = OpenConnection(gsDispenseServer, giDispensePort);
+			if( sock < 0 )	return -1;
+			
+			// Attempt authentication
+			if( Authenticate(sock) )
+				return -1;
+			
+			// Do donation
+			Dispense_Donate(sock, atoi(argv[i+1]), argv[i+1]);
+			
+			return 0;
+		}
+		
 		else {
 			// Item name / pattern
 			gsItemPattern = arg;
@@ -321,16 +347,18 @@ void ShowUsage(void)
 		"    dispense <item>\n"
 		"        Dispense named item\n"
 		"    dispense give <user> <ammount> \"<reason>\"\n"
-		"        Give some of your money away\n"
+		"        Give money to another user\n"
+		"    dispense donate <ammount> \"<reason>\"\n"
+		"        Donate to the club\n"
 		"    dispense acct [<user>]\n"
 		"        Show user balances\n"
 		"    dispense acct <user> [+-]<ammount> \"<reason>\"\n"
 		"        Alter a account value (Coke members only)\n"
 		"    dispense user add <user>\n"
-		"        Create new coke account (Wheel members only)\n"
-		"    dispense user type <flags>\n"
+		"        Create new coke account (Admins only)\n"
+		"    dispense user type <user> <flags>\n"
 		"        Alter a user's flags\n"
-		"        <flags> is a comma-separated list of user,coke,wheel,disabled\n"
+		"        <flags> is a comma-separated list of user, coke, admin or disabled\n"
 		"        Flags are removed by preceding the name with '-' or '!'\n"
 		"\n"
 		"General Options:\n"
@@ -970,7 +998,7 @@ int Dispense_AlterBalance(int Socket, const char *Username, int Ammount, const c
 }
 
 /**
- * \brief Alter a user's balance
+ * \brief Give money to another user
  */
 int Dispense_Give(int Socket, const char *Username, int Ammount, const char *Reason)
 {
@@ -1014,6 +1042,48 @@ int Dispense_Give(int Socket, const char *Username, int Ammount, const char *Rea
 	return -1;
 }
 
+
+/**
+ * \brief Donate money to the club
+ */
+int Dispense_Donate(int Socket, int Ammount, const char *Reason)
+{
+	char	*buf;
+	 int	responseCode;
+	
+	if( Ammount < 0 ) {
+		printf("Sorry, you can only give, you can't take.\n");
+		return -1;
+	}
+	
+	// Fast return on zero
+	if( Ammount == 0 ) {
+		printf("Are you actually going to give any?\n");
+		return 0;
+	}
+	
+	sendf(Socket, "DONATE %i %s\n", Ammount, Reason);
+	buf = ReadLine(Socket);
+	
+	responseCode = atoi(buf);
+	free(buf);
+	
+	switch(responseCode)
+	{
+	case 200:	return 0;	// OK
+	
+	case 402:	
+		fprintf(stderr, "Insufficient balance\n");
+		return 1;
+	
+	default:
+		fprintf(stderr, "Unknown response code %i\n", responseCode);
+		return -1;
+	}
+	
+	return -1;
+}
+
 /**
  * \brief Enumerate users
  */
@@ -1026,15 +1096,15 @@ int Dispense_EnumUsers(int Socket)
 	
 	if( giMinimumBalance != INT_MIN ) {
 		if( giMaximumBalance != INT_MAX ) {
-			sendf(Socket, "ENUM_USERS %i %i\n", giMinimumBalance, giMaximumBalance);
+			sendf(Socket, "ENUM_USERS min_balance:%i max_balance:%i\n", giMinimumBalance, giMaximumBalance);
 		}
 		else {
-			sendf(Socket, "ENUM_USERS %i\n", giMinimumBalance);
+			sendf(Socket, "ENUM_USERS min_balance:%i\n", giMinimumBalance);
 		}
 	}
 	else {
 		if( giMaximumBalance != INT_MAX ) {
-			sendf(Socket, "ENUM_USERS - %i\n", giMaximumBalance);
+			sendf(Socket, "ENUM_USERS max_balance:%i\n", giMaximumBalance);
 		}
 		else {
 			sendf(Socket, "ENUM_USERS\n");
diff --git a/src/cokebank.h b/src/cokebank.h
index d94e36fdda586d24f9fece81188ec50f34f21cbe..7c7997e285ceaef5ced143e4e9043455ebd55c73 100644
--- a/src/cokebank.h
+++ b/src/cokebank.h
@@ -28,20 +28,23 @@
  */
 typedef struct sAcctIterator	tAcctIterator;
 
+/**
+ * \brief Flag values for the \a Flags parameter to Bank_Iterator
+ */
 enum eBank_ItFlags
 {
-	BANK_ITFLAG_MINBALANCE	= 0x01,
-	BANK_ITFLAG_MAXBALANCE	= 0x02,
-	BANK_ITFLAG_SEENBEFORE	= 0x04,
-	BANK_ITFLAG_SEENAFTER	= 0x08,
+	BANK_ITFLAG_MINBALANCE	= 0x01,	//!< Balance value is Minium Balance
+	BANK_ITFLAG_MAXBALANCE	= 0x02,	//!< Balance value is Maximum Balance (higher priority)
+	BANK_ITFLAG_SEENAFTER	= 0x04,	//!< Last seen value is lower bound
+	BANK_ITFLAG_SEENBEFORE	= 0x08,	//!< Last seen value is upper bound (higher priority)
 	
-	BANK_ITFLAG_SORT_NONE	= 0x000,
-	BANK_ITFLAG_SORT_NAME	= 0x100,
-	BANK_ITFLAG_SORT_BAL	= 0x200,
-	BANK_ITFLAG_SORT_UNIXID	= 0x300,
-	BANK_ITFLAG_SORT_LASTSEEN	= 0x400,
-	BANK_ITFLAG_SORTMASK	= 0x700,
-	BANK_ITFLAG_REVSORT	= 0x800
+	BANK_ITFLAG_SORT_NONE	= 0x000,	//!< No sorting (up to the implementation)
+	BANK_ITFLAG_SORT_NAME	= 0x100,	//!< Sort alphabetically ascending by name
+	BANK_ITFLAG_SORT_BAL	= 0x200,	//!< Sort by balance, ascending
+	BANK_ITFLAG_SORT_UNIXID	= 0x300,	//!< Sort by UnixUID (TODO: Needed?)
+	BANK_ITFLAG_SORT_LASTSEEN = 0x400,	//!< Sort by last seen time (ascending)
+	BANK_ITFLAG_SORTMASK	= 0x700,	//!< Sort type mask
+	BANK_ITFLAG_REVSORT 	= 0x800	//!< Sort descending instead
 };
 
 /**
@@ -67,8 +70,8 @@ extern int	Bank_Initialise(const char *Argument);
 
 /**
  * \brief Transfer money from one account to another
- * \param SourceUser	UID (from \a Bank_GetUserID) to take the money from
- * \param DestUser	UID (from \a Bank_GetUserID) give money to
+ * \param SourceAcct	UID (from \a Bank_GetUserID) to take the money from
+ * \param DestAcct	UID (from \a Bank_GetUserID) give money to
  * \param Ammount	Amount of money (in cents) to transfer
  * \param Reason	Reason for the transfer
  */
@@ -98,11 +101,13 @@ extern int	Bank_GetBalance(int AcctID);
 extern char	*Bank_GetAcctName(int AcctID);
 /**
  * \brief Get an account ID from a passed name
+ * \param Name	Name to search for
+ * \return ID of the account, or -1 if not found
  */
 extern int	Bank_GetAcctByName(const char *Name);
 /**
  * \brief Create a new account
- * \param Username	Name for the new account (if NULL, an anoymous account is created)
+ * \param Name	Name for the new account (if NULL, an anoymous account is created)
  * \return ID of the new account
  */
 extern int	Bank_CreateAcct(const char *Name);
@@ -169,4 +174,10 @@ extern int	Bank_AddAcctCard(int AcctID, const char *CardID);
  */
 extern char	*mkstr(const char *Format, ...);
 
+/**
+ * \brief Dispense log access
+ * \note Try not to over-use, only stuff that matters should go in here
+ */
+extern void	Log_Info(const char *Format, ...);
+
 #endif
diff --git a/src/cokebank_sqlite/main.c b/src/cokebank_sqlite/main.c
index 26282dc890088e773510fb988c3a3eabe08c98f2..c112260ce6ff95bd311725e9a1215af5fa198d66 100644
--- a/src/cokebank_sqlite/main.c
+++ b/src/cokebank_sqlite/main.c
@@ -90,6 +90,8 @@ int Bank_Initialise(const char *Argument)
 			sqlite3_free(errmsg);
 			return 1;
 		}
+		
+		Log_Info("SQLite database rebuilt");
 	}
 	else
 	{
@@ -159,7 +161,7 @@ int Bank_GetFlags(int UserID)
 
 	// Build Query
 	query = mkstr(
-		"SELECT acct_is_disabled,acct_is_coke,acct_is_wheel,acct_is_door,acct_is_internal"
+		"SELECT acct_is_disabled,acct_is_coke,acct_is_admin,acct_is_door,acct_is_internal"
 		" FROM accounts WHERE acct_id=%i LIMIT 1",
 		UserID
 		);
@@ -335,6 +337,7 @@ tAcctIterator *Bank_Iterator(int FlagMask, int FlagValues, int Flags, int MinMax
 	const char	*revSort;
 	sqlite3_stmt	*ret;
 	
+	// Balance condtion
 	if( Flags & BANK_ITFLAG_MINBALANCE )
 		balanceClause = " AND acct_balance>=";
 	else if( Flags & BANK_ITFLAG_MAXBALANCE )
@@ -344,6 +347,7 @@ tAcctIterator *Bank_Iterator(int FlagMask, int FlagValues, int Flags, int MinMax
 		MinMaxBalance = 0;
 	}
 	
+	// Last seen condition
 	if( Flags & BANK_ITFLAG_SEENAFTER )
 		lastSeenClause = " AND acct_last_seen>=";
 	else if( Flags & BANK_ITFLAG_SEENBEFORE )
@@ -352,6 +356,7 @@ tAcctIterator *Bank_Iterator(int FlagMask, int FlagValues, int Flags, int MinMax
 		lastSeenClause = " AND datetime(-1,'unixepoch')!=";
 	}
 	
+	// Sorting clause
 	switch( Flags & BANK_ITFLAG_SORTMASK )
 	{
 	case BANK_ITFLAG_SORT_NONE:
diff --git a/src/server/dispense.c b/src/server/dispense.c
index d55c18de5f4bb1cde2837cab1a6d2722924cffc5..81d49dca8ef5dd23ea8c583055d1616b2a95e5f5 100644
--- a/src/server/dispense.c
+++ b/src/server/dispense.c
@@ -165,7 +165,7 @@ int _GetMinBalance(int Account)
 
 int _Transfer(int Source, int Destination, int Ammount, const char *Reason)
 {
-	if( Ammount < 0 )
+	if( Ammount > 0 )
 	{
 		if( Bank_GetBalance(Source) + Ammount < _GetMinBalance(Source) )
 			return 1;
diff --git a/src/server/server.c b/src/server/server.c
index 0b9005d14eeb88386db04fc12bf1e2a26606f9c1..9c36f3d7ce279eafa6a13003ef6bebf813d4d0fb 100644
--- a/src/server/server.c
+++ b/src/server/server.c
@@ -59,6 +59,7 @@ void	Server_Cmd_ENUMITEMS(tClient *Client, char *Args);
 void	Server_Cmd_ITEMINFO(tClient *Client, char *Args);
 void	Server_Cmd_DISPENSE(tClient *Client, char *Args);
 void	Server_Cmd_GIVE(tClient *Client, char *Args);
+void	Server_Cmd_DONATE(tClient *Client, char *Args);
 void	Server_Cmd_ADD(tClient *Client, char *Args);
 void	Server_Cmd_ENUMUSERS(tClient *Client, char *Args);
 void	Server_Cmd_USERINFO(tClient *Client, char *Args);
@@ -83,6 +84,7 @@ const struct sClientCommand {
 	{"ITEM_INFO", Server_Cmd_ITEMINFO},
 	{"DISPENSE", Server_Cmd_DISPENSE},
 	{"GIVE", Server_Cmd_GIVE},
+	{"DONATE", Server_Cmd_DONATE},
 	{"ADD", Server_Cmd_ADD},
 	{"ENUM_USERS", Server_Cmd_ENUMUSERS},
 	{"USER_INFO", Server_Cmd_USERINFO},
@@ -203,6 +205,7 @@ void Server_HandleClient(int Socket, int bTrusted)
 	clientInfo.Socket = Socket;
 	clientInfo.ID = giServer_NextClientID ++;
 	clientInfo.bIsTrusted = bTrusted;
+	clientInfo.EffectiveUID = -1;
 	
 	// Read from client
 	/*
@@ -771,36 +774,133 @@ void Server_Cmd_ADD(tClient *Client, char *Args)
 void Server_Cmd_ENUMUSERS(tClient *Client, char *Args)
 {
 	 int	i, numRet = 0;
-	 int	maxBal = INT_MAX, minBal = INT_MIN;
 	tAcctIterator	*it;
+	 int	maxBal = INT_MAX, minBal = INT_MIN;
+	 int	flagMask = 0, flagVal = 0;
 	 int	sort = BANK_ITFLAG_SORT_NAME;
+	time_t	lastSeenAfter=0, lastSeenBefore=0;
+	
+	 int	flags;	// Iterator flags
+	 int	balValue;	// Balance value for iterator
+	time_t	timeValue;	// Time value for iterator
 	
 	// Parse arguments
 	if( Args && strlen(Args) )
 	{
-		char	*min = Args, *max;
-		
-		max = strchr(Args, ' ');
-		if( max ) {
-			*max = '\0';
-			max ++;
-		}
-		
-		// If <minBal> != "-"
-		if( strcmp(min, "-") != 0 )
-			minBal = atoi(min);
-		// If <maxBal> != "-"
-		if( max && strcmp(max, "-") != 0 )
-			maxBal = atoi(max);
+		char	*space = Args, *type, *val;
+		do
+		{
+			type = space;
+			// Get next space
+			space = strchr(space, ' ');
+			if(space)	*space = '\0';
+			
+			// Get type
+			val = strchr(type, ':');
+			if( val ) {
+				*val = '\0';
+				val ++;
+				
+				// Types
+				// - Minium Balance
+				if( strcmp(type, "min_balance") == 0 ) {
+					minBal = atoi(val);
+				}
+				// - Maximum Balance
+				else if( strcmp(type, "max_balance") == 0 ) {
+					maxBal = atoi(val);
+				}
+				// - Flags
+				else if( strcmp(type, "flags") == 0 ) {
+					if( Server_int_ParseFlags(Client, val, &flagMask, &flagVal) )
+						return ;
+				}
+				// - Last seen before timestamp
+				else if( strcmp(type, "last_seen_before") == 0 ) {
+					lastSeenAfter = atoll(val);
+				}
+				// - Last seen after timestamp
+				else if( strcmp(type, "last_seen_after") == 0 ) {
+					lastSeenAfter = atoll(val);
+				}
+				// - Sorting 
+				else if( strcmp(type, "sort") == 0 ) {
+					char	*dash = strchr(val, '-');
+					if( dash ) {
+						*dash = '\0';
+						dash ++;
+					}
+					if( strcmp(val, "name") == 0 ) {
+						sort = BANK_ITFLAG_SORT_NAME;
+					}
+					else if( strcmp(val, "balance") == 0 ) {
+						sort = BANK_ITFLAG_SORT_BAL;
+					}
+					else if( strcmp(val, "lastseen") == 0 ) {
+						sort = BANK_ITFLAG_SORT_LASTSEEN;
+					}
+					else {
+						sendf(Client->Socket, "407 Unknown sort field ('%s')\n", val);
+						return ;
+					}
+					// Handle sort direction
+					if( dash ) {
+						if( strcmp(dash, "desc") == 0 ) {
+							sort |= BANK_ITFLAG_REVSORT;
+						}
+						else {
+							sendf(Client->Socket, "407 Unknown sort direction '%s'\n", dash);
+							return ;
+						}
+						dash[-1] = '-';
+					}
+				}
+				else {
+					sendf(Client->Socket, "407 Unknown argument to ENUM_USERS '%s:%s'\n", type, val);
+					return ;
+				}
+				
+				val[-1] = ':';
+			}
+			else {
+				sendf(Client->Socket, "407 Unknown argument to ENUM_USERS '%s'\n", type);
+				return ;
+			}
+			
+			// Eat whitespace
+			if( space ) {
+				*space = ' ';	// Repair (to be nice)
+				space ++;
+				while(*space == ' ')	space ++;
+			}
+		}	while(space);
 	}
 	
 	// Create iterator
-	if( maxBal != INT_MAX )
-		it = Bank_Iterator(0, 0, sort|BANK_ITFLAG_MAXBALANCE, maxBal, 0);
-	else if( minBal != INT_MIN )
-		it = Bank_Iterator(0, 0, sort|BANK_ITFLAG_MINBALANCE, minBal, 0);
-	else
-		it = Bank_Iterator(0, 0, sort, 0, 0);
+	if( maxBal != INT_MAX ) {
+		flags = sort|BANK_ITFLAG_MAXBALANCE;
+		balValue = maxBal;
+	}
+	else if( minBal != INT_MIN ) {
+		flags = sort|BANK_ITFLAG_MINBALANCE;
+		balValue = minBal;
+	}
+	else {
+		flags = sort;
+		balValue = 0;
+	}
+	if( lastSeenBefore ) {
+		timeValue = lastSeenBefore;
+		flags |= BANK_ITFLAG_SEENBEFORE;
+	}
+	else if( lastSeenAfter ) {
+		timeValue = lastSeenAfter;
+		flags |= BANK_ITFLAG_SEENAFTER;
+	}
+	else {
+		timeValue = 0;
+	}
+	it = Bank_Iterator(flagMask, flagVal, flags, balValue, timeValue);
 	
 	// Get return number
 	while( (i = Bank_IteratorNext(it)) != -1 )
@@ -822,12 +922,7 @@ void Server_Cmd_ENUMUSERS(tClient *Client, char *Args)
 	
 	
 	// Create iterator
-	if( maxBal != INT_MAX )
-		it = Bank_Iterator(0, 0, sort|BANK_ITFLAG_MAXBALANCE, maxBal, 0);
-	else if( minBal != INT_MIN )
-		it = Bank_Iterator(0, 0, sort|BANK_ITFLAG_MINBALANCE, minBal, 0);
-	else
-		it = Bank_Iterator(0, 0, sort, 0, 0);
+	it = Bank_Iterator(flagMask, flagVal, flags, balValue, timeValue);
 	
 	while( (i = Bank_IteratorNext(it)) != -1 )
 	{
@@ -921,6 +1016,12 @@ void Server_Cmd_USERADD(tClient *Client, char *Args)
 		return ;
 	}
 	
+	{
+		char	*thisName = Bank_GetAcctName(Client->UID);
+		Log_Info("Account '%s' created by '%s'", username, thisName);
+		free(thisName);
+	}
+	
 	sendf(Client->Socket, "200 User Added\n");
 }