From 2402457fd4ea286febae34182d9a9f3b63cb6565 Mon Sep 17 00:00:00 2001 From: John Hodge Date: Sun, 30 Jan 2011 23:14:45 +0800 Subject: [PATCH] SQLite database works, time for more thorough stress testing - Added database setup script to aid initial setup > Creates an account as the current user and gives them $10 --- SetupDatabase | 4 + notes/proto.txt | 2 +- src/client/main.c | 90 ++++++++++++++++++--- src/cokebank.h | 39 ++++++---- src/cokebank_sqlite/main.c | 7 +- src/server/dispense.c | 2 +- src/server/server.c | 155 ++++++++++++++++++++++++++++++------- 7 files changed, 245 insertions(+), 54 deletions(-) create mode 100755 SetupDatabase diff --git a/SetupDatabase b/SetupDatabase new file mode 100755 index 0000000..0a8b5ab --- /dev/null +++ b/SetupDatabase @@ -0,0 +1,4 @@ +#!/bin/sh +sudo ./dispense user add $USER +sudo ./dispense user type $USER coke +./dispense add $USER +1000 "initial" diff --git a/notes/proto.txt b/notes/proto.txt index dc11783..df3f9cc 100644 --- a/notes/proto.txt +++ b/notes/proto.txt @@ -82,7 +82,7 @@ c ITEM_INFO \n s 202 Item \n --- Get Users' Balances --- -c ENUM_USERS[ min:][ max:][ flags:][ lastseen:][ sort:[-desc]]\n +c ENUM_USERS[ min_balance:][ max_balance:][ flags:][ last_seen_before:][ last_seen_after:][ sort:[-desc]]\n s 201 Users \n s 202 User \n ... diff --git a/src/client/main.c b/src/client/main.c index 711d1a9..0aea926 100644 --- a/src/client/main.c +++ b/src/client/main.c @@ -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 \n" " Dispense named item\n" " dispense give \"\"\n" - " Give some of your money away\n" + " Give money to another user\n" + " dispense donate \"\"\n" + " Donate to the club\n" " dispense acct []\n" " Show user balances\n" " dispense acct [+-] \"\"\n" " Alter a account value (Coke members only)\n" " dispense user add \n" - " Create new coke account (Wheel members only)\n" - " dispense user type \n" + " Create new coke account (Admins only)\n" + " dispense user type \n" " Alter a user's flags\n" - " is a comma-separated list of user,coke,wheel,disabled\n" + " 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"); diff --git a/src/cokebank.h b/src/cokebank.h index d94e36f..7c7997e 100644 --- a/src/cokebank.h +++ b/src/cokebank.h @@ -28,20 +28,23 @@ */ 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 diff --git a/src/cokebank_sqlite/main.c b/src/cokebank_sqlite/main.c index 26282dc..c112260 100644 --- a/src/cokebank_sqlite/main.c +++ b/src/cokebank_sqlite/main.c @@ -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: diff --git a/src/server/dispense.c b/src/server/dispense.c index d55c18d..81d49dc 100644 --- a/src/server/dispense.c +++ b/src/server/dispense.c @@ -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; diff --git a/src/server/server.c b/src/server/server.c index 0b9005d..9c36f3d 100644 --- a/src/server/server.c +++ b/src/server/server.c @@ -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 != "-" - if( strcmp(min, "-") != 0 ) - minBal = atoi(min); - // If != "-" - 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"); } -- 2.20.1