+ free(buf);
+ return -1;
+ }
+
+ // Set effective user
+ if( gsEffectiveUser ) {
+ sendf(Socket, "SETEUSER %s\n", gsEffectiveUser);
+
+ buf = ReadLine(Socket);
+ responseCode = atoi(buf);
+
+ switch(responseCode)
+ {
+ case 200:
+ printf("Running as '%s' by '%s'\n", gsEffectiveUser, pwd->pw_name);
+ break;
+
+ case 403:
+ printf("Only coke members can use `dispense -u`\n");
+ free(buf);
+ return -1;
+
+ case 404:
+ printf("Invalid user selected\n");
+ free(buf);
+ return -1;
+
+ default:
+ fprintf(stderr, "Unkown response code %i from server\n", responseCode);
+ printf("%s\n", buf);
+ free(buf);
+ exit(-1);
+ }
+
+ free(buf);
+ }
+
+ gbIsAuthenticated = 1;
+
+ return 0;
+}
+
+
+/**
+ * \brief Fill the item information structure
+ * \return Boolean Failure
+ */
+void PopulateItemList(int Socket)
+{
+ char *buf;
+ int responseCode;
+
+ char *itemType, *itemStart;
+ int count, i;
+ regmatch_t matches[4];
+
+ // Ask server for stock list
+ send(Socket, "ENUM_ITEMS\n", 11, 0);
+ buf = ReadLine(Socket);
+
+ //printf("Output: %s\n", buf);
+
+ responseCode = atoi(buf);
+ 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>
+ // 202 Item <count>
+ RunRegex(&gArrayRegex, buf, 4, matches, "Malformed server response");
+
+ itemType = &buf[ matches[2].rm_so ]; buf[ matches[2].rm_eo ] = '\0';
+ count = atoi( &buf[ 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 = &buf[ matches[3].rm_eo ];
+
+ free(buf);
+
+ giNumItems = count;
+ gaItems = malloc( giNumItems * sizeof(tItem) );
+
+ // Fetch item information
+ for( i = 0; i < giNumItems; i ++ )
+ {
+ regmatch_t matches[6];
+
+ // Get item info
+ buf = ReadLine(Socket);
+ responseCode = atoi(buf);
+
+ if( responseCode != 202 ) {
+ fprintf(stderr, "Unknown response from dispense server (Response Code %i)\n", responseCode);
+ exit(-1);
+ }
+
+ RunRegex(&gItemRegex, buf, 6, matches, "Malformed server response");
+
+ buf[ matches[3].rm_eo ] = '\0';
+
+ gaItems[i].Ident = strdup( buf + matches[3].rm_so );
+ gaItems[i].Price = atoi( buf + matches[4].rm_so );
+ gaItems[i].Desc = strdup( buf + matches[5].rm_so );
+
+ free(buf);
+ }
+
+ // Read end of list
+ buf = ReadLine(Socket);
+ responseCode = atoi(buf);
+
+ if( responseCode != 200 ) {
+ fprintf(stderr, "Unknown response from dispense server %i\n'%s'",
+ responseCode, buf
+ );
+ exit(-1);
+ }
+
+ free(buf);
+}
+
+/**
+ * \brief Dispense an item
+ * \return Boolean Failure
+ */
+int DispenseItem(int Socket, int ItemID)
+{
+ int ret, responseCode;
+ char *buf;
+
+ if( ItemID < 0 || ItemID > giNumItems ) return -1;
+
+ // Dispense!
+ sendf(Socket, "DISPENSE %s\n", gaItems[ItemID].Ident);
+ buf = ReadLine(Socket);
+
+ responseCode = atoi(buf);
+ switch( responseCode )
+ {
+ case 200:
+ printf("Dispense OK\n");
+ ret = 0;
+ break;
+ case 401:
+ printf("Not authenticated\n");
+ ret = 1;
+ break;
+ case 402:
+ printf("Insufficient balance\n");
+ ret = 1;
+ break;
+ case 406:
+ printf("Bad item name, bug report\n");
+ ret = 1;
+ break;
+ case 500:
+ printf("Item failed to dispense, is the slot empty?\n");
+ ret = 1;
+ break;
+ case 501:
+ printf("Dispense not possible (slot empty/permissions)\n");
+ ret = 1;
+ break;
+ default:
+ printf("Unknown response code %i ('%s')\n", responseCode, buf);
+ ret = -2;
+ break;
+ }
+
+ free(buf);
+ return ret;
+}
+
+/**
+ * \brief Alter a user's balance
+ */
+int Dispense_AlterBalance(int Socket, const char *Username, int Ammount, const char *Reason)
+{
+ char *buf;
+ int responseCode;
+
+ sendf(Socket, "ADD %s %i %s\n", Username, Ammount, Reason);
+ buf = ReadLine(Socket);
+
+ responseCode = atoi(buf);
+ free(buf);
+
+ switch(responseCode)
+ {
+ case 200: return 0; // OK
+ case 403: // Not in coke
+ fprintf(stderr, "You are not in coke (sucker)\n");
+ return 1;
+ case 404: // Unknown user
+ fprintf(stderr, "Unknown user '%s'\n", Username);
+ return 2;
+ default:
+ fprintf(stderr, "Unknown response code %i\n", responseCode);
+ return -1;
+ }
+
+ return -1;
+}
+
+/**
+ * \brief Alter a user's balance
+ */
+int Dispense_SetBalance(int Socket, const char *Username, int Ammount, const char *Reason)
+{
+ char *buf;
+ int responseCode;
+
+ sendf(Socket, "SET %s %i %s\n", Username, Ammount, Reason);
+ buf = ReadLine(Socket);
+
+ responseCode = atoi(buf);
+ free(buf);
+
+ switch(responseCode)
+ {
+ case 200: return 0; // OK
+ case 403: // Not in coke
+ fprintf(stderr, "You are not in coke (sucker)\n");
+ return 1;
+ case 404: // Unknown user
+ fprintf(stderr, "Unknown user '%s'\n", Username);
+ return 2;
+ default:
+ fprintf(stderr, "Unknown response code %i\n", responseCode);
+ return -1;
+ }
+
+ return -1;
+}
+
+/**
+ * \brief Enumerate users
+ */
+int Dispense_EnumUsers(int Socket)
+{
+ char *buf;
+ int responseCode;
+ int nUsers;
+ regmatch_t matches[4];
+
+ if( giMinimumBalance != INT_MIN ) {
+ if( giMaximumBalance != INT_MAX ) {
+ sendf(Socket, "ENUM_USERS %i %i\n", giMinimumBalance, giMaximumBalance);
+ }
+ else {
+ sendf(Socket, "ENUM_USERS %i\n", giMinimumBalance);
+ }
+ }
+ else {
+ if( giMaximumBalance != INT_MAX ) {
+ sendf(Socket, "ENUM_USERS - %i\n", giMaximumBalance);
+ }
+ else {
+ sendf(Socket, "ENUM_USERS\n");
+ }
+ }
+ buf = ReadLine(Socket);
+ responseCode = atoi(buf);
+
+ switch(responseCode)
+ {
+ case 201: break; // Ok, length follows
+
+ default:
+ fprintf(stderr, "Unknown response code %i\n%s\n", responseCode, buf);
+ free(buf);