X-Git-Url: https://git.ucc.asn.au/?p=tpg%2Fopendispense2.git;a=blobdiff_plain;f=src%2Fclient%2Fmain.c;h=4d33de10af44cae6436f2b2ca55c81b7ca398dc5;hp=a241f2e93d047de7ca9e0e4f97a95e304c6e0cc8;hb=24654ef0078320798912a273508e37f9ce921ba7;hpb=f66299331a4e635b3d25d3526e3900e67cd1895b diff --git a/src/client/main.c b/src/client/main.c index a241f2e..4d33de1 100644 --- a/src/client/main.c +++ b/src/client/main.c @@ -29,6 +29,8 @@ #define DEBUG_TRACE_SERVER 0 #define USE_AUTOAUTH 1 +#define MAX_TXT_ARGS 5 // Maximum number of textual arguments (including command) + enum eUI_Modes { UI_MODE_BASIC, // Non-NCurses @@ -42,6 +44,7 @@ enum eUI_Modes typedef struct sItem { char *Type; int ID; + int Status; // 0: Availiable, 1: Sold out, -1: Error char *Desc; int Price; } tItem; @@ -51,7 +54,7 @@ typedef struct sItem { void ShowUsage(void); // --- GUI --- int ShowNCursesUI(void); - int ShowItemAt(int Row, int Col, int Width, int Index); + int ShowItemAt(int Row, int Col, int Width, int Index, int bHilighted); void PrintAlign(int Row, int Col, int Width, const char *Left, char Pad1, const char *Mid, char Pad2, const char *Right, ...); // --- Coke Server Communication --- int OpenConnection(const char *Host, int Port); @@ -99,14 +102,18 @@ char *gsUserFlags; //!< User's flag set int main(int argc, char *argv[]) { int sock; - int i; + int i, ret = 0; char buffer[BUFSIZ]; + char *text_args[MAX_TXT_ARGS]; // Non-flag arguments + int text_argc = 0; + text_args[0] = ""; + // -- Create regular expressions // > Code Type Count ... CompileRegex(&gArrayRegex, "^([0-9]{3})\\s+([A-Za-z]+)\\s+([0-9]+)", REG_EXTENDED); // - // > Code Type Ident Price Desc - CompileRegex(&gItemRegex, "^([0-9]{3})\\s+([A-Za-z]+)\\s+([A-Za-z]+):([0-9]+)\\s+([0-9]+)\\s+(.+)$", 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 @@ -175,171 +182,184 @@ int main(int argc, char *argv[]) case 'n': // Dry Run / read-only gbDryRun = 1; break; + case '0': case '1': + case '2': case '3': + case '4': case '5': + case '6': case '7': + case '8': case '9': + if( text_argc + 1 == MAX_TXT_ARGS ) + { + fprintf(stderr, "ERROR: Too many arguments\n"); + return 1; + } + text_args[text_argc++] = argv[i]; + break; } continue; } - - // - // `dispense acct` - // - - if( strcmp(arg, "acct") == 0 ) + + if( text_argc + 1 == MAX_TXT_ARGS ) { - // Connect to server - sock = OpenConnection(gsDispenseServer, giDispensePort); - if( sock < 0 ) return -1; + fprintf(stderr, "ERROR: Too many arguments\n"); + return 1; + } + + text_args[text_argc++] = argv[i]; + + } + // + // `dispense acct` + // - + if( strcmp(text_args[0], "acct") == 0 ) + { + // Connect to server + sock = OpenConnection(gsDispenseServer, giDispensePort); + if( sock < 0 ) return -1; // List accounts? - if( i + 1 == argc ) { - Dispense_EnumUsers(sock); - return 0; - } - - // argv[i+1]: Username - - // Alter account? - if( i + 2 < argc ) - { - if( i + 3 >= argc ) { - fprintf(stderr, "Error: `dispense acct' needs a reason\n"); - exit(1); - } - - // Authentication required - if( Authenticate(sock) ) - return -1; - - // argv[i+1]: Username - // argv[i+2]: Ammount - // argv[i+3]: Reason - - if( argv[i+2][0] == '=' ) { - // Set balance - if( argv[i+2][1] != '0' && atoi(&argv[i+2][1]) == 0 ) { - fprintf(stderr, "Error: Invalid balance to be set\n"); - exit(1); - } - - Dispense_SetBalance(sock, argv[i+1], atoi(argv[i+2]+1), argv[i+3]); - } - else { - // Alter balance - Dispense_AlterBalance(sock, argv[i+1], atoi(argv[i+2]), argv[i+3]); - } - } - - // Show user information - Dispense_ShowUser(sock, argv[i+1]); - - close(sock); + if( text_argc == 1 ) { + Dispense_EnumUsers(sock); return 0; } - // - // `dispense give` - // - "Here, have some money." - if( strcmp(arg, "give") == 0 ) - { - if( i + 3 >= argc ) { - fprintf(stderr, "`dispense give` takes three arguments\n"); - ShowUsage(); - return -1; - } - - // argv[i+1]: Destination - // argv[i+2]: Ammount - // argv[i+3]: Reason - // Connect to server - sock = OpenConnection(gsDispenseServer, giDispensePort); - if( sock < 0 ) return -1; - - // Authenticate - if( Authenticate(sock) ) - return -1; - - Dispense_Give(sock, argv[i+1], atoi(argv[i+2]), argv[i+3]); - return 0; - } - // - // `dispense user` - // - User administration (Admin Only) - if( strcmp(arg, "user") == 0 ) + // text_args[1]: Username + + // Alter account? + if( text_argc == 4 ) { - // Check argument count - if( i + 1 >= argc ) { - fprintf(stderr, "Error: `dispense user` requires arguments\n"); - ShowUsage(); - exit(1); - } - - // Connect to server - sock = OpenConnection(gsDispenseServer, giDispensePort); - if( sock < 0 ) return -1; - - // Attempt authentication + // Authentication required if( Authenticate(sock) ) return -1; - // Add new user? - if( strcmp(argv[i+1], "add") == 0 ) - { - if( i + 2 >= argc ) { - fprintf(stderr, "Error: `dispense user add` requires an argument\n"); - ShowUsage(); + // text_args[1]: Username + // text_args[2]: Ammount + // text_args[3]: Reason + + if( text_args[2][0] == '=' ) { + // Set balance + if( text_args[2][1] != '0' && atoi(text_args[2]+1) == 0 ) { + fprintf(stderr, "Error: Invalid balance to be set\n"); exit(1); } - Dispense_AddUser(sock, argv[i+2]); + Dispense_SetBalance(sock, text_args[1], atoi(text_args[2]+1), text_args[3]); } - // Update a user - else if( strcmp(argv[i+1], "type") == 0 ) - { - if( i + 3 >= argc ) { - fprintf(stderr, "Error: `dispense user type` requires two arguments\n"); - ShowUsage(); - exit(1); - } - - Dispense_SetUserType(sock, argv[i+2], argv[i+3]); + else { + // Alter balance + Dispense_AlterBalance(sock, text_args[1], atoi(text_args[2]), text_args[3]); } - else - { - fprintf(stderr, "Error: Unknown sub-command for `dispense user`\n"); + } + + // Show user information + Dispense_ShowUser(sock, text_args[1]); + + close(sock); + return 0; + } + // + // `dispense give` + // - "Here, have some money." + if( strcmp(text_args[0], "give") == 0 ) + { + if( text_argc != 3 ) { + fprintf(stderr, "`dispense give` takes three arguments\n"); + ShowUsage(); + return -1; + } + + // text_args[1]: Destination + // text_args[2]: Ammount + // text_args[3]: Reason + + // Connect to server + sock = OpenConnection(gsDispenseServer, giDispensePort); + if( sock < 0 ) return -1; + + // Authenticate + if( Authenticate(sock) ) + return -1; + + Dispense_Give(sock, text_args[1], atoi(text_args[2]), text_args[3]); + return 0; + } + // + // `dispense user` + // - User administration (Admin Only) + if( strcmp(text_args[0], "user") == 0 ) + { + // Check argument count + if( text_argc == 1 ) { + fprintf(stderr, "Error: `dispense user` requires arguments\n"); + ShowUsage(); + exit(1); + } + + // Connect to server + sock = OpenConnection(gsDispenseServer, giDispensePort); + if( sock < 0 ) return -1; + + // Attempt authentication + if( Authenticate(sock) ) + return -1; + + // Add new user? + if( strcmp(text_args[1], "add") == 0 ) + { + if( text_argc != 3 ) { + fprintf(stderr, "Error: `dispense user add` requires an argument\n"); ShowUsage(); exit(1); } - return 0; + + Dispense_AddUser(sock, text_args[2]); } - - // Donation! - if( strcmp(arg, "donate") == 0 ) + // Update a user + else if( strcmp(text_args[1], "type") == 0 ) { - // Check argument count - if( i + 2 >= argc ) { - fprintf(stderr, "Error: `dispense donate` requires two arguments\n"); + if( text_argc != 4 ) { + fprintf(stderr, "Error: `dispense user type` requires two arguments\n"); ShowUsage(); exit(1); } - // Connect to server - sock = OpenConnection(gsDispenseServer, giDispensePort); - if( sock < 0 ) return -1; - - // Attempt authentication - if( Authenticate(sock) ) - return -1; - - // Do donation - Dispense_Donate(sock, atoi(argv[i+1]), argv[i+1]); - - return 0; + Dispense_SetUserType(sock, text_args[2], text_args[3]); } - - else { - // Item name / pattern - gsItemPattern = arg; - break; + else + { + fprintf(stderr, "Error: Unknown sub-command for `dispense user`\n"); + ShowUsage(); + exit(1); } + return 0; + } + + // Donation! + if( strcmp(text_args[0], "donate") == 0 ) + { + // Check argument count + if( text_argc != 3 ) { + fprintf(stderr, "Error: `dispense donate` requires two arguments\n"); + ShowUsage(); + exit(1); + } + + // Connect to server + sock = OpenConnection(gsDispenseServer, giDispensePort); + if( sock < 0 ) return -1; + + // Attempt authentication + if( Authenticate(sock) ) + return -1; + + // Do donation + Dispense_Donate(sock, atoi(text_args[1]), text_args[2]); + + return 0; + } + else { + // Item name / pattern + gsItemPattern = text_args[0]; } // Connect to server @@ -355,7 +375,7 @@ int main(int argc, char *argv[]) // Disconnect from server close(sock); - if( gsItemPattern ) + if( gsItemPattern && gsItemPattern[0] ) { regmatch_t matches[3]; // Door (hard coded) @@ -497,11 +517,11 @@ int main(int argc, char *argv[]) Dispense_ItemInfo(sock, gaItems[i].Type, gaItems[i].ID); Authenticate(sock); - DispenseItem(sock, gaItems[i].Type, gaItems[i].ID); + ret = DispenseItem(sock, gaItems[i].Type, gaItems[i].ID); close(sock); } - return 0; + return ret; } void ShowUsage(void) @@ -564,7 +584,7 @@ int ShowNCursesUI(void) int itemCount; int maxItemIndex; int itemBase = 0; - int currentItem = 0; + int currentItem; int ret = -2; // -2: Used for marking "no return yet" char balance_str[5+1+2+1]; // If $9999.99 is too little, something's wrong @@ -588,14 +608,15 @@ int ShowNCursesUI(void) raw(); noecho(); // Get max index - maxItemIndex = ShowItemAt(0, 0, 0, -1); + maxItemIndex = ShowItemAt(0, 0, 0, -1, 0); // Get item count per screen // - 6: randomly chosen (Need at least 3) itemCount = LINES - 6; if( itemCount > maxItemIndex ) itemCount = maxItemIndex; // Get first index - while( ShowItemAt(0, 0, 0, currentItem) == -1 ) + currentItem = 0; + while( ShowItemAt(0, 0, 0, currentItem, 0) == -1 ) currentItem ++; @@ -618,28 +639,27 @@ int ShowNCursesUI(void) int pos = 0; move( yBase + 1 + i, xBase ); + printw("| "); - if( currentItem == itemBase + i ) { - printw("| ->"); - } - else { - printw("| "); - } - pos += 4; + pos += 2; // Check for the '...' row // - Oh god, magic numbers! if( (i == 0 && itemBase > 0) || (i == itemCount - 1 && itemBase < maxItemIndex - itemCount) ) { - printw(" ..."); pos += 6; + printw(" ..."); pos += 8; times = (width - pos) - 1; - //times = width-1 - 8 - 3; while(times--) addch(' '); } // Show an item else { - ShowItemAt( yBase + 1 + i, xBase + pos, (width - pos) - 3, itemBase + i); + ShowItemAt( + yBase + 1 + i, xBase + pos, // Position + (width - pos) - 3, // Width + itemBase + i, // Index + !!(currentItem == itemBase + i) // Hilighted + ); printw(" "); } @@ -688,26 +708,26 @@ int ShowNCursesUI(void) case 'B': currentItem ++; // Skip over spacers - while( ShowItemAt(0, 0, 0, currentItem) == -1 ) + while( ShowItemAt(0, 0, 0, currentItem, 0) == -1 ) currentItem ++; if( currentItem >= maxItemIndex ) { currentItem = 0; // Skip over spacers - while( ShowItemAt(0, 0, 0, currentItem) == -1 ) + while( ShowItemAt(0, 0, 0, currentItem, 0) == -1 ) currentItem ++; } break; case 'A': currentItem --; // Skip over spacers - while( ShowItemAt(0, 0, 0, currentItem) == -1 ) + while( ShowItemAt(0, 0, 0, currentItem, 0) == -1 ) currentItem --; if( currentItem < 0 ) { currentItem = maxItemIndex - 1; // Skip over spacers - while( ShowItemAt(0, 0, 0, currentItem) == -1 ) + while( ShowItemAt(0, 0, 0, currentItem, 0) == -1 ) currentItem --; } break; @@ -726,7 +746,7 @@ int ShowNCursesUI(void) switch(ch) { case '\n': - ret = ShowItemAt(0, 0, 0, currentItem); + ret = ShowItemAt(0, 0, 0, currentItem, 0); break; case 'q': ret = -1; // -1: Return with no dispense @@ -750,11 +770,12 @@ int ShowNCursesUI(void) * \return Dispense index of item * \note Part of the NCurses UI */ -int ShowItemAt(int Row, int Col, int Width, int Index) +int ShowItemAt(int Row, int Col, int Width, int Index, int bHilighted) { int _x, _y, times; char *name = NULL; int price = 0; + int status = -1; switch(giUIMode) { @@ -779,6 +800,7 @@ int ShowItemAt(int Row, int Col, int Width, int Index) { name = gaItems[Index].Desc; price = gaItems[Index].Price; + status = gaItems[Index].Status; break; } Index -= 7; @@ -794,6 +816,7 @@ int ShowItemAt(int Row, int Col, int Width, int Index) Index += 7; name = gaItems[Index].Desc; price = gaItems[Index].Price; + status = gaItems[Index].Status; break; default: return -1; @@ -806,24 +829,48 @@ int ShowItemAt(int Row, int Col, int Width, int Index) if( Index >= 0 ) { - printw("%02i %s", Index, name); + // Show hilight and status + switch( status ) + { + case 0: + if( bHilighted ) + printw("-> "); + else + printw(" "); + break; + case 1: + printw("SLD"); + break; + + default: + case -1: + printw("ERR"); + break; + } + + printw(" %s", name); getyx(stdscr, _y, _x); // Assumes max 4 digit prices - times = Width - 4 - (_x - Col); // TODO: Better handling for large prices + times = Width - 5 - (_x - Col); // TODO: Better handling for large prices while(times--) addch(' '); - printw("%4i", price); + + printw(" %4i", price); } else { printw("-- %s", name); getyx(stdscr, _y, _x); - times = Width - 4 - (_x - Col); // TODO: Better handling for large prices + times = Width - 4 - (_x - Col); while(times--) addch(' '); - printw(" --", price); + printw(" "); } } + // If the item isn't availiable for sale, return -1 (so it's skipped) + if( status ) + Index = -1; + return Index; } @@ -916,8 +963,10 @@ int OpenConnection(const char *Host, int Port) fprintf(stderr, "Failed to create socket\n"); return -1; } + +// printf("geteuid() = %i, getuid() = %i\n", geteuid(), getuid()); - if( geteuid() == 0 ) + if( geteuid() == 0 || getuid() == 0 ) { int i; struct sockaddr_in localAddr; @@ -925,7 +974,7 @@ int OpenConnection(const char *Host, int Port) localAddr.sin_family = AF_INET; // IPv4 // Loop through all the top ports until one is avaliable - for( i = 1001; i < 1024; i ++) + for( i = 512; i < 1024; i ++) { localAddr.sin_port = htons(i); // IPv4 // Attempt to bind to low port for autoauth @@ -934,6 +983,8 @@ int OpenConnection(const char *Host, int Port) } if( i == 1024 ) printf("Warning: AUTOAUTH unavaliable\n"); +// else +// printf("Bound to 0.0.0.0:%i\n", i); } if( connect(sock, (struct sockaddr *) &serverAddr, sizeof(serverAddr)) < 0 ) { @@ -1141,6 +1192,72 @@ int GetUserBalance(int Socket) return 0; } +/** + * \brief Read an item info response from the server + * \param Dest Destination for the read item (strings will be on the heap) + */ +int ReadItemInfo(int Socket, tItem *Dest) +{ + char *buf; + int responseCode; + + regmatch_t matches[8]; + char *statusStr; + + // Get item info + buf = ReadLine(Socket); + responseCode = atoi(buf); + + switch(responseCode) + { + case 202: break; + + case 406: + printf("Bad item name\n"); + free(buf); + return 1; + + default: + fprintf(stderr, "Unknown response from dispense server (Response Code %i)\n%s", responseCode, buf); + exit(-1); + } + + RunRegex(&gItemRegex, buf, 8, matches, "Malformed server response"); + + buf[ matches[3].rm_eo ] = '\0'; + buf[ matches[5].rm_eo ] = '\0'; + buf[ matches[7].rm_eo ] = '\0'; + + statusStr = &buf[ matches[5].rm_so ]; + + Dest->ID = atoi( buf + matches[4].rm_so ); + + if( strcmp(statusStr, "avail") == 0 ) + Dest->Status = 0; + else if( strcmp(statusStr, "sold") == 0 ) + Dest->Status = 1; + else if( strcmp(statusStr, "error") == 0 ) + Dest->Status = -1; + else { + fprintf(stderr, "Unknown response from dispense server (status '%s')\n", + statusStr); + return 1; + } + Dest->Price = atoi( buf + matches[6].rm_so ); + + // Hack a little to reduce heap fragmentation + { + char tmpType[strlen(buf + matches[3].rm_so) + 1]; + char tmpDesc[strlen(buf + matches[7].rm_so) + 1]; + strcpy(tmpType, buf + matches[3].rm_so); + strcpy(tmpDesc, buf + matches[7].rm_so); + free(buf); + Dest->Type = strdup( tmpType ); + Dest->Desc = strdup( tmpDesc ); + } + + return 0; +} /** * \brief Fill the item information structure @@ -1195,27 +1312,7 @@ void PopulateItemList(int Socket) // 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); + ReadItemInfo( Socket, &gaItems[i] ); } // Read end of list @@ -1239,46 +1336,23 @@ void PopulateItemList(int Socket) */ int Dispense_ItemInfo(int Socket, const char *Type, int ID) { - int responseCode; - char *buf; - regmatch_t matches[7]; - char *type, *desc; - int id, price; + tItem item; - // Dispense! + // Query sendf(Socket, "ITEM_INFO %s:%i\n", Type, ID); - buf = ReadLine(Socket); - responseCode = atoi(buf); - switch( responseCode ) + if( ReadItemInfo(Socket, &item) ) { - case 202: - break; - - case 406: - printf("Bad item name\n"); - free(buf); - return 1; - - default: - printf("Unknown response code %i ('%s')\n", responseCode, buf); - free(buf); return -1; } - - RunRegex(&gItemRegex, buf, 7, matches, "Malformed server response"); - - buf[ matches[3].rm_eo ] = '\0'; + printf("%8s:%-2i %2i.%02i %s\n", + item.Type, item.ID, + item.Price/100, item.Price%100, + item.Desc); - type = buf + matches[3].rm_so; buf[matches[3].rm_eo] = '\0'; - id = atoi( buf + matches[4].rm_so ); - price = atoi( buf + matches[5].rm_so ); - desc = buf + matches[6].rm_so; buf[matches[6].rm_eo] = '\0'; - - printf("%8s:%-2i %2i.%02i %s\n", type, id, price/100, price%100, desc); - - free(buf); + free(item.Type); + free(item.Desc); return 0; }