+ // 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[7];
+
+ // 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, 7, matches, "Malformed server response");
+
+ buf[ matches[3].rm_eo ] = '\0';
+
+ gaItems[i].Type = strdup( buf + matches[3].rm_so );
+ gaItems[i].ID = atoi( buf + matches[4].rm_so );
+ gaItems[i].Price = atoi( buf + matches[5].rm_so );
+ gaItems[i].Desc = strdup( buf + matches[6].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:%i\n", gaItems[ItemID].Type, gaItems[ItemID].ID);
+ 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 402:
+ fprintf(stderr, "Insufficient balance\n");
+ return 1;
+ 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 Set a user's balance
+ * \note Only avaliable to dispense admins
+ */
+int Dispense_SetBalance(int Socket, const char *Username, int Balance, const char *Reason)
+{
+ char *buf;
+ int responseCode;
+
+ sendf(Socket, "SET %s %i %s\n", Username, Balance, 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 an admin\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 Give money to another user
+ */
+int Dispense_Give(int Socket, const char *Username, 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, "GIVE %s %i %s\n", Username, 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;
+
+ 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 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
+ */
+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 min_balance:%i max_balance:%i\n", giMinimumBalance, giMaximumBalance);
+ }
+ else {
+ sendf(Socket, "ENUM_USERS min_balance:%i\n", giMinimumBalance);
+ }
+ }
+ else {
+ if( giMaximumBalance != INT_MAX ) {
+ sendf(Socket, "ENUM_USERS max_balance:%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);
+ return -1;
+ }
+
+ // Get count (not actually used)
+ RunRegex(&gArrayRegex, buf, 4, matches, "Malformed server response");
+ nUsers = atoi( buf + matches[3].rm_so );
+ printf("%i users returned\n", nUsers);
+
+ // Free string
+ free(buf);
+
+ // Read returned users
+ do {
+ buf = ReadLine(Socket);
+ responseCode = atoi(buf);
+
+ if( responseCode != 202 ) break;
+
+ _PrintUserLine(buf);
+ free(buf);
+ } while(responseCode == 202);
+
+ // Check final response
+ if( responseCode != 200 ) {
+ fprintf(stderr, "Unknown response code %i\n%s\n", responseCode, buf);
+ free(buf);
+ return -1;
+ }
+
+ free(buf);
+
+ return 0;
+}
+
+int Dispense_ShowUser(int Socket, const char *Username)
+{
+ char *buf;
+ int responseCode, ret;
+
+ sendf(Socket, "USER_INFO %s\n", Username);
+ buf = ReadLine(Socket);
+
+ responseCode = atoi(buf);
+
+ switch(responseCode)
+ {
+ case 202:
+ _PrintUserLine(buf);
+ ret = 0;
+ break;
+
+ case 404:
+ printf("Unknown user '%s'\n", Username);
+ ret = 1;
+ break;
+
+ default:
+ fprintf(stderr, "Unknown response code %i '%s'\n", responseCode, buf);
+ ret = -1;
+ break;
+ }
+
+ free(buf);
+
+ return ret;
+}
+
+void _PrintUserLine(const char *Line)
+{
+ regmatch_t matches[6];
+ int bal;
+
+ RunRegex(&gUserInfoRegex, Line, 6, matches, "Malformed server response");
+ // 3: Username
+ // 4: Balance
+ // 5: Flags
+ {
+ int usernameLen = matches[3].rm_eo - matches[3].rm_so;
+ char username[usernameLen + 1];
+ int flagsLen = matches[5].rm_eo - matches[5].rm_so;
+ char flags[flagsLen + 1];
+
+ memcpy(username, Line + matches[3].rm_so, usernameLen);
+ username[usernameLen] = '\0';
+ memcpy(flags, Line + matches[5].rm_so, flagsLen);
+ flags[flagsLen] = '\0';
+
+ bal = atoi(Line + matches[4].rm_so);
+ printf("%-15s: $%4i.%02i (%s)\n", username, bal/100, abs(bal)%100, flags);
+ }
+}
+
+int Dispense_AddUser(int Socket, const char *Username)
+{
+ char *buf;
+ int responseCode, ret;
+
+ sendf(Socket, "USER_ADD %s\n", Username);
+
+ buf = ReadLine(Socket);
+ responseCode = atoi(buf);
+
+ switch(responseCode)
+ {
+ case 200:
+ printf("User '%s' added\n", Username);
+ ret = 0;
+ break;
+
+ case 403:
+ printf("Only wheel can add users\n");
+ ret = 1;
+ break;
+
+ case 404:
+ printf("User '%s' already exists\n", Username);
+ ret = 0;
+ break;
+
+ default:
+ fprintf(stderr, "Unknown response code %i '%s'\n", responseCode, buf);
+ ret = -1;
+ break;
+ }
+
+ free(buf);
+
+ return ret;
+}
+
+int Dispense_SetUserType(int Socket, const char *Username, const char *TypeString)
+{
+ char *buf;
+ int responseCode, ret;
+
+ // TODO: Pre-validate the string
+
+ sendf(Socket, "USER_FLAGS %s %s\n", Username, TypeString);
+
+ buf = ReadLine(Socket);
+ responseCode = atoi(buf);
+
+ switch(responseCode)
+ {
+ case 200:
+ printf("User '%s' updated\n", Username);
+ ret = 0;
+ break;
+
+ case 403:
+ printf("Only wheel can modify users\n");
+ ret = 1;
+ break;
+
+ case 404:
+ printf("User '%s' does not exist\n", Username);
+ ret = 0;
+ break;
+
+ case 407:
+ printf("Flag string is invalid\n");
+ ret = 0;
+ break;
+
+ default:
+ fprintf(stderr, "Unknown response code %i '%s'\n", responseCode, buf);
+ ret = -1;
+ break;
+ }
+
+ free(buf);
+
+ return ret;
+}
+
+// ---------------
+// --- Helpers ---
+// ---------------
+char *ReadLine(int Socket)
+{
+ static char buf[BUFSIZ];
+ static int bufPos = 0;
+ static int bufValid = 0;
+ int len;
+ char *newline = NULL;
+ int retLen = 0;
+ char *ret = malloc(10);
+
+ #if DEBUG_TRACE_SERVER
+ printf("ReadLine: ");
+ #endif
+ fflush(stdout);
+
+ ret[0] = '\0';
+
+ while( !newline )
+ {
+ if( bufValid ) {
+ len = bufValid;
+ }
+ else {
+ len = recv(Socket, buf+bufPos, BUFSIZ-1-bufPos, 0);
+ buf[bufPos+len] = '\0';
+ }
+
+ newline = strchr( buf+bufPos, '\n' );
+ if( newline ) {
+ *newline = '\0';
+ }
+
+ retLen += strlen(buf+bufPos);
+ ret = realloc(ret, retLen + 1);
+ strcat( ret, buf+bufPos );
+
+ if( newline ) {
+ int newLen = newline - (buf+bufPos) + 1;
+ bufValid = len - newLen;
+ bufPos += newLen;
+ }
+ if( len + bufPos == BUFSIZ - 1 ) bufPos = 0;
+ }
+
+ #if DEBUG_TRACE_SERVER
+ printf("%i '%s'\n", retLen, ret);
+ #endif
+
+ return ret;
+}
+
+int sendf(int Socket, const char *Format, ...)
+{
+ va_list args;
+ int len;
+
+ va_start(args, Format);
+ len = vsnprintf(NULL, 0, Format, args);
+ va_end(args);
+
+ {
+ char buf[len+1];
+ va_start(args, Format);
+ vsnprintf(buf, len+1, Format, args);
+ va_end(args);
+
+ #if DEBUG_TRACE_SERVER
+ printf("sendf: %s", buf);
+ #endif
+
+ return send(Socket, buf, len, 0);
+ }