int OpenConnection(const char *Host, int Port);
int Authenticate(int Socket);
void PopulateItemList(int Socket);
- int DispenseItem(int Socket, int ItemID);
+ int Dispense_ItemInfo(int Socket, const char *Type, int ID);
+ int DispenseItem(int Socket, const char *Type, int ID);
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);
tItem *gaItems;
int giNumItems;
-regex_t gArrayRegex, gItemRegex, gSaltRegex, gUserInfoRegex;
+regex_t gArrayRegex, gItemRegex, gSaltRegex, gUserInfoRegex, gUserItemIdentRegex;
int gbIsAuthenticated = 0;
char *gsItemPattern; //!< Item pattern
char *gsEffectiveUser; //!< '-u' Dispense as another user
int gbUseNCurses = 0; //!< '-G' Use the NCurses GUI?
+ int gbDryRun = 0; //!< '-n' Read-only
int giMinimumBalance = INT_MIN; //!< '-m' Minumum balance for `dispense acct`
int giMaximumBalance = INT_MAX; //!< '-M' Maximum balance for `dispense acct`
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);
// Parse Arguments
for( i = 1; i < argc; i ++ )
return 0;
case 'm': // Minimum balance
+ if( i + 1 >= argc ) {
+ fprintf(stderr, "%s: -m takes an argument\n", argv[0]);
+ ShowUsage();
+ }
giMinimumBalance = atoi(argv[++i]);
break;
case 'M': // Maximum balance
+ if( i + 1 >= argc ) {
+ fprintf(stderr, "%s: -M takes an argument\n", argv[0]);
+ ShowUsage();
+ }
giMaximumBalance = atoi(argv[++i]);
break;
case 'u': // Override User
+ if( i + 1 >= argc ) {
+ fprintf(stderr, "%s: -u takes an argument\n", argv[0]);
+ ShowUsage();
+ }
gsEffectiveUser = argv[++i];
break;
+ case 'H': // Override remote host
+ if( i + 1 >= argc ) {
+ fprintf(stderr, "%s: -H takes an argument\n", argv[0]);
+ ShowUsage();
+ }
+ gsDispenseServer = argv[++i];
+ break;
+ case 'P': // Override remote port
+ if( i + 1 >= argc ) {
+ fprintf(stderr, "%s: -P takes an argument\n", argv[0]);
+ ShowUsage();
+ }
+ giDispensePort = atoi(argv[++i]);
+ break;
+
case 'G': // Use GUI
gbUseNCurses = 1;
break;
+ case 'n': // Dry Run / read-only
+ gbDryRun = 1;
+ break;
}
continue;
ShowUsage();
return -1;
}
- // TODO: `dispense give`
// argv[i+1]: Destination
// argv[i+2]: Ammount
if( gsItemPattern )
{
- // TODO: Implement `dispense <name>`
- printf("TODO: Implement `dispense <name>`\n");
- i = -1;
+ regmatch_t matches[3];
+ // Door (hard coded)
+ if( strcmp(gsItemPattern, "door") == 0 )
+ {
+ // Connect, Authenticate, dispense and close
+ sock = OpenConnection(gsDispenseServer, giDispensePort);
+ if( sock < 0 ) return -1;
+ Authenticate(sock);
+ DispenseItem(sock, "door", 0);
+ close(sock);
+ return 0;
+ }
+ // Item id (<type>:<num>)
+ else if( RunRegex(&gUserItemIdentRegex, gsItemPattern, 3, matches, NULL) == 0 )
+ {
+ char *ident;
+ int id;
+
+ // Get and finish ident
+ ident = gsItemPattern + matches[1].rm_so;
+ gsItemPattern[matches[1].rm_eo] = '\0';
+ // Get ID
+ id = atoi( gsItemPattern + matches[2].rm_so );
+
+ // Connect, Authenticate, dispense and close
+ sock = OpenConnection(gsDispenseServer, giDispensePort);
+ if( sock < 0 ) return -1;
+
+ Dispense_ItemInfo(sock, ident, id);
+
+ Authenticate(sock);
+ DispenseItem(sock, ident, id);
+ close(sock);
+ return 0;
+ }
+ // Item number (6 = coke)
+ else if( strcmp(gsItemPattern, "0") == 0 || atoi(gsItemPattern) > 0 )
+ {
+ i = atoi(gsItemPattern);
+ }
+ // Item prefix
+ else
+ {
+ int j;
+ int best = -1;
+ for( i = 0; i < giNumItems; i ++ )
+ {
+ // Prefix match (with case-insensitive match)
+ for( j = 0; gsItemPattern[j]; j ++ )
+ {
+ if( gaItems[i].Desc[j] == gsItemPattern[j] )
+ continue;
+ if( tolower(gaItems[i].Desc[j]) == tolower(gsItemPattern[j]) )
+ continue;
+ break;
+ }
+ // Check if the prefix matched
+ if( gsItemPattern[j] != '\0' )
+ continue;
+
+ // Prefect match
+ if( gaItems[i].Desc[j] == '\0' ) {
+ best = i;
+ break;
+ }
+
+ // Only one match allowed
+ if( best == -1 ) {
+ best = i;
+ }
+ else {
+ // TODO: Allow ambiguous matches?
+ }
+ }
+
+ if( best == -1 )
+ {
+ fprintf(stderr, "No item matches the passed string\n");
+ return 1;
+ }
+
+ i = best;
+ }
}
else if( gbUseNCurses )
{
else
{
// Very basic dispense interface
- for( i = 0; i < giNumItems; i ++ ) {
+ for( i = 0; i < giNumItems; i ++ ) {
printf("%2i %s:%i\t%3i %s\n", i, gaItems[i].Type, gaItems[i].ID,
gaItems[i].Price, gaItems[i].Desc);
}
// Connect, Authenticate, dispense and close
sock = OpenConnection(gsDispenseServer, giDispensePort);
if( sock < 0 ) return -1;
+
+ Dispense_ItemInfo(sock, gaItems[i].Type, gaItems[i].ID);
+
Authenticate(sock);
- DispenseItem(sock, i);
+ DispenseItem(sock, gaItems[i].Type, gaItems[i].ID);
close(sock);
}
free(buf);
}
+
+/**
+ * \brief Get information on an item
+ * \return Boolean Failure
+ */
+int Dispense_ItemInfo(int Socket, const char *Type, int ID)
+{
+ int responseCode;
+ char *buf;
+ regmatch_t matches[7];
+ char *type, *desc;
+ int id, price;
+
+ // Dispense!
+ sendf(Socket, "ITEM_INFO %s:%i\n", Type, ID);
+ buf = ReadLine(Socket);
+
+ responseCode = atoi(buf);
+ switch( responseCode )
+ {
+ 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';
+
+ 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);
+
+ return 0;
+}
+
/**
* \brief Dispense an item
* \return Boolean Failure
*/
-int DispenseItem(int Socket, int ItemID)
+int DispenseItem(int Socket, const char *Type, int ID)
{
int ret, responseCode;
char *buf;
- if( ItemID < 0 || ItemID > giNumItems ) return -1;
+ // Check for a dry run
+ if( gbDryRun ) {
+ printf("Dry Run - No action\n");
+ return 0;
+ }
// Dispense!
- sendf(Socket, "DISPENSE %s:%i\n", gaItems[ItemID].Type, gaItems[ItemID].ID);
+ sendf(Socket, "DISPENSE %s:%i\n", Type, ID);
buf = ReadLine(Socket);
responseCode = atoi(buf);
ret = 1;
break;
case 406:
- printf("Bad item name, bug report\n");
+ printf("Bad item name\n");
ret = 1;
break;
case 500:
char *buf;
int responseCode;
+ // Check for a dry run
+ if( gbDryRun ) {
+ printf("Dry Run - No action\n");
+ return 0;
+ }
+
sendf(Socket, "ADD %s %i %s\n", Username, Ammount, Reason);
buf = ReadLine(Socket);
char *buf;
int responseCode;
+ // Check for a dry run
+ if( gbDryRun ) {
+ printf("Dry Run - No action\n");
+ return 0;
+ }
+
sendf(Socket, "SET %s %i %s\n", Username, Balance, Reason);
buf = ReadLine(Socket);
return 0;
}
+ // Check for a dry run
+ if( gbDryRun ) {
+ printf("Dry Run - No action\n");
+ return 0;
+ }
+
sendf(Socket, "GIVE %s %i %s\n", Username, Ammount, Reason);
buf = ReadLine(Socket);
return 0;
}
+ // Check for a dry run
+ if( gbDryRun ) {
+ printf("Dry Run - No action\n");
+ return 0;
+ }
+
sendf(Socket, "DONATE %i %s\n", Ammount, Reason);
buf = ReadLine(Socket);
char *buf;
int responseCode, ret;
+ // Check for a dry run
+ if( gbDryRun ) {
+ printf("Dry Run - No action\n");
+ return 0;
+ }
+
sendf(Socket, "USER_ADD %s\n", Username);
buf = ReadLine(Socket);
char *buf;
int responseCode, ret;
+ // Check for a dry run
+ if( gbDryRun ) {
+ printf("Dry Run - No action\n");
+ return 0;
+ }
+
// TODO: Pre-validate the string
sendf(Socket, "USER_FLAGS %s %s\n", Username, TypeString);
int ret;
ret = regexec(regex, string, nMatches, matches, 0);
- if( ret ) {
+ if( ret && errorMessage ) {
size_t len = regerror(ret, regex, NULL, 0);
char errorStr[len];
regerror(ret, regex, errorStr, len);
{
char *command, *args;
int i;
- #if 0
- char **argList;
- int numArgs = 0;
- #endif
+
+ if( giDebugLevel >= 2 )
+ Debug(Client, "Server_ParseClientCommand: (CommandString = '%s')", CommandString);
if( Server_int_ParseArgs(1, CommandString, &command, &args, NULL) )
{
// Is this an error? (just ignore for now)
- args = "";
+ //args = "";
}
// Find command
for( i = 0; i < NUM_COMMANDS; i++ )
{
- if(strcmp(CommandString, gaServer_Commands[i].Name) == 0) {
+ if(strcmp(command, gaServer_Commands[i].Name) == 0) {
+ if( giDebugLevel >= 2 )
+ Debug(Client, "CMD %s - \"%s\"", command, args);
gaServer_Commands[i].Function(Client, args);
return ;
}
{
char *username;
- Server_int_ParseArgs(0, Args, &username, NULL);
+ if( Server_int_ParseArgs(0, Args, &username, NULL) )
+ {
+ sendf(Client->Socket, "407 AUTOAUTH takes 1 argument\n");
+ return ;
+ }
// Check if trusted
if( !Client->bIsTrusted ) {
// You can't be an internal account
if( Bank_GetFlags(Client->UID) & USER_FLAG_INTERNAL ) {
+ if(giDebugLevel)
+ Debug(Client, "Autoauth as '%s', not allowed", username);
Client->UID = -1;
sendf(Client->Socket, "401 Auth Failure\n");
return ;
int Server_int_ParseArgs(int bUseLongLast, char *ArgStr, ...)
{
va_list args;
- char savedChar = *ArgStr;
+ char savedChar;
char **dest;
va_start(args, ArgStr);
+
+ // Check for null
+ if( !ArgStr )
+ {
+ while( (dest = va_arg(args, char **)) )
+ *dest = NULL;
+ va_end(args);
+ return 1;
+ }
+
+ savedChar = *ArgStr;
while( (dest = va_arg(args, char **)) )
{
do {
*dest = NULL;
} while( (dest = va_arg(args, char **)) );
+ va_end(args);
return -1;
}
- // Set destination
- *dest = ArgStr;
-
if( *ArgStr == '"' )
{
+ ArgStr ++;
+ *dest = ArgStr;
// Read until quote
while( *ArgStr && *ArgStr != '"' )
ArgStr ++;
}
else
{
+ // Set destination
+ *dest = ArgStr;
// Read until a space
while( *ArgStr && *ArgStr != ' ' && *ArgStr != '\t' )
ArgStr ++;
}
savedChar = *ArgStr; // savedChar is used to un-mangle the last string
*ArgStr = '\0';
+ ArgStr ++;
}
+ va_end(args);
// Oops, extra arguments, and greedy not set
- if( savedChar == ' ' && bUseLongLast )
+ if( (savedChar == ' ' || savedChar == '\t') && !bUseLongLast ) {
return -1;
+ }
// Un-mangle last
- if(bUseLongLast)
+ if(bUseLongLast) {
+ ArgStr --;
*ArgStr = savedChar;
-
- va_end(args);
+ }
return 0; // Success!
}