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
--- 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
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
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
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);
// TODO: Be less lazy here and check the timestamp
AddPeriodicFunction( Items_ReadFromFile );
}
+
/**
* \brief Read the item list from disk
*/
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;
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, ...);
{"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])))
// 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(;;)
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, ...)
{