--- /dev/null
+#!/bin/sh
+sudo ./dispense user add $USER
+sudo ./dispense user type $USER coke
+./dispense add $USER +1000 "initial"
s 202 Item <item_id> <price> <description>\n
--- Get Users' Balances ---
-c ENUM_USERS[ min:<balance>][ max:<balance>][ flags:<flagset>][ lastseen:<unix_timestamp>][ sort:<field>[-desc]]\n
+c ENUM_USERS[ min_balance:<balance>][ max_balance:<balance>][ flags:<flagset>][ last_seen_before:<unix_timestamp>][ last_seen_after:<unix_timestamp>][ sort:<field>[-desc]]\n
s 201 Users <count>\n
s 202 User <username> <balance> <flags>\n
...
int DispenseItem(int Socket, int ItemID);
int Dispense_AlterBalance(int Socket, const char *Username, int Ammount, const char *Reason);
int Dispense_Give(int Socket, const char *Username, int Ammount, const char *Reason);
+ int Dispense_Donate(int Socket, int Ammount, const char *Reason);
int Dispense_EnumUsers(int Socket);
int Dispense_ShowUser(int Socket, const char *Username);
void _PrintUserLine(const char *Line);
//
// `dispense give`
// - "Here, have some money."
- else if( strcmp(arg, "give") == 0 )
+ if( strcmp(arg, "give") == 0 )
{
if( i + 3 >= argc ) {
fprintf(stderr, "`dispense give` takes three arguments\n");
//
// `dispense user`
// - User administration (Wheel Only)
- else if( strcmp(arg, "user") == 0 )
+ if( strcmp(arg, "user") == 0 )
{
// Check argument count
if( i + 1 >= argc ) {
}
return 0;
}
+
+ // Donation!
+ if( strcmp(arg, "donate") == 0 )
+ {
+ // Check argument count
+ if( i + 2 >= argc ) {
+ 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(argv[i+1]), argv[i+1]);
+
+ return 0;
+ }
+
else {
// Item name / pattern
gsItemPattern = arg;
" dispense <item>\n"
" Dispense named item\n"
" dispense give <user> <ammount> \"<reason>\"\n"
- " Give some of your money away\n"
+ " Give money to another user\n"
+ " dispense donate <ammount> \"<reason>\"\n"
+ " Donate to the club\n"
" dispense acct [<user>]\n"
" Show user balances\n"
" dispense acct <user> [+-]<ammount> \"<reason>\"\n"
" Alter a account value (Coke members only)\n"
" dispense user add <user>\n"
- " Create new coke account (Wheel members only)\n"
- " dispense user type <flags>\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,wheel,disabled\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"
}
/**
- * \brief Alter a user's balance
+ * \brief Give money to another user
*/
int Dispense_Give(int Socket, const char *Username, int Ammount, const char *Reason)
{
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
*/
if( giMinimumBalance != INT_MIN ) {
if( giMaximumBalance != INT_MAX ) {
- sendf(Socket, "ENUM_USERS %i %i\n", giMinimumBalance, giMaximumBalance);
+ sendf(Socket, "ENUM_USERS min_balance:%i max_balance:%i\n", giMinimumBalance, giMaximumBalance);
}
else {
- sendf(Socket, "ENUM_USERS %i\n", giMinimumBalance);
+ sendf(Socket, "ENUM_USERS min_balance:%i\n", giMinimumBalance);
}
}
else {
if( giMaximumBalance != INT_MAX ) {
- sendf(Socket, "ENUM_USERS - %i\n", giMaximumBalance);
+ sendf(Socket, "ENUM_USERS max_balance:%i\n", giMaximumBalance);
}
else {
sendf(Socket, "ENUM_USERS\n");
*/
typedef struct sAcctIterator tAcctIterator;
+/**
+ * \brief Flag values for the \a Flags parameter to Bank_Iterator
+ */
enum eBank_ItFlags
{
- BANK_ITFLAG_MINBALANCE = 0x01,
- BANK_ITFLAG_MAXBALANCE = 0x02,
- BANK_ITFLAG_SEENBEFORE = 0x04,
- BANK_ITFLAG_SEENAFTER = 0x08,
+ BANK_ITFLAG_MINBALANCE = 0x01, //!< Balance value is Minium Balance
+ BANK_ITFLAG_MAXBALANCE = 0x02, //!< Balance value is Maximum Balance (higher priority)
+ BANK_ITFLAG_SEENAFTER = 0x04, //!< Last seen value is lower bound
+ BANK_ITFLAG_SEENBEFORE = 0x08, //!< Last seen value is upper bound (higher priority)
- BANK_ITFLAG_SORT_NONE = 0x000,
- BANK_ITFLAG_SORT_NAME = 0x100,
- BANK_ITFLAG_SORT_BAL = 0x200,
- BANK_ITFLAG_SORT_UNIXID = 0x300,
- BANK_ITFLAG_SORT_LASTSEEN = 0x400,
- BANK_ITFLAG_SORTMASK = 0x700,
- BANK_ITFLAG_REVSORT = 0x800
+ BANK_ITFLAG_SORT_NONE = 0x000, //!< No sorting (up to the implementation)
+ BANK_ITFLAG_SORT_NAME = 0x100, //!< Sort alphabetically ascending by name
+ BANK_ITFLAG_SORT_BAL = 0x200, //!< Sort by balance, ascending
+ BANK_ITFLAG_SORT_UNIXID = 0x300, //!< Sort by UnixUID (TODO: Needed?)
+ BANK_ITFLAG_SORT_LASTSEEN = 0x400, //!< Sort by last seen time (ascending)
+ BANK_ITFLAG_SORTMASK = 0x700, //!< Sort type mask
+ BANK_ITFLAG_REVSORT = 0x800 //!< Sort descending instead
};
/**
/**
* \brief Transfer money from one account to another
- * \param SourceUser UID (from \a Bank_GetUserID) to take the money from
- * \param DestUser UID (from \a Bank_GetUserID) give money to
+ * \param SourceAcct UID (from \a Bank_GetUserID) to take the money from
+ * \param DestAcct UID (from \a Bank_GetUserID) give money to
* \param Ammount Amount of money (in cents) to transfer
* \param Reason Reason for the transfer
*/
extern char *Bank_GetAcctName(int AcctID);
/**
* \brief Get an account ID from a passed name
+ * \param Name Name to search for
+ * \return ID of the account, or -1 if not found
*/
extern int Bank_GetAcctByName(const char *Name);
/**
* \brief Create a new account
- * \param Username Name for the new account (if NULL, an anoymous account is created)
+ * \param Name Name for the new account (if NULL, an anoymous account is created)
* \return ID of the new account
*/
extern int Bank_CreateAcct(const char *Name);
*/
extern char *mkstr(const char *Format, ...);
+/**
+ * \brief Dispense log access
+ * \note Try not to over-use, only stuff that matters should go in here
+ */
+extern void Log_Info(const char *Format, ...);
+
#endif
sqlite3_free(errmsg);
return 1;
}
+
+ Log_Info("SQLite database rebuilt");
}
else
{
// Build Query
query = mkstr(
- "SELECT acct_is_disabled,acct_is_coke,acct_is_wheel,acct_is_door,acct_is_internal"
+ "SELECT acct_is_disabled,acct_is_coke,acct_is_admin,acct_is_door,acct_is_internal"
" FROM accounts WHERE acct_id=%i LIMIT 1",
UserID
);
const char *revSort;
sqlite3_stmt *ret;
+ // Balance condtion
if( Flags & BANK_ITFLAG_MINBALANCE )
balanceClause = " AND acct_balance>=";
else if( Flags & BANK_ITFLAG_MAXBALANCE )
MinMaxBalance = 0;
}
+ // Last seen condition
if( Flags & BANK_ITFLAG_SEENAFTER )
lastSeenClause = " AND acct_last_seen>=";
else if( Flags & BANK_ITFLAG_SEENBEFORE )
lastSeenClause = " AND datetime(-1,'unixepoch')!=";
}
+ // Sorting clause
switch( Flags & BANK_ITFLAG_SORTMASK )
{
case BANK_ITFLAG_SORT_NONE:
int _Transfer(int Source, int Destination, int Ammount, const char *Reason)
{
- if( Ammount < 0 )
+ if( Ammount > 0 )
{
if( Bank_GetBalance(Source) + Ammount < _GetMinBalance(Source) )
return 1;
void Server_Cmd_ITEMINFO(tClient *Client, char *Args);
void Server_Cmd_DISPENSE(tClient *Client, char *Args);
void Server_Cmd_GIVE(tClient *Client, char *Args);
+void Server_Cmd_DONATE(tClient *Client, char *Args);
void Server_Cmd_ADD(tClient *Client, char *Args);
void Server_Cmd_ENUMUSERS(tClient *Client, char *Args);
void Server_Cmd_USERINFO(tClient *Client, char *Args);
{"ITEM_INFO", Server_Cmd_ITEMINFO},
{"DISPENSE", Server_Cmd_DISPENSE},
{"GIVE", Server_Cmd_GIVE},
+ {"DONATE", Server_Cmd_DONATE},
{"ADD", Server_Cmd_ADD},
{"ENUM_USERS", Server_Cmd_ENUMUSERS},
{"USER_INFO", Server_Cmd_USERINFO},
clientInfo.Socket = Socket;
clientInfo.ID = giServer_NextClientID ++;
clientInfo.bIsTrusted = bTrusted;
+ clientInfo.EffectiveUID = -1;
// Read from client
/*
void Server_Cmd_ENUMUSERS(tClient *Client, char *Args)
{
int i, numRet = 0;
- int maxBal = INT_MAX, minBal = INT_MIN;
tAcctIterator *it;
+ int maxBal = INT_MAX, minBal = INT_MIN;
+ int flagMask = 0, flagVal = 0;
int sort = BANK_ITFLAG_SORT_NAME;
+ time_t lastSeenAfter=0, lastSeenBefore=0;
+
+ int flags; // Iterator flags
+ int balValue; // Balance value for iterator
+ time_t timeValue; // Time value for iterator
// Parse arguments
if( Args && strlen(Args) )
{
- char *min = Args, *max;
-
- max = strchr(Args, ' ');
- if( max ) {
- *max = '\0';
- max ++;
- }
-
- // If <minBal> != "-"
- if( strcmp(min, "-") != 0 )
- minBal = atoi(min);
- // If <maxBal> != "-"
- if( max && strcmp(max, "-") != 0 )
- maxBal = atoi(max);
+ char *space = Args, *type, *val;
+ do
+ {
+ type = space;
+ // Get next space
+ space = strchr(space, ' ');
+ if(space) *space = '\0';
+
+ // Get type
+ val = strchr(type, ':');
+ if( val ) {
+ *val = '\0';
+ val ++;
+
+ // Types
+ // - Minium Balance
+ if( strcmp(type, "min_balance") == 0 ) {
+ minBal = atoi(val);
+ }
+ // - Maximum Balance
+ else if( strcmp(type, "max_balance") == 0 ) {
+ maxBal = atoi(val);
+ }
+ // - Flags
+ else if( strcmp(type, "flags") == 0 ) {
+ if( Server_int_ParseFlags(Client, val, &flagMask, &flagVal) )
+ return ;
+ }
+ // - Last seen before timestamp
+ else if( strcmp(type, "last_seen_before") == 0 ) {
+ lastSeenAfter = atoll(val);
+ }
+ // - Last seen after timestamp
+ else if( strcmp(type, "last_seen_after") == 0 ) {
+ lastSeenAfter = atoll(val);
+ }
+ // - Sorting
+ else if( strcmp(type, "sort") == 0 ) {
+ char *dash = strchr(val, '-');
+ if( dash ) {
+ *dash = '\0';
+ dash ++;
+ }
+ if( strcmp(val, "name") == 0 ) {
+ sort = BANK_ITFLAG_SORT_NAME;
+ }
+ else if( strcmp(val, "balance") == 0 ) {
+ sort = BANK_ITFLAG_SORT_BAL;
+ }
+ else if( strcmp(val, "lastseen") == 0 ) {
+ sort = BANK_ITFLAG_SORT_LASTSEEN;
+ }
+ else {
+ sendf(Client->Socket, "407 Unknown sort field ('%s')\n", val);
+ return ;
+ }
+ // Handle sort direction
+ if( dash ) {
+ if( strcmp(dash, "desc") == 0 ) {
+ sort |= BANK_ITFLAG_REVSORT;
+ }
+ else {
+ sendf(Client->Socket, "407 Unknown sort direction '%s'\n", dash);
+ return ;
+ }
+ dash[-1] = '-';
+ }
+ }
+ else {
+ sendf(Client->Socket, "407 Unknown argument to ENUM_USERS '%s:%s'\n", type, val);
+ return ;
+ }
+
+ val[-1] = ':';
+ }
+ else {
+ sendf(Client->Socket, "407 Unknown argument to ENUM_USERS '%s'\n", type);
+ return ;
+ }
+
+ // Eat whitespace
+ if( space ) {
+ *space = ' '; // Repair (to be nice)
+ space ++;
+ while(*space == ' ') space ++;
+ }
+ } while(space);
}
// Create iterator
- if( maxBal != INT_MAX )
- it = Bank_Iterator(0, 0, sort|BANK_ITFLAG_MAXBALANCE, maxBal, 0);
- else if( minBal != INT_MIN )
- it = Bank_Iterator(0, 0, sort|BANK_ITFLAG_MINBALANCE, minBal, 0);
- else
- it = Bank_Iterator(0, 0, sort, 0, 0);
+ if( maxBal != INT_MAX ) {
+ flags = sort|BANK_ITFLAG_MAXBALANCE;
+ balValue = maxBal;
+ }
+ else if( minBal != INT_MIN ) {
+ flags = sort|BANK_ITFLAG_MINBALANCE;
+ balValue = minBal;
+ }
+ else {
+ flags = sort;
+ balValue = 0;
+ }
+ if( lastSeenBefore ) {
+ timeValue = lastSeenBefore;
+ flags |= BANK_ITFLAG_SEENBEFORE;
+ }
+ else if( lastSeenAfter ) {
+ timeValue = lastSeenAfter;
+ flags |= BANK_ITFLAG_SEENAFTER;
+ }
+ else {
+ timeValue = 0;
+ }
+ it = Bank_Iterator(flagMask, flagVal, flags, balValue, timeValue);
// Get return number
while( (i = Bank_IteratorNext(it)) != -1 )
// Create iterator
- if( maxBal != INT_MAX )
- it = Bank_Iterator(0, 0, sort|BANK_ITFLAG_MAXBALANCE, maxBal, 0);
- else if( minBal != INT_MIN )
- it = Bank_Iterator(0, 0, sort|BANK_ITFLAG_MINBALANCE, minBal, 0);
- else
- it = Bank_Iterator(0, 0, sort, 0, 0);
+ it = Bank_Iterator(flagMask, flagVal, flags, balValue, timeValue);
while( (i = Bank_IteratorNext(it)) != -1 )
{
return ;
}
+ {
+ char *thisName = Bank_GetAcctName(Client->UID);
+ Log_Info("Account '%s' created by '%s'", username, thisName);
+ free(thisName);
+ }
+
sendf(Client->Socket, "200 User Added\n");
}