From 15038b1c31d2912682dccdc553258f6431952105 Mon Sep 17 00:00:00 2001 From: John Hodge Date: Sun, 6 Oct 2013 14:37:13 +0800 Subject: [PATCH] Client - Cleaned up subcommands --- src/client/main.c | 1064 ++++++++++++++++++++++++--------------------- 1 file changed, 575 insertions(+), 489 deletions(-) diff --git a/src/client/main.c b/src/client/main.c index 1f4485d..83d2bde 100644 --- a/src/client/main.c +++ b/src/client/main.c @@ -29,6 +29,7 @@ // === PROTOTYPES === void ShowUsage(void); int main(int argc, char *argv[]); + int ParseArguments(int argc, char *argv[]); // --- Coke Server Communication --- // --- Helpers --- char *trim(char *string); @@ -155,551 +156,490 @@ void ShowUsage(void) ); } -int main(int argc, char *argv[]) +// +// `dispense finger` +// - Display coke@ucc.gu.uwa.edu.au finger output +// +int subcommand_finger(void) { - int sock; - int i, ret = 0; - char buffer[BUFSIZ]; - - gsTextArgs[0] = ""; - - // -- Create regular expressions - // > Code Type Count ... - CompileRegex(&gArrayRegex, "^([0-9]{3})\\s+([A-Za-z]+)\\s+([0-9]+)", REG_EXTENDED); // - // > Code Type Ident Status Price Desc - CompileRegex(&gItemRegex, "^([0-9]{3})\\s+([A-Za-z]+)\\s+([A-Za-z]+):([0-9]+)\\s+(avail|sold|error)\\s+([0-9]+)\\s+(.+)$", REG_EXTENDED); - // > Code 'SALT' salt - CompileRegex(&gSaltRegex, "^([0-9]{3})\\s+([A-Za-z]+)\\s+(.+)$", REG_EXTENDED); - // > Code 'User' Username Balance Flags - CompileRegex(&gUserInfoRegex, "^([0-9]{3})\\s+([A-Za-z]+)\\s+([^ ]+)\\s+(-?[0-9]+)\\s+(.+)$", REG_EXTENDED); - // > Item Ident - CompileRegex(&gUserItemIdentRegex, "^([A-Za-z]+):([0-9]+)$", REG_EXTENDED); + // Connect to server + int sock = OpenConnection(gsDispenseServer, giDispensePort); + if( sock < 0 ) return RV_SOCKET_ERROR; - // Parse Arguments - for( i = 1; i < argc; i ++ ) - { - char *arg = argv[i]; - - if( arg[0] == '-' ) - { - switch(arg[1]) - { - case 'h': - case '?': - ShowUsage(); - return 0; - - case 'c': - if( i > 2 && strcmp(argv[i-1], "type") == 0 ) - goto _default; - if( i + 1 >= argc ) { - fprintf(stderr, "%s: -c takes an argument\n", argv[0]); - ShowUsage(); - return -1; - } - giDispenseCount = atoi(argv[++i]); - if( giDispenseCount < 1 || giDispenseCount > DISPENSE_MULTIPLE_MAX ) { - fprintf(stderr, "Sorry, only 1-20 can be passed to -c (safety)\n"); - return -1; - } - - break ; - - case 'm': // Minimum balance - if( i + 1 >= argc ) { - fprintf(stderr, "%s: -m takes an argument\n", argv[0]); - ShowUsage(); - return RV_ARGUMENTS; - } - giMinimumBalance = atoi(argv[++i]); - break; - case 'M': // Maximum balance - if( i + 1 >= argc ) { - fprintf(stderr, "%s: -M takes an argument\n", argv[0]); - ShowUsage(); - return RV_ARGUMENTS; - } - giMaximumBalance = atoi(argv[++i]); - break; - - case 'u': // Override User - if( i + 1 >= argc ) { - fprintf(stderr, "%s: -u takes an argument\n", argv[0]); - ShowUsage(); - return RV_ARGUMENTS; - } - gsEffectiveUser = argv[++i]; - break; - - case 'H': // Override remote host - if( i + 1 >= argc ) { - fprintf(stderr, "%s: -H takes an argument\n", argv[0]); - ShowUsage(); - return RV_ARGUMENTS; - } - gsDispenseServer = argv[++i]; - break; - case 'P': // Override remote port - if( i + 1 >= argc ) { - fprintf(stderr, "%s: -P takes an argument\n", argv[0]); - ShowUsage(); - return RV_ARGUMENTS; - } - giDispensePort = atoi(argv[++i]); - break; - - // Set slot name/price - case 's': - if( giTextArgc != 0 ) { - fprintf(stderr, "%s: -s must appear before other arguments\n", argv[0]); - ShowUsage(); - return RV_ARGUMENTS; - } - gsTextArgs[0] = "slot"; // HACK!! - giTextArgc ++; - break; - - case 'G': // Don't use GUI - giUIMode = UI_MODE_BASIC; - break; - case 'D': // Drinks only - giUIMode = UI_MODE_DRINKSONLY; - break; - case 'n': // Dry Run / read-only - gbDryRun = 1; - break; - case '-': - if( strcmp(argv[i], "--help") == 0 ) { - ShowUsage(); - return 0; - } - else if( strcmp(argv[i], "--dry-run") == 0 ) { - gbDryRun = 1; - } - else if( strcmp(argv[i], "--drinks-only") == 0 ) { - giUIMode = UI_MODE_DRINKSONLY; - } - else if( strcmp(argv[i], "--can-select-all") == 0 ) { - gbDisallowSelectWithoutBalance = 0; - } - else { - fprintf(stderr, "%s: Unknown switch '%s'\n", argv[0], argv[i]); - ShowUsage(); - return RV_ARGUMENTS; - } - break; - default: _default: - // The first argument is not allowed to begin with 'i' - // (catches most bad flags) - if( giTextArgc == 0 ) { - fprintf(stderr, "%s: Unknown switch '%s'\n", argv[0], argv[i]); - ShowUsage(); - return RV_ARGUMENTS; - } - if( giTextArgc == MAX_TXT_ARGS ) - { - fprintf(stderr, "ERROR: Too many arguments\n"); - return RV_ARGUMENTS; - } - gsTextArgs[giTextArgc++] = argv[i]; - break; - } + // Get items + PopulateItemList(sock); - continue; - } + printf("The UCC Coke machine.\n\n"); - if( giTextArgc == MAX_TXT_ARGS ) + // Only get coke slot statuses + for( int i = 0; i <= 6; i ++ ) + { + const char *status; + switch(gaItems[i].Status) { - fprintf(stderr, "ERROR: Too many arguments\n"); - return RV_ARGUMENTS; + case 0: status = "Avail"; break; + case 1: status = "Sold "; break; + default: + status = "Error"; + break; } - - gsTextArgs[giTextArgc++] = argv[i]; - + printf("%i - %s %3i %s\n", gaItems[i].ID, status, gaItems[i].Price, gaItems[i].Desc); } - // - // `dispense finger` - // - - if( strcmp(gsTextArgs[0], "finger") == 0 ) - { - // Connect to server - sock = OpenConnection(gsDispenseServer, giDispensePort); - if( sock < 0 ) return RV_SOCKET_ERROR; - - // Get items - PopulateItemList(sock); + printf("\nMay your pink fish bing into the distance.\n"); - printf("The UCC Coke machine.\n\n"); - - // Only get coke slot statuses - for( i = 0; i <= 6; i ++ ) - { - const char *status; - switch(gaItems[i].Status) - { - case 0: status = "Avail"; break; - case 1: status = "Sold "; break; - default: - status = "Error"; - break; - } - printf("%i - %s %3i %s\n", gaItems[i].ID, status, gaItems[i].Price, gaItems[i].Desc); - } - - printf("\nMay your pink fish bing into the distance.\n"); + return 0; +} - return 0; - } +// +// `dispense acct` +// - Display/manipulate account balances +// +int subcommand_acct(void) +{ + int ret; - // - // `dispense acct` - // - - if( strcmp(gsTextArgs[0], "acct") == 0 ) - { - // Connect to server - sock = OpenConnection(gsDispenseServer, giDispensePort); - if( sock < 0 ) return RV_SOCKET_ERROR; - // List accounts? - if( giTextArgc == 1 ) { - ret = Dispense_EnumUsers(sock); - close(sock); - return ret; - } - - // gsTextArgs[1]: Username - - // Alter account? - if( giTextArgc != 2 ) - { - if( giTextArgc != 4 ) { - fprintf(stderr, "`dispense acct` requires a reason\n"); - ShowUsage(); - return RV_ARGUMENTS; - } - - // Authentication required - ret = Authenticate(sock); - if(ret) return ret; - - // gsTextArgs[1]: Username - // gsTextArgs[2]: Ammount - // gsTextArgs[3]: Reason - char *tmp = NULL; - long int balance = strtol(gsTextArgs[2]+(gsTextArgs[2][0] == '='), &tmp, 10); - if(!tmp || *tmp != '\0') { - fprintf(stderr, "dispense acct: Value must be a decimal number of cents\n"); - return RV_ARGUMENTS; - } - - if( gsTextArgs[2][0] == '=' ) { - // Set balance - ret = Dispense_SetBalance(sock, gsTextArgs[1], balance, gsTextArgs[3]); - } - else { - // Alter balance - ret = Dispense_AlterBalance(sock, gsTextArgs[1], balance, gsTextArgs[3]); - } - } - // On error, quit - if( ret ) { - close(sock); - return ret; - } - - // Show user information - ret = Dispense_ShowUser(sock, gsTextArgs[1]); - + // Connect to server + int sock = OpenConnection(gsDispenseServer, giDispensePort); + if( sock < 0 ) return RV_SOCKET_ERROR; + // List accounts? + if( giTextArgc == 1 ) { + ret = Dispense_EnumUsers(sock); close(sock); return ret; } - // - // `dispense give` - // - "Here, have some money." - else if( strcmp(gsTextArgs[0], "give") == 0 ) + + // gsTextArgs[1]: Username + + // Alter account? + if( giTextArgc != 2 ) { if( giTextArgc != 4 ) { - fprintf(stderr, "`dispense give` takes three arguments\n"); + fprintf(stderr, "`dispense acct` requires a reason\n"); ShowUsage(); return RV_ARGUMENTS; } - // gsTextArgs[1]: Destination - // gsTextArgs[2]: Ammount - // gsTextArgs[3]: Reason - - // Connect to server - sock = OpenConnection(gsDispenseServer, giDispensePort); - if( sock < 0 ) return RV_SOCKET_ERROR; - - // Authenticate + // Authentication required ret = Authenticate(sock); if(ret) return ret; - - char *tmp = NULL; - int amt = strtol(gsTextArgs[2], &tmp, 10); - if( !tmp || *tmp != '\0' ) { - fprintf(stderr, "dispense give: Balance is invalid, must be decimal number of cents"); + + // gsTextArgs[1]: Username + // gsTextArgs[2]: Ammount + // gsTextArgs[3]: Reason + char *tmp = NULL; + long int balance = strtol(gsTextArgs[2]+(gsTextArgs[2][0] == '='), &tmp, 10); + if(!tmp || *tmp != '\0') { + fprintf(stderr, "dispense acct: Value must be a decimal number of cents\n"); return RV_ARGUMENTS; } - ret = Dispense_Give(sock, gsTextArgs[1], amt, gsTextArgs[3]); - + + if( gsTextArgs[2][0] == '=' ) { + // Set balance + ret = Dispense_SetBalance(sock, gsTextArgs[1], balance, gsTextArgs[3]); + } + else { + // Alter balance + ret = Dispense_AlterBalance(sock, gsTextArgs[1], balance, gsTextArgs[3]); + } + } + // On error, quit + if( ret ) { close(sock); - return ret; } - // - // `dispense user` - // - User administration (Admin Only) - if( strcmp(gsTextArgs[0], "user") == 0 ) + + // Show user information + ret = Dispense_ShowUser(sock, gsTextArgs[1]); + + close(sock); + return ret; +} + +// +// `dispense give` +// - Transfer credit from the current user to another account +// +// "Here, have some money." +// +int subcommand_give(int argc, char *args[]) +{ + int ret; + + if( argc != 3 ) { + fprintf(stderr, "`dispense give` takes three arguments\n"); + ShowUsage(); + return RV_ARGUMENTS; + } + + const char *dst_acct = args[0]; + const char *amt_str = args[1]; + const char *message = args[2]; + + // Connect to server + int sock = OpenConnection(gsDispenseServer, giDispensePort); + if( sock < 0 ) return RV_SOCKET_ERROR; + + // Authenticate + ret = Authenticate(sock); + if(ret) return ret; + + char *end = NULL; + long amt = strtol(amt_str, &end, 10); + if( !end || *end != '\0' ) { + fprintf(stderr, "dispense give: Balance is invalid, must be decimal number of cents"); + return RV_ARGUMENTS; + } + ret = Dispense_Give(sock, dst_acct, amt, message); + + close(sock); + return ret; +} + +// +// `dispense user` +// - User administration (Admin Only) +// +int subcommand_user(int argc, char *args[]) +{ + int ret; + + // Check argument count + if( argc == 0 ) { + fprintf(stderr, "Error: `dispense user` requires arguments\n"); + ShowUsage(); + return RV_ARGUMENTS; + } + + // Connect to server + int sock = OpenConnection(gsDispenseServer, giDispensePort); + if( sock < 0 ) return RV_SOCKET_ERROR; + + // Attempt authentication + ret = Authenticate(sock); + if(ret) return ret; + + // Add new user? + if( strcmp(args[0], "add") == 0 ) { - // Check argument count - if( giTextArgc == 1 ) { - fprintf(stderr, "Error: `dispense user` requires arguments\n"); + if( giTextArgc != 3 ) { + fprintf(stderr, "Error: `dispense user add` requires an argument\n"); ShowUsage(); return RV_ARGUMENTS; } - // Connect to server - sock = OpenConnection(gsDispenseServer, giDispensePort); - if( sock < 0 ) return RV_SOCKET_ERROR; - - // Attempt authentication - ret = Authenticate(sock); - if(ret) return ret; - - // Add new user? - if( strcmp(gsTextArgs[1], "add") == 0 ) - { - if( giTextArgc != 3 ) { - fprintf(stderr, "Error: `dispense user add` requires an argument\n"); - ShowUsage(); - return RV_ARGUMENTS; - } - - ret = Dispense_AddUser(sock, gsTextArgs[2]); - } - // Update a user - else if( strcmp(gsTextArgs[1], "type") == 0 || strcmp(gsTextArgs[1], "flags") == 0 ) - { - if( giTextArgc < 4 || giTextArgc > 5 ) { - fprintf(stderr, "Error: `dispense user type` requires two arguments\n"); - ShowUsage(); - return RV_ARGUMENTS; - } - - if( giTextArgc == 4 ) - ret = Dispense_SetUserType(sock, gsTextArgs[2], gsTextArgs[3], ""); - else - ret = Dispense_SetUserType(sock, gsTextArgs[2], gsTextArgs[3], gsTextArgs[4]); - } - else - { - fprintf(stderr, "Error: Unknown sub-command for `dispense user`\n"); + ret = Dispense_AddUser(sock, args[1]); + } + // Update a user + else if( strcmp(args[0], "type") == 0 || strcmp(args[0], "flags") == 0 ) + { + if( argc < 3 || argc > 4 ) { + fprintf(stderr, "Error: `dispense user type` requires two arguments\n"); ShowUsage(); return RV_ARGUMENTS; } - close(sock); - return ret; + + ret = Dispense_SetUserType(sock, args[1], args[2], (argc == 3 ? "" : args[3])); } - // Donation! - else if( strcmp(gsTextArgs[0], "donate") == 0 ) + else { - // Check argument count - if( giTextArgc != 3 ) { - fprintf(stderr, "Error: `dispense donate` requires two arguments\n"); - ShowUsage(); + fprintf(stderr, "Error: Unknown sub-command for `dispense user`\n"); + ShowUsage(); + return RV_ARGUMENTS; + } + close(sock); + return ret; +} + +// +// `dispense donate` +// - Donate money to the club +// +int subcommand_donate(int argc, char *args[]) +{ + int ret; + + // Check argument count + if( argc != 2 ) { + fprintf(stderr, "Error: `dispense donate` requires two arguments\n"); + ShowUsage(); + return RV_ARGUMENTS; + } + + // Connect to server + int sock = OpenConnection(gsDispenseServer, giDispensePort); + if( sock < 0 ) return RV_SOCKET_ERROR; + + // Attempt authentication + ret = Authenticate(sock); + if(ret) return ret; + + // Do donation + ret = Dispense_Donate(sock, atoi(args[0]), args[1]); + + close(sock); + + return ret; +} + +// +// `dispense refund` +// - Refund a purchased item +// +// "Well excuuuuse me, princess" +// +int subcommand_refund(int argc, char *args[]) +{ + int ret; + + // Check argument count + if( argc != 2 && argc != 3 ) { + fprintf(stderr, "Error: `dispense refund` takes 2 or 3 arguments\n"); + ShowUsage(); + return RV_ARGUMENTS; + } + + // Connect to server + int sock = OpenConnection(gsDispenseServer, giDispensePort); + if(sock < 0) return RV_SOCKET_ERROR; + + // Attempt authentication + ret = Authenticate(sock); + if(ret) return ret; + + int price = 0; + if( argc > 2 ) { + price = atoi(args[2]); + if( price <= 0 ) { + fprintf(stderr, "Error: Override price is invalid (should be > 0)\n"); + return RV_ARGUMENTS; + } + } + + // Username, Item, cost + ret = Dispense_Refund(sock, args[0], args[1], price); + + // TODO: More + close(sock); + return ret; +} + +// +// `dispense iteminfo` +// - Get the state of an item +// +int subcommand_iteminfo(int argc, char *args[]) +{ + int ret; + + // Check argument count + if( argc != 1 ) { + fprintf(stderr, "Error: `dispense iteminfo` takes one argument\n"); + ShowUsage(); + return RV_ARGUMENTS; + } + + char *item_id = args[0]; + + regmatch_t matches[3]; + // Parse item ID + if( RunRegex(&gUserItemIdentRegex, item_id, 3, matches, NULL) != 0 ) { + fprintf(stderr, "Error: Invalid item ID passed (: expected)\n"); + return RV_ARGUMENTS; + } + char *type = item_id + matches[1].rm_so; + item_id[ matches[1].rm_eo ] = '\0'; + int id = atoi( item_id + matches[2].rm_so ); + + int sock = OpenConnection(gsDispenseServer, giDispensePort); + if( sock < 0 ) return RV_SOCKET_ERROR; + + ret = Dispense_ItemInfo(sock, type, id); + close(sock); + return ret; +} + +// +// `dispense slot` +// - Update the name/price of an item +// +int subcommand_slot(int argc, char *args[]) +{ + int ret; + + // Check arguments + if( argc != 3 ) { + fprintf(stderr, "Error: `dispense slot` takes three arguments\n"); + ShowUsage(); + return RV_ARGUMENTS; + } + char *slot_id = args[0]; + char *price_str = args[1]; + char *newname = args[2]; + + // Parse arguments + regmatch_t matches[3]; + if( RunRegex(&gUserItemIdentRegex, slot_id, 3, matches, NULL) != 0 ) { + fprintf(stderr, "Error: Invalid item ID passed (: expected)\n"); + return RV_ARGUMENTS; + } + const char *item_type = slot_id + matches[1].rm_so; + slot_id[ matches[1].rm_eo ] = '\0'; + int item_id = atoi( slot_id + matches[2].rm_so ); + + // - Price + char *end; + int price = strtol( price_str, &end, 0 ); + if( price < 0 || *end != '\0' ) { + fprintf(stderr, "Error: Invalid price passed (must be >= 0)\n"); + return RV_ARGUMENTS; + } + + // -- Sanity + for( char *pos = newname; *pos; pos ++ ) + { + if( !isalnum(*pos) && *pos != ' ' ) { + fprintf(stderr, "Error: You should only have letters, numbers and spaces in an item name\n"); return RV_ARGUMENTS; } - - // Connect to server - sock = OpenConnection(gsDispenseServer, giDispensePort); - if( sock < 0 ) return RV_SOCKET_ERROR; - - // Attempt authentication - ret = Authenticate(sock); - if(ret) return ret; - - // Do donation - ret = Dispense_Donate(sock, atoi(gsTextArgs[1]), gsTextArgs[2]); - - close(sock); + } + + // Connect & Authenticate + int sock = OpenConnection(gsDispenseServer, giDispensePort); + if( sock < 0 ) return RV_SOCKET_ERROR; + + ret = Authenticate(sock); + if(ret) return ret; + + // Update the slot + ret = Dispense_SetItem(sock, item_type, item_id, price, newname); + + close(sock); + return ret; +} - return ret; +// +// `dispense pincheck` +// - Validate a user's pin (used by the snack machine) +// +int subcommand_pincheck(int argc, char *args[]) +{ + int ret; + + if( argc < 1 || argc > 2 ) { + fprintf(stderr, "Error: `dispense pincheck` takes one/two arguments\n"); + ShowUsage(); + return RV_ARGUMENTS; + } + + struct passwd *pwd = getpwuid( getuid() ); + gsUserName = strdup(pwd->pw_name); + + const char *pin = args[0]; + const char *user = (argc > 1 ? args[1] : gsUserName); + + int sock = OpenConnection(gsDispenseServer, giDispensePort); + if( sock < 0 ) return RV_SOCKET_ERROR; + + ret = Authenticate(sock); + if(ret) return ret; + + ret = DispenseCheckPin(sock, user, pin); + + close(sock); + return ret; +} + +// +// `dispense pinset` +// - Set the pin of the current account +// +int subcommand_pinset(int argc, char *args[]) +{ + int ret; + + if( argc != 1 ) { + fprintf(stderr, "Error: `dispense pinset` takes one argument\n"); + ShowUsage(); + return RV_ARGUMENTS; } - // Refund an item - else if( strcmp(gsTextArgs[0], "refund") == 0 ) - { - int price = 0; - // Check argument count - if( giTextArgc != 3 && giTextArgc != 4 ) { - fprintf(stderr, "Error: `dispense refund` takes 2 or 3 arguments\n"); - ShowUsage(); - return RV_ARGUMENTS; - } - // Connect to server - sock = OpenConnection(gsDispenseServer, giDispensePort); - if(sock < 0) return RV_SOCKET_ERROR; + const char *pin = args[0]; + + int sock = OpenConnection(gsDispenseServer, giDispensePort); + if( sock < 0 ) return RV_SOCKET_ERROR; - // Attempt authentication - ret = Authenticate(sock); - if(ret) return ret; + ret = Authenticate(sock); + if(ret) return ret; - if( giTextArgc == 4 ) { - price = atoi(gsTextArgs[3]); - if( price <= 0 ) { - fprintf(stderr, "Error: Override price is invalid (should be > 0)\n"); - return RV_ARGUMENTS; - } - } + ret = DispenseSetPin(sock, pin); + + close(sock); + return ret; +} - // Username, Item, cost - ret = Dispense_Refund(sock, gsTextArgs[1], gsTextArgs[2], price); +int main(int argc, char *argv[]) +{ + int i, ret = 0; + char buffer[BUFSIZ]; + + gsTextArgs[0] = ""; - // TODO: More - close(sock); - return ret; - } - // Query an item price - else if( strcmp(gsTextArgs[0], "iteminfo") == 0 ) - { - regmatch_t matches[3]; - char *type; - int id; - // Check argument count - if( giTextArgc != 2 ) { - fprintf(stderr, "Error: `dispense iteminfo` requires an argument\n"); - ShowUsage(); - return RV_ARGUMENTS; - } - // Parse item ID - if( RunRegex(&gUserItemIdentRegex, gsTextArgs[1], 3, matches, NULL) != 0 ) { - fprintf(stderr, "Error: Invalid item ID passed (: expected)\n"); - return RV_ARGUMENTS; - } - type = gsTextArgs[1] + matches[1].rm_so; - gsTextArgs[1][ matches[1].rm_eo ] = '\0'; - id = atoi( gsTextArgs[1] + matches[2].rm_so ); + // -- Create regular expressions + // > Code Type Count ... + CompileRegex(&gArrayRegex, "^([0-9]{3})\\s+([A-Za-z]+)\\s+([0-9]+)", REG_EXTENDED); // + // > Code Type Ident Status Price Desc + CompileRegex(&gItemRegex, "^([0-9]{3})\\s+([A-Za-z]+)\\s+([A-Za-z]+):([0-9]+)\\s+(avail|sold|error)\\s+([0-9]+)\\s+(.+)$", REG_EXTENDED); + // > Code 'SALT' salt + CompileRegex(&gSaltRegex, "^([0-9]{3})\\s+([A-Za-z]+)\\s+(.+)$", REG_EXTENDED); + // > Code 'User' Username Balance Flags + CompileRegex(&gUserInfoRegex, "^([0-9]{3})\\s+([A-Za-z]+)\\s+([^ ]+)\\s+(-?[0-9]+)\\s+(.+)$", REG_EXTENDED); + // > Item Ident + CompileRegex(&gUserItemIdentRegex, "^([A-Za-z]+):([0-9]+)$", REG_EXTENDED); - sock = OpenConnection(gsDispenseServer, giDispensePort); - if( sock < 0 ) return RV_SOCKET_ERROR; - - ret = Dispense_ItemInfo(sock, type, id); - close(sock); + // Parse Arguments + ret = ParseArguments(argc, argv); + if( ret ) return ret; + + // Sub-commands + if( strcmp(gsTextArgs[0], "finger") == 0 ) { + return subcommand_finger(); } - // Set slot - else if( strcmp(gsTextArgs[0], "slot") == 0 ) - { - regmatch_t matches[3]; - char *item_type, *newname; - int item_id, price; - - // Check arguments - if( giTextArgc != 4 ) { - fprintf(stderr, "Error: `dispense slot` takes three arguments\n"); - ShowUsage(); - return RV_ARGUMENTS; - } - - // Parse arguments - if( RunRegex(&gUserItemIdentRegex, gsTextArgs[1], 3, matches, NULL) != 0 ) { - fprintf(stderr, "Error: Invalid item ID passed (: expected)\n"); - return RV_ARGUMENTS; - } - item_type = gsTextArgs[1] + matches[1].rm_so; - gsTextArgs[1][ matches[1].rm_eo ] = '\0'; - item_id = atoi( gsTextArgs[1] + matches[2].rm_so ); - - // - Price - price = atoi( gsTextArgs[2] ); - if( price <= 0 && gsTextArgs[2][0] != '0' ) { - fprintf(stderr, "Error: Invalid price passed (must be >= 0)\n"); - return RV_ARGUMENTS; - } - - // - New name - newname = gsTextArgs[3]; - // -- Sanity - { - char *pos; - for( pos = newname; *pos; pos ++ ) - { - if( !isalnum(*pos) && *pos != ' ' ) { - fprintf(stderr, "Error: You should only have letters, numbers and spaces in an item name\n"); - return RV_ARGUMENTS; - } - } - } - - // Connect & Authenticate - sock = OpenConnection(gsDispenseServer, giDispensePort); - if( sock < 0 ) return RV_SOCKET_ERROR; - ret = Authenticate(sock); - if(ret) return ret; - // Update the slot - ret = Dispense_SetItem(sock, item_type, item_id, price, newname); - - close(sock); - return ret; + else if( strcmp(gsTextArgs[0], "acct") == 0 ) { + return subcommand_acct(); } - // Check a user's pin - else if(strcmp(gsTextArgs[0], "pincheck") == 0) - { - if( giTextArgc < 2 || giTextArgc > 3 ) { - fprintf(stderr, "Error: `dispense pincheck` takes one/two arguments\n"); - ShowUsage(); - return RV_ARGUMENTS; - } - struct passwd *pwd = getpwuid( getuid() ); - gsUserName = strdup(pwd->pw_name); - - const char *pin = gsTextArgs[1]; - const char *user = gsUserName; - if( giTextArgc == 3 ) - user = gsTextArgs[2]; - - - sock = OpenConnection(gsDispenseServer, giDispensePort); - if( sock < 0 ) return RV_SOCKET_ERROR; - ret = Authenticate(sock); - if(ret) return ret; - - ret = DispenseCheckPin(sock, user, pin); - - close(sock); - return ret; + else if( strcmp(gsTextArgs[0], "give") == 0 ) { + return subcommand_give(giTextArgc-1, gsTextArgs+1); } - // Update 'your' pin - else if(strcmp(gsTextArgs[0], "pinset") == 0) - { - if( giTextArgc != 2 ) { - fprintf(stderr, "Error: `dispense pinset` takes one argument\n"); - ShowUsage(); - return RV_ARGUMENTS; - } - - const char *pin = gsTextArgs[1]; - - sock = OpenConnection(gsDispenseServer, giDispensePort); - if( sock < 0 ) return RV_SOCKET_ERROR; - ret = Authenticate(sock); - if(ret) return ret; - - ret = DispenseSetPin(sock, pin); - - close(sock); - return ret; + else if( strcmp(gsTextArgs[0], "user") == 0 ) { + return subcommand_user(giTextArgc-1, gsTextArgs+1); } - // Item name / pattern - else - { + else if( strcmp(gsTextArgs[0], "donate") == 0 ) { + return subcommand_donate(giTextArgc-1, gsTextArgs+1); + } + else if( strcmp(gsTextArgs[0], "refund") == 0 ) { + return subcommand_refund(giTextArgc-1, gsTextArgs+1); + } + else if( strcmp(gsTextArgs[0], "iteminfo") == 0 ) { + return subcommand_iteminfo(giTextArgc-1, gsTextArgs+1); + } + else if( strcmp(gsTextArgs[0], "slot") == 0 ) { + return subcommand_slot(giTextArgc-1, gsTextArgs+1); + } + else if(strcmp(gsTextArgs[0], "pincheck") == 0) { + return subcommand_pincheck(giTextArgc-1, gsTextArgs+1); + } + else if(strcmp(gsTextArgs[0], "pinset") == 0) { + return subcommand_pinset(giTextArgc-1, gsTextArgs+1); + } + else { + // Item name / pattern gsItemPattern = gsTextArgs[0]; } // Connect to server - sock = OpenConnection(gsDispenseServer, giDispensePort); + int sock = OpenConnection(gsDispenseServer, giDispensePort); if( sock < 0 ) return RV_SOCKET_ERROR; // Get the user's balance @@ -875,6 +815,152 @@ int main(int argc, char *argv[]) return ret; } +int ParseArguments(int argc, char *argv[]) +{ + for( int i = 1; i < argc; i ++ ) + { + char *arg = argv[i]; + + if( arg[0] == '-' ) + { + switch(arg[1]) + { + case 'h': + case '?': + ShowUsage(); + return 0; + + case 'c': + if( i > 2 && strcmp(argv[i-1], "type") == 0 ) + goto _default; + if( i + 1 >= argc ) { + fprintf(stderr, "%s: -c takes an argument\n", argv[0]); + ShowUsage(); + return -1; + } + giDispenseCount = atoi(argv[++i]); + if( giDispenseCount < 1 || giDispenseCount > DISPENSE_MULTIPLE_MAX ) { + fprintf(stderr, "Sorry, only 1-20 can be passed to -c (safety)\n"); + return -1; + } + + break ; + + case 'm': // Minimum balance + if( i + 1 >= argc ) { + fprintf(stderr, "%s: -m takes an argument\n", argv[0]); + ShowUsage(); + return RV_ARGUMENTS; + } + giMinimumBalance = atoi(argv[++i]); + break; + case 'M': // Maximum balance + if( i + 1 >= argc ) { + fprintf(stderr, "%s: -M takes an argument\n", argv[0]); + ShowUsage(); + return RV_ARGUMENTS; + } + giMaximumBalance = atoi(argv[++i]); + break; + + case 'u': // Override User + if( i + 1 >= argc ) { + fprintf(stderr, "%s: -u takes an argument\n", argv[0]); + ShowUsage(); + return RV_ARGUMENTS; + } + gsEffectiveUser = argv[++i]; + break; + + case 'H': // Override remote host + if( i + 1 >= argc ) { + fprintf(stderr, "%s: -H takes an argument\n", argv[0]); + ShowUsage(); + return RV_ARGUMENTS; + } + gsDispenseServer = argv[++i]; + break; + case 'P': // Override remote port + if( i + 1 >= argc ) { + fprintf(stderr, "%s: -P takes an argument\n", argv[0]); + ShowUsage(); + return RV_ARGUMENTS; + } + giDispensePort = atoi(argv[++i]); + break; + + // Set slot name/price + case 's': + if( giTextArgc != 0 ) { + fprintf(stderr, "%s: -s must appear before other arguments\n", argv[0]); + ShowUsage(); + return RV_ARGUMENTS; + } + gsTextArgs[0] = "slot"; // HACK!! + giTextArgc ++; + break; + + case 'G': // Don't use GUI + giUIMode = UI_MODE_BASIC; + break; + case 'D': // Drinks only + giUIMode = UI_MODE_DRINKSONLY; + break; + case 'n': // Dry Run / read-only + gbDryRun = 1; + break; + case '-': + if( strcmp(argv[i], "--help") == 0 ) { + ShowUsage(); + return 0; + } + else if( strcmp(argv[i], "--dry-run") == 0 ) { + gbDryRun = 1; + } + else if( strcmp(argv[i], "--drinks-only") == 0 ) { + giUIMode = UI_MODE_DRINKSONLY; + } + else if( strcmp(argv[i], "--can-select-all") == 0 ) { + gbDisallowSelectWithoutBalance = 0; + } + else { + fprintf(stderr, "%s: Unknown switch '%s'\n", argv[0], argv[i]); + ShowUsage(); + return RV_ARGUMENTS; + } + break; + default: _default: + // The first argument is not allowed to begin with 'i' + // (catches most bad flags) + if( giTextArgc == 0 ) { + fprintf(stderr, "%s: Unknown switch '%s'\n", argv[0], argv[i]); + ShowUsage(); + return RV_ARGUMENTS; + } + if( giTextArgc == MAX_TXT_ARGS ) + { + fprintf(stderr, "ERROR: Too many arguments\n"); + return RV_ARGUMENTS; + } + gsTextArgs[giTextArgc++] = argv[i]; + break; + } + + continue; + } + + if( giTextArgc == MAX_TXT_ARGS ) + { + fprintf(stderr, "ERROR: Too many arguments\n"); + return RV_ARGUMENTS; + } + + gsTextArgs[giTextArgc++] = argv[i]; + + } + return 0; +} + // --------------- // --- Helpers --- // --------------- -- 2.20.1