RV_PERMISSIONS,
RV_ARGUMENTS,
RV_BALANCE,
+ RV_SERVER_ERROR, // Generic for 5xx codes
RV_UNKNOWN_ERROR = -1,
- RV_SOCKET_ERROR = -2
+ RV_SOCKET_ERROR = -2,
+ RV_UNKNOWN_RESPONSE = -3,
};
// === TYPES ===
} tItem;
// === PROTOTYPES ===
- int main(int argc, char *argv[]);
void ShowUsage(void);
+ int main(int argc, char *argv[]);
// --- GUI ---
int ShowNCursesUI(void);
int ShowItemAt(int Row, int Col, int Width, int Index, int bHilighted);
int Dispense_AlterBalance(int Socket, const char *Username, int Ammount, const char *Reason);
int Dispense_SetBalance(int Socket, const char *Username, int Balance, const char *Reason);
int Dispense_Give(int Socket, const char *Username, int Ammount, const char *Reason);
+ int Dispense_Refund(int Socket, const char *Username, const char *Item, int PriceOverride);
int Dispense_Donate(int Socket, int Ammount, const char *Reason);
int Dispense_EnumUsers(int Socket);
int Dispense_ShowUser(int Socket, const char *Username);
int giDispenseCount = 1; //!< Number of dispenses to do
// === CODE ===
+void ShowUsage(void)
+{
+ printf(
+ "Usage:\n"
+ " == Everyone ==\n"
+ " dispense\n"
+ " Show interactive list\n"
+ " dispense <name>|<index>|<itemid>\n"
+ " Dispense named item (<name> matches if it is a unique prefix)\n"
+ " dispense give <user> <ammount> \"<reason>\"\n"
+ " Give money to another user\n"
+ " dispense donate <ammount> \"<reason>\"\n"
+ " Donate to the club\n"
+ " dispense iteminfo <itemid>\n"
+ " Get the name and price for an item\n"
+ " == Coke members == \n"
+ " dispense acct [<user>]\n"
+ " Show user balances\n"
+ " dispense acct <user> [+-]<ammount> \"<reason>\"\n"
+ " Alter a account value\n"
+ " dispense refund <user> <itemid> [<price>]\n"
+ " Refund an item to a user (with optional price override)\n"
+ " == Dispense administrators ==\n"
+ " dispense acct <user> =<ammount> \"<reason>\"\n"
+ " Set an account balance\n"
+ " dispense user add <user>\n"
+ " Create new account\n"
+ " dispense user type <user> <flags>\n"
+ " Alter a user's flags\n"
+ " <flags> is a comma-separated list of user, coke, admin, internal or disabled\n"
+ " Flags are removed by preceding the name with '-' or '!'\n"
+ "\n"
+ "General Options:\n"
+ " -c <count>\n"
+ " Dispense multiple times\n"
+ " -u <username>\n"
+ " Set a different user (Coke members only)\n"
+ " -h / -?\n"
+ " Show help text\n"
+ " -G\n"
+ " Use alternate GUI\n"
+ " -n\n"
+ " Dry run - Do not actually do dispenses\n"
+ " -m <min balance>\n"
+ " -M <max balance>\n"
+ " Set the Maximum/Minimum balances shown in `dispense acct`\n"
+ "Definitions:\n"
+ " <itemid>\n"
+ " Item ID of the form <type>:<num> where <type> is a non-empty string of alpha-numeric characters, and <num> is a non-negative integer\n"
+// " <user>\n"
+// " Account name\n"
+ );
+}
+
int main(int argc, char *argv[])
{
int sock;
gbDryRun = 1;
break;
default:
+ if( !isdigit(argv[i][0]) ) {
+ fprintf(stderr, "%s: Unknown switch '%s'\n", argv[0], argv[i]);
+ ShowUsage();
+ return RV_ARGUMENTS;
+ }
if( text_argc + 1 == MAX_TXT_ARGS )
{
fprintf(stderr, "ERROR: Too many arguments\n");
// text_args[1]: Username
// Alter account?
- if( text_argc == 4 )
+ if( text_argc != 2 )
{
+ if( text_argc != 4 ) {
+ fprintf(stderr, "`dispense acct` requires a reason\n");
+ ShowUsage();
+ return RV_ARGUMENTS;
+ }
+
// Authentication required
ret = Authenticate(sock);
if(ret) return ret;
// Refund an item
else if( strcmp(text_args[0], "refund") == 0 )
{
+ int price = 0;
// Check argument count
if( text_argc != 3 && text_argc != 4 ) {
fprintf(stderr, "Error: `dispense refund` takes 2 or 3 arguments\n");
ret = Authenticate(sock);
if(ret) return ret;
+ if( text_argc == 4 ) {
+ price = atoi(text_args[3]);
+ if( price <= 0 ) {
+ fprintf(stderr, "Error: Override price is invalid (should be > 0)\n");
+ return RV_ARGUMENTS;
+ }
+ }
+
+ // Username, Item, cost
+ ret = Dispense_Refund(sock, text_args[1], text_args[2], price);
+
// TODO: More
- close(ret);
+ close(sock);
return RV_UNKNOWN_ERROR;
}
// Query an item price
return ret;
}
-void ShowUsage(void)
-{
- printf(
- "Usage:\n"
- " == Everyone ==\n"
- " dispense\n"
- " Show interactive list\n"
- " dispense <name>|<index>|<itemid>\n"
- " Dispense named item (<name> matches if it is a unique prefix)\n"
- " dispense give <user> <ammount> \"<reason>\"\n"
- " Give money to another user\n"
- " dispense donate <ammount> \"<reason>\"\n"
- " Donate to the club\n"
- " dispense iteminfo <type>:<id>\n"
- " Get the name and price for an item\n"
- " == Coke members == \n"
- " dispense acct [<user>]\n"
- " Show user balances\n"
- " dispense acct <user> [+-]<ammount> \"<reason>\"\n"
- " Alter a account value\n"
- " == Dispense administrators ==\n"
- " dispense acct <user> =<ammount> \"<reason>\"\n"
- " Set an account balance\n"
- " dispense user add <user>\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, admin or disabled\n"
- " Flags are removed by preceding the name with '-' or '!'\n"
- "\n"
- "General Options:\n"
- " -c <count>\n"
- " Dispense multiple times\n"
- " -u <username>\n"
- " Set a different user (Coke members only)\n"
- " -h / -?\n"
- " Show help text\n"
- " -G\n"
- " Use alternate GUI\n"
- " -m <min balance>\n"
- " -M <max balance>\n"
- " Set the Maximum/Minimum balances shown in `dispense acct`\n"
- );
-}
-
// -------------------
// --- NCurses GUI ---
// -------------------
*/
int ShowNCursesUI(void)
{
- // TODO: ncurses interface (with separation between item classes)
- // - Hmm... that would require standardising the item ID to be <class>:<index>
- // Oh, why not :)
int ch;
int i, times;
int xBase, yBase;
case '\n':
ret = ShowItemAt(0, 0, 0, currentItem, 0);
break;
+ case 0x1b: // Escape
case 'q':
ret = -1; // -1: Return with no dispense
break;
fprintf(stderr, "Failed to connect to server\n");
return -1;
}
+
+ // We're not authenticated if the connection has just opened
+ gbIsAuthenticated = 0;
return sock;
}
struct passwd *pwd;
char *buf;
int responseCode;
+ #if ATTEMPT_PASSWORD_AUTH
char salt[32];
int i;
regmatch_t matches[4];
+ #endif
if( gbIsAuthenticated ) return 0;
case 401: // Untrusted, attempt password authentication
free(buf);
-
+
+ #if ATTEMPT_PASSWORD_AUTH
sendf(Socket, "USER %s\n", pwd->pw_name);
printf("Using username %s\n", pwd->pw_name);
free(buf);
if( i == 3 )
return RV_INVALID_USER; // 2 = Bad Password
+
+ #else
+ fprintf(stderr, "Untrusted host, AUTOAUTH unavaliable\n");
+ return RV_INVALID_USER;
+ #endif
break;
case 404: // Bad Username
break;
case 500:
printf("Item failed to dispense, is the slot empty?\n");
- ret = 1;
+ ret = RV_SERVER_ERROR;
break;
case 501:
printf("Dispense not possible (slot empty/permissions)\n");
- ret = 1;
+ ret = RV_SERVER_ERROR;
break;
default:
printf("Unknown response code %i ('%s')\n", responseCode, buf);
case 200: return 0; // OK
case 402:
fprintf(stderr, "Insufficient balance\n");
- return 1;
+ return RV_BAD_ITEM;
case 403: // Not in coke
fprintf(stderr, "You are not in coke (sucker)\n");
- return 1;
+ return RV_PERMISSIONS;
case 404: // Unknown user
fprintf(stderr, "Unknown user '%s'\n", Username);
- return 2;
+ return RV_INVALID_USER;
default:
fprintf(stderr, "Unknown response code %i\n", responseCode);
- return -1;
+ return RV_UNKNOWN_RESPONSE;
}
return -1;
case 200: return 0; // OK
case 403: // Not in coke
fprintf(stderr, "You are not an admin\n");
- return 1;
+ return RV_PERMISSIONS;
case 404: // Unknown user
fprintf(stderr, "Unknown user '%s'\n", Username);
- return 2;
+ return RV_INVALID_USER;
default:
fprintf(stderr, "Unknown response code %i\n", responseCode);
- return -1;
+ return RV_UNKNOWN_RESPONSE;
}
return -1;
if( Ammount < 0 ) {
printf("Sorry, you can only give, you can't take.\n");
- return 1;
+ return RV_ARGUMENTS;
}
// Fast return on zero
if( Ammount == 0 ) {
printf("Are you actually going to give any?\n");
- return 1;
+ return RV_ARGUMENTS;
}
// Check for a dry run
}
sendf(Socket, "GIVE %s %i %s\n", Username, Ammount, Reason);
+
buf = ReadLine(Socket);
-
responseCode = atoi(buf);
- free(buf);
-
+ free(buf);
switch(responseCode)
{
case 200:
printf("Give succeeded\n");
- return 0; // OK
+ return RV_SUCCESS; // OK
case 402:
fprintf(stderr, "Insufficient balance\n");
- return 1;
+ return RV_BALANCE;
case 404: // Unknown user
fprintf(stderr, "Unknown user '%s'\n", Username);
- return 2;
+ return RV_INVALID_USER;
default:
fprintf(stderr, "Unknown response code %i\n", responseCode);
- return -1;
+ return RV_UNKNOWN_RESPONSE;
}
return -1;
}
+int Dispense_Refund(int Socket, const char *Username, const char *Item, int PriceOverride)
+{
+ char *buf;
+ int responseCode, ret = -1;
+
+ // Check item id
+ if( RunRegex(&gUserItemIdentRegex, Item, 0, NULL, NULL) != 0 )
+ {
+ fprintf(stderr, "Error: Invalid item ID passed (should be <type>:<num>)\n");
+ return RV_ARGUMENTS;
+ }
+
+ // Check username (quick)
+ if( strchr(Username, ' ') || strchr(Username, '\n') )
+ {
+ fprintf(stderr, "Error: Username is invalid (no spaces or newlines please)\n");
+ return RV_ARGUMENTS;
+ }
+
+ // Send the query
+ sendf(Socket, "REFUND %s %s %i\n", Username, Item, PriceOverride);
+
+ buf = ReadLine(Socket);
+ responseCode = atoi(buf);
+ switch(responseCode)
+ {
+ case 200:
+ Dispense_ShowUser(Socket, Username); // Show destination account
+ ret = 0;
+ break;
+ case 403:
+ fprintf(stderr, "Refund access is only avaliable to coke members\n");
+ ret = RV_PERMISSIONS;
+ break;
+ case 404:
+ fprintf(stderr, "Unknown user '%s' passed\n", Username);
+ ret = RV_INVALID_USER;
+ break;
+ case 406:
+ fprintf(stderr, "Invalid item '%s' passed\n", Item);
+ ret = RV_BAD_ITEM;
+ break;
+ default:
+ fprintf(stderr, "Unknown response from server %i\n%s\n", responseCode, buf);
+ ret = -1;
+ break;
+ }
+ free(buf);
+ return ret;
+}
/**
* \brief Donate money to the club