SQLite database works, time for more thorough stress testing
authorJohn Hodge <[email protected]>
Sun, 30 Jan 2011 15:14:45 +0000 (23:14 +0800)
committerJohn Hodge <[email protected]>
Sun, 30 Jan 2011 15:14:45 +0000 (23:14 +0800)
- Added database setup script to aid initial setup
 > Creates an account as the current user and gives them $10

SetupDatabase [new file with mode: 0755]
notes/proto.txt
src/client/main.c
src/cokebank.h
src/cokebank_sqlite/main.c
src/server/dispense.c
src/server/server.c

diff --git a/SetupDatabase b/SetupDatabase
new file mode 100755 (executable)
index 0000000..0a8b5ab
--- /dev/null
@@ -0,0 +1,4 @@
+#!/bin/sh
+sudo ./dispense user add $USER
+sudo ./dispense user type $USER coke
+./dispense add $USER +1000 "initial"
index dc11783..df3f9cc 100644 (file)
@@ -82,7 +82,7 @@ c     ITEM_INFO <item_id>\n
 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
     ...
index 711d1a9..0aea926 100644 (file)
@@ -50,6 +50,7 @@ void  PopulateItemList(int Socket);
  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);
@@ -170,7 +171,7 @@ int main(int argc, char *argv[])
                //
                // `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");
@@ -197,7 +198,7 @@ int main(int argc, char *argv[])
                // 
                // `dispense user`
                // - User administration (Wheel Only)
-               else if( strcmp(arg, "user") == 0 )
+               if( strcmp(arg, "user") == 0 )
                {
                        // Check argument count
                        if( i + 1 >= argc ) {
@@ -244,6 +245,31 @@ int main(int argc, char *argv[])
                        }
                        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;
@@ -321,16 +347,18 @@ void ShowUsage(void)
                "    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"
@@ -970,7 +998,7 @@ int Dispense_AlterBalance(int Socket, const char *Username, int Ammount, const c
 }
 
 /**
- * \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)
 {
@@ -1014,6 +1042,48 @@ int Dispense_Give(int Socket, const char *Username, int Ammount, const char *Rea
        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
  */
@@ -1026,15 +1096,15 @@ int Dispense_EnumUsers(int Socket)
        
        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");
index d94e36f..7c7997e 100644 (file)
  */
 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
 };
 
 /**
@@ -67,8 +70,8 @@ extern int    Bank_Initialise(const char *Argument);
 
 /**
  * \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
  */
@@ -98,11 +101,13 @@ extern int Bank_GetBalance(int AcctID);
 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);
@@ -169,4 +174,10 @@ extern int Bank_AddAcctCard(int AcctID, const char *CardID);
  */
 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
index 26282dc..c112260 100644 (file)
@@ -90,6 +90,8 @@ int Bank_Initialise(const char *Argument)
                        sqlite3_free(errmsg);
                        return 1;
                }
+               
+               Log_Info("SQLite database rebuilt");
        }
        else
        {
@@ -159,7 +161,7 @@ int Bank_GetFlags(int UserID)
 
        // 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
                );
@@ -335,6 +337,7 @@ tAcctIterator *Bank_Iterator(int FlagMask, int FlagValues, int Flags, int MinMax
        const char      *revSort;
        sqlite3_stmt    *ret;
        
+       // Balance condtion
        if( Flags & BANK_ITFLAG_MINBALANCE )
                balanceClause = " AND acct_balance>=";
        else if( Flags & BANK_ITFLAG_MAXBALANCE )
@@ -344,6 +347,7 @@ tAcctIterator *Bank_Iterator(int FlagMask, int FlagValues, int Flags, int MinMax
                MinMaxBalance = 0;
        }
        
+       // Last seen condition
        if( Flags & BANK_ITFLAG_SEENAFTER )
                lastSeenClause = " AND acct_last_seen>=";
        else if( Flags & BANK_ITFLAG_SEENBEFORE )
@@ -352,6 +356,7 @@ tAcctIterator *Bank_Iterator(int FlagMask, int FlagValues, int Flags, int MinMax
                lastSeenClause = " AND datetime(-1,'unixepoch')!=";
        }
        
+       // Sorting clause
        switch( Flags & BANK_ITFLAG_SORTMASK )
        {
        case BANK_ITFLAG_SORT_NONE:
index d55c18d..81d49dc 100644 (file)
@@ -165,7 +165,7 @@ int _GetMinBalance(int Account)
 
 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;
index 0b9005d..9c36f3d 100644 (file)
@@ -59,6 +59,7 @@ void  Server_Cmd_ENUMITEMS(tClient *Client, char *Args);
 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);
@@ -83,6 +84,7 @@ const struct sClientCommand {
        {"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},
@@ -203,6 +205,7 @@ void Server_HandleClient(int Socket, int bTrusted)
        clientInfo.Socket = Socket;
        clientInfo.ID = giServer_NextClientID ++;
        clientInfo.bIsTrusted = bTrusted;
+       clientInfo.EffectiveUID = -1;
        
        // Read from client
        /*
@@ -771,36 +774,133 @@ void Server_Cmd_ADD(tClient *Client, char *Args)
 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 )
@@ -822,12 +922,7 @@ void Server_Cmd_ENUMUSERS(tClient *Client, char *Args)
        
        
        // 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 )
        {
@@ -921,6 +1016,12 @@ void Server_Cmd_USERADD(tClient *Client, char *Args)
                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");
 }
 

UCC git Repository :: git.ucc.asn.au