diff --git a/notes/proto.txt b/notes/proto.txt
index d48031e8d48cea4904f635e02d5547ae50cd537e..8a1a1da712a1b08bdc3c90b61d77332cf0ef0daa 100644
--- a/notes/proto.txt
+++ b/notes/proto.txt
@@ -57,7 +57,7 @@ s	200 Auth OK as <username>\n or 401 Untrusted\n or 404 Bad Card ID\n
 c	SETEUSER <username>\n
 s	200 User set\n or 403 Not in coke\n or 404 User not found\n
 
-=== Commands ===
+=== Standard User Commands ===
 --- Dispense an item ---
 c	DISPENSE <item_id>\n
 s	200 Dispense OK\n or 402 Poor You\n or 406 Bad Item\n or 500 Dispense Error\n
@@ -67,10 +67,20 @@ s	200 Give OK\n or 402 Poor You\n or 404 Bad User\n
 --- Donate to the club ---
 c	DONATE <ammount> <reason>\n
 s	200 Give OK\n or 402 Poor You\n
+
+=== Coke Member Commands (Account Manipulation) ===
 --- Alter balance ---
 c	ADD <user> <ammount> <reason>\n
 s	200 Add OK\n or 402 No balance\n or 403 Not Coke\n or 404 Bad User\n
+--- Set balance ---
+c	SET <user> <balance> <reason>\n
+s	200 Add OK\n or 402 No balance\n or 403 Not Coke\n or 404 Bad User\n
+--- Refund a drink ---
+c	REFUND <user> <item>[ <price>]\n
+s	200 Add OK\n or 403 Not Coke\n or 404 Bad User\n 406 Bad Item\n
 
+
+=== Items ===
 --- Get Item list ---
 c	ENUM_ITEMS\n
 s	201 Items <count>\n
@@ -81,6 +91,11 @@ s	200 List End\n
 c	ITEM_INFO <item_id>\n
 s	202 Item <item_id> <status> <price> <description>\n
 <status>	"avail", "sold", or "error"
+--- Update an item ---
+c	UPDATE_ITEM <item_id> <price> <name>\n
+s	200 Item updated
+
+=== Users ===
 --- Get Users' Balances ---
 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
@@ -95,6 +110,7 @@ s	200 List End\n
 c	USER_INFO\n
 s	202 User <username> <balance> <flags>\n
 
+=== User Manipulation ===
 --- Add a new user ---
 c	USER_ADD <username>\n
 s	200 User Added\n or 403 Not Wheel\n or 404 User Exists\n
diff --git a/src/server/common.h b/src/server/common.h
index cf68523908971885c50e3f79af26c79790ad4205..d5fd43cf83c269450e5a989bce5869d35ebcd33f 100644
--- a/src/server/common.h
+++ b/src/server/common.h
@@ -72,6 +72,8 @@ extern int	giNumHandlers;
 extern int	giDebugLevel;
 
 // === FUNCTIONS ===
+extern void	Items_UpdateFile(void);
+
 // --- Helpers --
 extern void	AddPeriodicFunction(void (*Fcn)(void));
 extern void	CompileRegex(regex_t *Regex, const char *Pattern, int Flags);
diff --git a/src/server/itemdb.c b/src/server/itemdb.c
index 0c4df3fdbdf4b6b9cc7a47610ab89e82d6da052f..dc188fdc2d63859bfbc5f082a632295d96de5ee2 100644
--- a/src/server/itemdb.c
+++ b/src/server/itemdb.c
@@ -102,6 +102,7 @@ void Load_Itemlist(void)
 	// TODO: Be less lazy here and check the timestamp
 	AddPeriodicFunction( Items_ReadFromFile );
 }
+
 /**
  * \brief Read the item list from disk
  */
@@ -221,6 +222,159 @@ void Items_ReadFromFile(void)
 	gItems_LastUpdated = time(NULL);
 }
 
+/**
+ * \brief Update the item file from the internal database
+ */
+void Items_UpdateFile(void)
+{
+	FILE	*fp;
+	char	buffer[BUFSIZ];
+	char	*line;
+	 int	lineNum = 0;
+	 int	i;
+	regmatch_t	matches[5];
+	char	**line_comments;
+	 int	*line_items;
+
+	// Error check
+	fp = fopen(gsItemListFile, "r");
+	if(!fp) {
+		fprintf(stderr, "Unable to open item file '%s'\n", gsItemListFile);
+		perror("Unable to open item file");
+		return ;
+	}
+	
+	// Count lines
+	while( fgets(buffer, BUFSIZ, fp) )
+	{
+		lineNum ++;
+	}
+	
+	line_comments = malloc(lineNum * sizeof(char*));
+	line_items = malloc(lineNum * sizeof(int));
+	
+	// Parse file
+	lineNum = 0;
+	fseek(fp, 0, SEEK_SET);
+	while( fgets(buffer, BUFSIZ, fp) )
+	{
+		char	*hashPos, *semiPos;
+		char	*type;
+		 int	num;
+		tHandler	*handler;
+
+		lineNum ++;
+		line_items[lineNum-1] = -1;
+		line_comments[lineNum-1] = NULL;
+
+		// Get comments
+		hashPos = strchr(buffer, '#');
+		semiPos = strchr(buffer, ';');
+		if( hashPos && semiPos ) {
+			if( hashPos < semiPos )
+				line_comments[lineNum-1] = strdup(hashPos);
+		}
+		else if( hashPos ) {
+			line_comments[lineNum-1] = strdup(hashPos);
+		}
+		else if( semiPos ) {
+			line_comments[lineNum-1] = strdup(semiPos);
+		}
+		if(hashPos)	*hashPos = '\0';
+		if(semiPos)	*semiPos = '\0';
+		
+		// Trim whitespace
+		line = trim(buffer);
+		if(strlen(line) == 0)	continue;
+		
+		// Pass regex over line
+		if( RunRegex( &gItemFile_Regex, line, 5, matches, NULL) ) {
+			fprintf(stderr, "Syntax error on line %i of item file '%s'\n", lineNum, gsItemListFile);
+			return ;
+		}
+
+		// Read line data
+		type  = line + matches[1].rm_so;	line[ matches[1].rm_eo ] = '\0';
+		num   = atoi( line + matches[2].rm_so );
+
+		// Find handler
+		handler = NULL;
+		for( i = 0; i < giNumHandlers; i ++ )
+		{
+			if( strcmp(type, gaHandlers[i]->Name) == 0 ) {
+				handler = gaHandlers[i];
+				break;
+			}
+		}
+		if( !handler ) {
+			fprintf(stderr, "Warning: Unknown item type '%s' on line %i\n", type, lineNum);
+			continue ;
+		}
+
+		for( i = 0; i < giNumItems; i ++ )
+		{
+			if( gaItems[i].Handler != handler )	continue;
+			if( gaItems[i].ID != num )	continue;
+			
+			line_items[lineNum-1] = i;
+			break;
+		}
+		if( i >= giNumItems ) {
+			continue;
+		}
+	}
+	
+	fclose(fp);
+	
+	fp = fopen("items.cfg.new", "w");	// DEBUG: Don't kill the real item file until debugged
+	
+	// Create new file
+	{
+		 int	done_items[giNumItems];
+		memset(done_items, 0, sizeof(done_items));
+		
+		// Existing items
+		for( i = 0; i < lineNum; i ++ )
+		{
+			if( line_items[i] != -1 ) {
+				tItem	*item = &gaItems[ line_items[i] ];
+				
+				if( done_items[ line_items[i] ] ) {
+					fprintf(fp, "; DUP -");
+				}
+				
+				done_items[ line_items[i] ] = 1;
+				fprintf(fp, "%s\t%i\t%i\t%s\t",
+					item->Handler->Name, item->ID, item->Price, item->Name
+					);
+			}
+			
+			if( line_comments[i] ) {
+				fprintf(fp, "%s", line_comments[i]);
+				free( line_comments[i] );
+			}
+			
+			fprintf(fp, "\n");
+		}
+		
+		// New items
+		for( i = 0; i < giNumItems; i ++ )
+		{
+			tItem	*item = &gaItems[i];
+			if( done_items[i] )	continue ;
+			
+			fprintf(fp, "%s\t%i\t%i\t%s\n",
+				item->Handler->Name, item->ID, item->Price, item->Name
+				);
+		}
+	}
+	
+	free( line_comments );
+	free( line_items );
+	fclose(fp);
+}
+
+
 char *trim(char *__str)
 {
 	char	*ret;
diff --git a/src/server/server.c b/src/server/server.c
index de79f417c79a9530152cf5c3935055bdf977240d..1ebb61256d5ae12cd6e1b6fee26b0ffd2da88f3b 100644
--- a/src/server/server.c
+++ b/src/server/server.c
@@ -70,6 +70,7 @@ void	Server_Cmd_USERINFO(tClient *Client, char *Args);
 void	_SendUserInfo(tClient *Client, int UserID);
 void	Server_Cmd_USERADD(tClient *Client, char *Args);
 void	Server_Cmd_USERFLAGS(tClient *Client, char *Args);
+void	Server_Cmd_UPDATEITEM(tClient *Client, char *Args);
 // --- Helpers ---
 void	Debug(tClient *Client, const char *Format, ...);
  int	sendf(int Socket, const char *Format, ...);
@@ -97,7 +98,8 @@ const struct sClientCommand {
 	{"ENUM_USERS", Server_Cmd_ENUMUSERS},
 	{"USER_INFO", Server_Cmd_USERINFO},
 	{"USER_ADD", Server_Cmd_USERADD},
-	{"USER_FLAGS", Server_Cmd_USERFLAGS}
+	{"USER_FLAGS", Server_Cmd_USERFLAGS},
+	{"UPDATE_ITEM", Server_Cmd_UPDATEITEM}
 };
 #define NUM_COMMANDS	((int)(sizeof(gaServer_Commands)/sizeof(gaServer_Commands[0])))
 
@@ -177,8 +179,10 @@ void Server_Start(void)
 	// write pidfile
 	{
 		FILE *fp = fopen("/var/run/dispsrv.pid", "w");
-		fprintf(fp, "%i", getpid());
-		fclose(fp);
+		if( fp ) {
+			fprintf(fp, "%i", getpid());
+			fclose(fp);
+		}
 	}
 
 	for(;;)
@@ -1283,6 +1287,52 @@ void Server_Cmd_USERFLAGS(tClient *Client, char *Args)
 	sendf(Client->Socket, "200 User Updated\n");
 }
 
+void Server_Cmd_UPDATEITEM(tClient *Client, char *Args)
+{
+	char	*itemname, *price_str, *description;
+	 int	price;
+	tItem	*item;
+	
+	if( Server_int_ParseArgs(1, Args, &itemname, &price_str, &description, NULL) ) {
+		sendf(Client->Socket, "407 UPDATE_ITEM takes 3 arguments\n");
+		return ;
+	}
+	
+	if( !Client->bIsAuthed ) {
+		sendf(Client->Socket, "401 Not Authenticated\n");
+		return ;
+	}
+
+	// Check user permissions
+	if( !(Bank_GetFlags(Client->UID) & (USER_FLAG_COKE|USER_FLAG_ADMIN))  ) {
+		sendf(Client->Socket, "403 Not in coke\n");
+		return ;
+	}
+	
+	item = _GetItemFromString(itemname);
+	if( !item ) {
+		// TODO: Create item?
+		sendf(Client->Socket, "406 Bad Item ID\n");
+		return ;
+	}
+	
+	price = atoi(price_str);
+	if( price <= 0 && price_str[0] != '0' ) {
+		sendf(CLient->Socket, "407 Invalid price set\n");
+	}
+	
+	// Update the item
+	free(item->Name);
+	item->Name = strdup(description);
+	item->Price = price;
+	
+	// Update item file
+	Items_UpdateFile();
+	
+	// Return OK
+	sendf(Client->Socket, "200 Item updated\n");
+}
+
 // --- INTERNAL HELPERS ---
 void Debug(tClient *Client, const char *Format, ...)
 {