From f8ff5876b5787375fe629450d0c5d088965720d0 Mon Sep 17 00:00:00 2001 From: John Hodge Date: Sun, 30 Jan 2011 18:55:58 +0800 Subject: [PATCH] SQLite cokebank feature complete, bugfix time --- RunServerTest | 12 +++ notes/proto.txt | 19 ++-- src/Makefile | 6 +- src/cokebank | 1 - src/cokebank.h | 3 +- src/cokebank_basic/Makefile | 2 +- src/cokebank_basic/main.c | 10 +- src/cokebank_sqlite/main.c | 93 +++++++++-------- src/server/common.h | 3 +- src/server/dispense.c | 91 ++++++++++++++--- src/server/server.c | 195 +++++++++++++++++++++++++----------- 11 files changed, 304 insertions(+), 131 deletions(-) delete mode 120000 src/cokebank diff --git a/RunServerTest b/RunServerTest index 5bedb53..b117c70 100755 --- a/RunServerTest +++ b/RunServerTest @@ -3,6 +3,18 @@ ARGS="--itemsfile items.cfg -p 11020" ARGS=$ARGS" --cokeport /dev/ttyUSB0" +if [ "x$2" != "x" ]; then + _cokebank=$1 +else + _cokebank="sqlite" +fi + +rm cokebank.so +rm cokebank.db + +ln -s cokebank_$_cokebank.so cokebank.so +ln -s cokebank_$_cokebank.db cokebank.db + if [ "x$1" = "xdbg" ]; then LD_LIBRARY_PATH=. gdb --args ./dispsrv $ARGS else diff --git a/notes/proto.txt b/notes/proto.txt index f82d27b..dc11783 100644 --- a/notes/proto.txt +++ b/notes/proto.txt @@ -38,8 +38,9 @@ Item IDs are allowed to contain any non-whitespace character, but in general they will be limited to the standard alpha-numeric set === User Auth === +--- Untrusted Users --- c USER \n -s 100 SALT \n or 100 User Set\n (If no salt used) +s 202 SALT \n or 100 User Set\n (If no salt used) c PASS \n (Hex-Encoded SHA-1 Hash of ) s 200 Auth OK\n or 401 Auth Failure\n User is now authenticated @@ -50,7 +51,7 @@ c AUTOAUTH \n s 200 Auth OK\n or 404 Bad Username\n or 401 Untrusted\n --- Alternate Method (MIFARE Authentication) c MIFARE \n -s 200 Auth OK as \n or 404 Bad Card ID\n or 401 Untrusted\n +s 200 Auth OK as \n or 401 Untrusted\n or 404 Bad Card ID\n --- Set effective user (User in `dispense -u`) --- c SETEUSER \n @@ -59,10 +60,13 @@ s 200 User set\n or 403 Not in coke\n or 404 User not found\n === Commands === --- Dispense an item --- c DISPENSE \n -s 200 Dispense OK\n or 402 Poor You\n or 500 Dispense Error\n or 406 Bad Item\n +s 200 Dispense OK\n or 402 Poor You\n or 406 Bad Item\n or 500 Dispense Error\n --- Give to another user --- c GIVE \n s 200 Give OK\n or 402 Poor You\n or 404 Bad User\n +--- Donate to the club --- +c DONATE \n +s 200 Give OK\n or 402 Poor You\n --- Alter balance --- c ADD \n s 200 Add OK\n or 402 No balance\n or 403 Not Coke\n or 404 Bad User\n @@ -78,12 +82,15 @@ c ITEM_INFO \n s 202 Item \n --- Get Users' Balances --- - and can be '-' to indicate "none" -c ENUM_USERS[ []]\n +c ENUM_USERS[ min:][ max:][ flags:][ lastseen:][ sort:[-desc]]\n s 201 Users \n s 202 User \n ... s 200 List End\n + Integer balance value (in cents) + Flag values (same format as USER_FLAGS) + Number of seconds since 1/Jan/1970 + Sort field (name,balance,lastseen) --- Get a User's Balance --- c USER_INFO\n s 202 User \n @@ -93,7 +100,7 @@ c USER_ADD \n s 200 User Added\n or 403 Not Wheel\n or 404 User Exists\n --- Set user flags --- is a comma-separated list of flag values (optionally preceded by - - to remove the flag) Valid values are: user,coke,wheel,meta,disabled,door + - to remove the flag) Valid values are: user,coke,admin,internal,disabled,door c USER_FLAGS \n s 200 User Updated\n or 403 Not Wheel\n or 404 Bad User\n or 407 Unknown Flags\n --- Add MIFARE ID --- diff --git a/src/Makefile b/src/Makefile index c1073d9..f577661 100644 --- a/src/Makefile +++ b/src/Makefile @@ -2,12 +2,14 @@ .PHONY: all clean all: - @make -C cokebank all + @make -C cokebank_sqlite all + @make -C cokebank_basic all @make -C server all @make -C client all clean: - @make -C cokebank clean + @make -C cokebank_sqlite clean + @make -C cokebank_basic clean @make -C server clean @make -C client clean diff --git a/src/cokebank b/src/cokebank deleted file mode 120000 index eff8399..0000000 --- a/src/cokebank +++ /dev/null @@ -1 +0,0 @@ -cokebank_basic \ No newline at end of file diff --git a/src/cokebank.h b/src/cokebank.h index ae244a2..d94e36f 100644 --- a/src/cokebank.h +++ b/src/cokebank.h @@ -18,6 +18,7 @@ #define COKEBANK_SALES_ACCT ">sales" //!< Sales made into #define COKEBANK_DEBT_ACCT ">liability" //!< Credit taken out of +#define COKEBANK_FREE_ACCT ">freeitems" //!< ODay drink costs taken out of /** * \brief Account iterator opaque structure @@ -50,7 +51,7 @@ enum eBank_ItFlags */ enum eCokebank_Flags { USER_FLAG_COKE = 0x01, //!< User is a coke member (can do coke accounting) - USER_FLAG_WHEEL = 0x02, //!< User is a wheel member (can create, delete and lock accounts) + USER_FLAG_ADMIN = 0x02, //!< User is a administrator (can create, delete and lock accounts) USER_FLAG_DOORGROUP = 0x04, //!< User is in the door group (can open the clubroom door) USER_FLAG_INTERNAL = 0x40, //!< Account is internal (cannot be authenticated, no lower balance limit) USER_FLAG_DISABLED = 0x80 //!< Account is disabled (no transactions allowed) diff --git a/src/cokebank_basic/Makefile b/src/cokebank_basic/Makefile index 6145889..e5b4181 100644 --- a/src/cokebank_basic/Makefile +++ b/src/cokebank_basic/Makefile @@ -1,5 +1,5 @@ -BIN := ../../cokebank.so +BIN := ../../cokebank_basic.so OBJ := main.o CPPFLAGS := diff --git a/src/cokebank_basic/main.c b/src/cokebank_basic/main.c index d5d7f25..0e7956a 100644 --- a/src/cokebank_basic/main.c +++ b/src/cokebank_basic/main.c @@ -397,7 +397,7 @@ int Bank_GetFlags(int ID) // root if( gaBank_Users[ID].UnixID == 0 ) { - gaBank_Users[ID].Flags |= USER_FLAG_WHEEL|USER_FLAG_COKE; + gaBank_Users[ID].Flags |= USER_FLAG_ADMIN|USER_FLAG_COKE; } #if USE_UNIX_GROUPS @@ -424,17 +424,19 @@ int Bank_GetFlags(int ID) } } + #if 0 // Check for additions to the "wheel" group grp = getgrnam("wheel"); if( grp ) { for( i = 0; grp->gr_mem[i]; i ++ ) { if( strcmp(grp->gr_mem[i], pwd->pw_name) == 0 ) { - gaBank_Users[ID].Flags |= USER_FLAG_WHEEL; + gaBank_Users[ID].Flags |= USER_FLAG_ADMIN; break ; } } } + #endif } #endif @@ -485,7 +487,7 @@ int Bank_int_GetMinAllowedBalance(int ID) return INT_MIN; // Wheel is allowed to go to -$100 - if( (flags & USER_FLAG_WHEEL) ) + if( (flags & USER_FLAG_ADMIN) ) return -10000; // Coke is allowed to go to -$20 @@ -534,7 +536,7 @@ int Bank_int_AddUser(const char *Username) gaBank_Users[giBank_NumUsers].Flags = USER_FLAG_INTERNAL; } else if( strcmp(Username, "root") == 0 ) { - gaBank_Users[giBank_NumUsers].Flags = USER_FLAG_WHEEL|USER_FLAG_COKE; + gaBank_Users[giBank_NumUsers].Flags = USER_FLAG_ADMIN|USER_FLAG_COKE; } // Increment count diff --git a/src/cokebank_sqlite/main.c b/src/cokebank_sqlite/main.c index e962b9d..26282dc 100644 --- a/src/cokebank_sqlite/main.c +++ b/src/cokebank_sqlite/main.c @@ -14,24 +14,31 @@ #include "../cokebank.h" #include -const char * const csBank_CreateAccountQry = "CREATE TABLE IF NOT EXISTS accounts (" +const char * const csBank_DatabaseSetup = +"CREATE TABLE IF NOT EXISTS accounts (" " acct_id INTEGER PRIMARY KEY NOT NULL," " acct_balance INTEGER NOT NULL DEFAULT 0," +" acct_last_seen DATETIME NOT NULL DEFAULT (datetime('now'))," " acct_name STRING UNIQUE," " acct_uid INTEGER UNIQUE DEFAULT NULL," -" acct_pin INTEGER CHECK (acct_pin > 0 AND acct_pin < 10000) DEFAULT NULL," +" acct_pin INTEGER DEFAULT NULL," //" acct_pin INTEGER CHECK (acct_pin > 0 AND acct_pin < 10000) DEFAULT NULL," " acct_is_disabled BOOLEAN NOT NULL DEFAULT false," " acct_is_coke BOOLEAN NOT NULL DEFAULT false," -" acct_is_wheel BOOLEAN NOT NULL DEFAULT false," +" acct_is_admin BOOLEAN NOT NULL DEFAULT false," " acct_is_door BOOLEAN NOT NULL DEFAULT false," " acct_is_internal BOOLEAN NOT NULL DEFAULT false" -")"; -const char * const csBank_CreateCardsQry = "CREATE TABLE IF NOT EXISTS cards (" +");" +"CREATE TABLE IF NOT EXISTS cards (" " acct_id INTEGER NOT NULL," " card_name STRING NOT NULL UNIQUE," " FOREIGN KEY (acct_id) REFERENCES accounts (acct_id) ON DELETE CASCADE" // Deletion of the account frees the card ^ ^ ^ -")"; +");" +"INSERT INTO accounts (acct_name,acct_is_admin,acct_uid) VALUES ('root',1,0);" +"INSERT INTO accounts (acct_name,acct_is_internal,acct_uid) VALUES ('"COKEBANK_SALES_ACCT"',1,-1);" +"INSERT INTO accounts (acct_name,acct_is_internal,acct_uid) VALUES ('"COKEBANK_DEBT_ACCT"',1,-2);" +"INSERT INTO accounts (acct_name,acct_is_internal,acct_uid) VALUES ('"COKEBANK_FREE_ACCT"',1,-3);" +; // === TYPES === struct sAcctIterator // Unused really, just used as a void type @@ -41,8 +48,8 @@ struct sAcctIterator // Unused really, just used as a void type // === PROTOYPES === int Bank_Initialise(const char *Argument); int Bank_Transfer(int SourceAcct, int DestAcct, int Ammount, const char *Reason); - int Bank_GetUserFlags(int AcctID); - int Bank_SetUserFlags(int AcctID, int Mask, int Value); + int Bank_GetFlags(int AcctID); + int Bank_SetFlags(int AcctID, int Mask, int Value); int Bank_GetBalance(int AcctID); char *Bank_GetAcctName(int AcctID); sqlite3_stmt *Bank_int_MakeStatemnt(sqlite3 *Database, const char *Query); @@ -73,21 +80,13 @@ int Bank_Initialise(const char *Argument) { // NOP } - else if( rv == SQLITE_NOTFOUND ) + else if( rv == SQLITE_NOTFOUND || rv == SQLITE_ERROR ) { sqlite3_free(errmsg); // Create tables - // - Accounts - rv = sqlite3_exec(gBank_Database, csBank_CreateAccountQry, NULL, NULL, &errmsg); + rv = sqlite3_exec(gBank_Database, csBank_DatabaseSetup, NULL, NULL, &errmsg); if( rv != SQLITE_OK ) { - fprintf(stderr, "SQLite Error: %s\n", errmsg); - sqlite3_free(errmsg); - return 1; - } - // - Mifare relation - rv = sqlite3_exec(gBank_Database, csBank_CreateCardsQry, NULL, NULL, &errmsg); - if( rv != SQLITE_OK ) { - fprintf(stderr, "SQLite Error: %s\n", errmsg); + fprintf(stderr, "Bank_Initialise - SQLite Error: %s\n", errmsg); sqlite3_free(errmsg); return 1; } @@ -95,7 +94,7 @@ int Bank_Initialise(const char *Argument) else { // Unknown error - fprintf(stderr, "SQLite Error: %s\n", errmsg); + fprintf(stderr, "Bank_Initialise - SQLite Error: %s (rv = %i)\n", errmsg, rv); sqlite3_free(errmsg); return 1; } @@ -118,24 +117,26 @@ int Bank_Transfer(int SourceUser, int DestUser, int Ammount, const char *Reason) sqlite3_exec(gBank_Database, "BEGIN TRANSACTION", NULL, NULL, NULL); // Take from the source - query = mkstr("UPDATE accounts SET acct_balance=acct_balance-%i WHERE acct_id=%i", Ammount, SourceUser); + query = mkstr("UPDATE accounts SET acct_balance=acct_balance-%i,acct_last_seen=datetime('now') WHERE acct_id=%i", Ammount, SourceUser); + printf("query = \"%s\"\n", query); rv = sqlite3_exec(gBank_Database, query, NULL, NULL, &errmsg); free(query); if( rv != SQLITE_OK ) { - fprintf(stderr, "SQLite Error: %s\n", errmsg); + fprintf(stderr, "Bank_Transfer - SQLite Error: %s\n", errmsg); sqlite3_free(errmsg); sqlite3_exec(gBank_Database, "ROLLBACK", NULL, NULL, NULL); return 1; } // Give to the destination - query = mkstr("UPDATE accounts SET acct_balance=acct_balance+%i WHERE acct_id=%i", Ammount, DestUser); + query = mkstr("UPDATE accounts SET acct_balance=acct_balance+%i,acct_last_seen=datetime('now') WHERE acct_id=%i", Ammount, DestUser); + printf("query = \"%s\"\n", query); rv = sqlite3_exec(gBank_Database, query, NULL, NULL, &errmsg); free(query); if( rv != SQLITE_OK ) { - fprintf(stderr, "SQLite Error: %s\n", errmsg); + fprintf(stderr, "Bank_Transfer - SQLite Error: %s\n", errmsg); sqlite3_free(errmsg); sqlite3_exec(gBank_Database, "ROLLBACK", NULL, NULL, NULL); return 1; @@ -150,7 +151,7 @@ int Bank_Transfer(int SourceUser, int DestUser, int Ammount, const char *Reason) /* * Get user flags */ -int Bank_GetUserFlags(int UserID) +int Bank_GetFlags(int UserID) { sqlite3_stmt *statement; char *query; @@ -173,11 +174,11 @@ int Bank_GetUserFlags(int UserID) // - Coke if( sqlite3_column_int(statement, 1) ) ret |= USER_FLAG_COKE; // - Wheel - if( sqlite3_column_int(statement, 2) ) ret |= USER_FLAG_WHEEL; + if( sqlite3_column_int(statement, 2) ) ret |= USER_FLAG_ADMIN; // - Door if( sqlite3_column_int(statement, 3) ) ret |= USER_FLAG_DOORGROUP; // - Internal - if( sqlite3_column_int(statement, 3) ) ret |= USER_FLAG_INTERNAL; + if( sqlite3_column_int(statement, 4) ) ret |= USER_FLAG_INTERNAL; // Destroy and return sqlite3_finalize(statement); @@ -188,7 +189,7 @@ int Bank_GetUserFlags(int UserID) /* * Set user flags */ -int Bank_SetUserFlags(int UserID, int Mask, int Value) +int Bank_SetFlags(int UserID, int Mask, int Value) { char *query; int rv; @@ -196,25 +197,27 @@ int Bank_SetUserFlags(int UserID, int Mask, int Value) #define MAP_FLAG(name, flag) (Mask&(flag)?(Value&(flag)?","name"=1":","name"=0"):"") query = mkstr( - "UDPATE accounts WHERE acct_id=%i SET acct_id=acct_id%s%s%s%s%s", - UserID, + "UPDATE accounts SET acct_id=acct_id%s%s%s%s%s WHERE acct_id=%i",// LIMIT 1", MAP_FLAG("acct_is_coke", USER_FLAG_COKE), - MAP_FLAG("acct_is_wheel", USER_FLAG_WHEEL), + MAP_FLAG("acct_is_admin", USER_FLAG_ADMIN), MAP_FLAG("acct_is_door", USER_FLAG_DOORGROUP), MAP_FLAG("acct_is_internal", USER_FLAG_INTERNAL), - MAP_FLAG("acct_is_disabled", USER_FLAG_DISABLED) + MAP_FLAG("acct_is_disabled", USER_FLAG_DISABLED), + UserID ); #undef MAP_FLAG // Execute Query rv = sqlite3_exec(gBank_Database, query, NULL, NULL, &errmsg); - free(query); if( rv != SQLITE_OK ) { - fprintf(stderr, "SQLite Error: %s\n", errmsg); + fprintf(stderr, "Bank_SetUserFlags - SQLite Error: %s\n", errmsg); + fprintf(stderr, "query = '%s'\n", query); + free(query); sqlite3_free(errmsg); return -1; } + free(query); return 0; } @@ -280,6 +283,7 @@ int Bank_GetAcctByName(const char *Name) if( !statement ) return -1; ret = sqlite3_column_int(statement, 0); + if( ret == 0 ) return -1; sqlite3_finalize(statement); return ret; @@ -307,7 +311,7 @@ int Bank_CreateAcct(const char *Name) rv = sqlite3_exec(gBank_Database, query, NULL, NULL, &errmsg); if( rv != SQLITE_OK ) { - fprintf(stderr, "SQLite Error: '%s'\n", errmsg); + fprintf(stderr, "Bank_CreateAcct - SQLite Error: '%s'\n", errmsg); fprintf(stderr, "Query = '%s'\n", query); sqlite3_free(errmsg); free(query); @@ -332,20 +336,20 @@ tAcctIterator *Bank_Iterator(int FlagMask, int FlagValues, int Flags, int MinMax sqlite3_stmt *ret; if( Flags & BANK_ITFLAG_MINBALANCE ) - balanceClause = "acct_balance>="; + balanceClause = " AND acct_balance>="; else if( Flags & BANK_ITFLAG_MAXBALANCE ) - balanceClause = "acct_balance<="; + balanceClause = " AND acct_balance<="; else { - balanceClause = "1!="; + balanceClause = " AND 1!="; MinMaxBalance = 0; } if( Flags & BANK_ITFLAG_SEENAFTER ) - lastSeenClause = "acct_last_seen>="; + lastSeenClause = " AND acct_last_seen>="; else if( Flags & BANK_ITFLAG_SEENBEFORE ) - lastSeenClause = "acct_last_seen<="; + lastSeenClause = " AND acct_last_seen<="; else { - lastSeenClause = "datetime(0,'unixepoch')!="; + lastSeenClause = " AND datetime(-1,'unixepoch')!="; } switch( Flags & BANK_ITFLAG_SORTMASK ) @@ -381,7 +385,7 @@ tAcctIterator *Bank_Iterator(int FlagMask, int FlagValues, int Flags, int MinMax "%s%s" // Sort and direction , MAP_FLAG("acct_is_coke", USER_FLAG_COKE), - MAP_FLAG("acct_is_wheel", USER_FLAG_WHEEL), + MAP_FLAG("acct_is_admin", USER_FLAG_ADMIN), MAP_FLAG("acct_is_door", USER_FLAG_DOORGROUP), MAP_FLAG("acct_is_internal", USER_FLAG_INTERNAL), MAP_FLAG("acct_is_disabled", USER_FLAG_DISABLED), @@ -389,6 +393,7 @@ tAcctIterator *Bank_Iterator(int FlagMask, int FlagValues, int Flags, int MinMax lastSeenClause, LastSeen, orderClause, revSort ); + //printf("query = \"%s\"\n", query); #undef MAP_FLAG ret = Bank_int_MakeStatemnt(gBank_Database, query); @@ -409,7 +414,7 @@ int Bank_IteratorNext(tAcctIterator *It) if( rv == SQLITE_DONE ) return -1; if( rv != SQLITE_ROW ) { - fprintf(stderr, "SQLite Error: %s\n", sqlite3_errmsg(gBank_Database)); + fprintf(stderr, "Bank_IteratorNext - SQLite Error: %s\n", sqlite3_errmsg(gBank_Database)); return -1; } @@ -490,7 +495,7 @@ int Bank_AddAcctCard(int AcctID, const char *CardID) } if( rv != SQLITE_OK ) { - fprintf(stderr, "SQLite Error: '%s'\n", errmsg); + fprintf(stderr, "Bank_AddAcctCard - SQLite Error: '%s'\n", errmsg); fprintf(stderr, "Query = '%s'\n", query); sqlite3_free(errmsg); free(query); diff --git a/src/server/common.h b/src/server/common.h index ec9e38a..8aaf4c0 100644 --- a/src/server/common.h +++ b/src/server/common.h @@ -80,7 +80,8 @@ extern char *mkstr(const char *Format, ...); // --- Dispense --- extern int DispenseItem(int ActualUser, int User, tItem *Item); extern int DispenseGive(int ActualUser, int SrcUser, int DestUser, int Ammount, const char *ReasonGiven); -extern int DispenseAdd(int User, int ByUser, int Ammount, const char *ReasonGiven); +extern int DispenseAdd(int ActualUser, int User, int Ammount, const char *ReasonGiven); +extern int DispenseDonate(int ActualUser, int User, int Ammount, const char *ReasonGiven); // --- Logging --- extern void Log_Error(const char *Format, ...); diff --git a/src/server/dispense.c b/src/server/dispense.c index b77fa9c..d55c18d 100644 --- a/src/server/dispense.c +++ b/src/server/dispense.c @@ -2,6 +2,10 @@ */ #include "common.h" #include +#include + + int _GetMinBalance(int Account); + int _Transfer(int Source, int Destination, int Ammount, const char *Reason); // === CODE === /** @@ -27,7 +31,7 @@ int DispenseItem(int ActualUser, int User, tItem *Item) // Subtract the balance reason = mkstr("Dispense - %s:%i %s", handler->Name, Item->ID, Item->Name); if( !reason ) reason = Item->Name; // TODO: Should I instead return an error? - ret = Bank_Transfer( User, Bank_GetAcctByName(COKEBANK_SALES_ACCT), Item->Price, reason); + ret = _Transfer( User, Bank_GetAcctByName(COKEBANK_SALES_ACCT), Item->Price, reason); free(reason); if(ret) return 2; // 2: No balance @@ -40,7 +44,7 @@ int DispenseItem(int ActualUser, int User, tItem *Item) if(ret) { Log_Error("Dispense failed after deducting cost (%s dispensing %s - %ic)", username, Item->Name, Item->Price); - Bank_Transfer( Bank_GetAcctByName(COKEBANK_SALES_ACCT), User, Item->Price, "rollback" ); + _Transfer( Bank_GetAcctByName(COKEBANK_SALES_ACCT), User, Item->Price, "rollback" ); free( username ); return -1; // 1: Unkown Error again } @@ -49,7 +53,7 @@ int DispenseItem(int ActualUser, int User, tItem *Item) actualUsername = Bank_GetAcctName(ActualUser); // And log that it happened - Log_Info("dispense '%s' (%s:%i) for %s by %s [cost %i, balance %i cents]", + Log_Info("dispense '%s' (%s:%i) for %s by %s [cost %i, balance %i]", Item->Name, handler->Name, Item->ID, username, actualUsername, Item->Price, Bank_GetBalance(User) ); @@ -70,17 +74,18 @@ int DispenseGive(int ActualUser, int SrcUser, int DestUser, int Ammount, const c if( Ammount < 0 ) return 1; // Um... negative give? Not on my watch! - ret = Bank_Transfer( SrcUser, DestUser, Ammount, ReasonGiven ); + ret = _Transfer( SrcUser, DestUser, Ammount, ReasonGiven ); if(ret) return 2; // No Balance + actualUsername = Bank_GetAcctName(ActualUser); srcName = Bank_GetAcctName(SrcUser); dstName = Bank_GetAcctName(DestUser); - actualUsername = Bank_GetAcctName(ActualUser); - Log_Info("give %i to %s from %s by %s (%s) [balances %i, %i]", - Ammount, dstName, srcName, actualUsername, ReasonGiven, - Bank_GetBalance(SrcUser), Bank_GetBalance(DestUser) + Log_Info("give %i to %s from %s by %s [balances %i, %i] - %s", + Ammount, dstName, srcName, actualUsername, + Bank_GetBalance(SrcUser), Bank_GetBalance(DestUser), + ReasonGiven ); free(srcName); @@ -93,19 +98,19 @@ int DispenseGive(int ActualUser, int SrcUser, int DestUser, int Ammount, const c /** * \brief Add money to an account */ -int DispenseAdd(int User, int ByUser, int Ammount, const char *ReasonGiven) +int DispenseAdd(int ActualUser, int User, int Ammount, const char *ReasonGiven) { int ret; char *dstName, *byName; - ret = Bank_Transfer( Bank_GetAcctByName(COKEBANK_DEBT_ACCT), User, Ammount, ReasonGiven ); + ret = _Transfer( Bank_GetAcctByName(COKEBANK_DEBT_ACCT), User, Ammount, ReasonGiven ); if(ret) return 2; - byName = Bank_GetAcctName(ByUser); + byName = Bank_GetAcctName(ActualUser); dstName = Bank_GetAcctName(User); - Log_Info("add %i to %s by %s (%s) [balance %i]", - Ammount, dstName, byName, ReasonGiven, Bank_GetBalance(User) + Log_Info("add %i to %s by %s [balance %i] - %s", + Ammount, dstName, byName, Bank_GetBalance(User), ReasonGiven ); free(byName); @@ -113,3 +118,63 @@ int DispenseAdd(int User, int ByUser, int Ammount, const char *ReasonGiven) return 0; } + +/** + * \brief Donate money to the club + */ +int DispenseDonate(int ActualUser, int User, int Ammount, const char *ReasonGiven) +{ + int ret; + char *srcName, *byName; + + if( Ammount < 0 ) return 2; + + ret = _Transfer( User, Bank_GetAcctByName(COKEBANK_DEBT_ACCT), Ammount, ReasonGiven ); + if(ret) return 2; + + byName = Bank_GetAcctName(ActualUser); + srcName = Bank_GetAcctName(User); + + Log_Info("donate %i from %s by %s [balance %i] - %s", + Ammount, srcName, byName, Bank_GetBalance(User), ReasonGiven + ); + + free(byName); + free(srcName); + + return 0; +} + +// --- Internal Functions --- +int _GetMinBalance(int Account) +{ + int flags = Bank_GetFlags(Account); + + // - Internal accounts have no lower bound + if( flags & USER_FLAG_INTERNAL ) return INT_MIN; + + // Admin to -$10 + if( flags & USER_FLAG_ADMIN ) return -1000; + + // Coke to -$10 + if( flags & USER_FLAG_COKE ) return -500; + + // Anyone else, non-negative + return 0; +} + +int _Transfer(int Source, int Destination, int Ammount, const char *Reason) +{ + if( Ammount < 0 ) + { + if( Bank_GetBalance(Source) + Ammount < _GetMinBalance(Source) ) + return 1; + } + else + { + if( Bank_GetBalance(Destination) - Ammount < _GetMinBalance(Destination) ) + return 1; + } + + return Bank_Transfer(Source, Destination, Ammount, Reason); +} diff --git a/src/server/server.c b/src/server/server.c index c997c43..0b9005d 100644 --- a/src/server/server.c +++ b/src/server/server.c @@ -66,6 +66,7 @@ void _SendUserInfo(tClient *Client, int UserID); void Server_Cmd_USERADD(tClient *Client, char *Args); void Server_Cmd_USERFLAGS(tClient *Client, char *Args); // --- Helpers --- + int Server_int_ParseFlags(tClient *Client, const char *Str, int *Mask, int *Value); int sendf(int Socket, const char *Format, ...); // === CONSTANTS === @@ -640,6 +641,64 @@ void Server_Cmd_GIVE(tClient *Client, char *Args) } } +void Server_Cmd_DONATE(tClient *Client, char *Args) +{ + char *ammount, *reason; + int iAmmount; + int thisUid; + + if( !Client->bIsAuthed ) { + sendf(Client->Socket, "401 Not Authenticated\n"); + return ; + } + + ammount = Args; + + // Get the start of the reason + reason = strchr(Args, ' '); + if( !ammount ) { + sendf(Client->Socket, "407 Invalid Argument, expected 2 parameters, 1 encountered\n"); + return ; + } + *reason = '\0'; + reason ++; + + // Check the end of the reason + if( strchr(reason, ' ') ) { + sendf(Client->Socket, "407 Invalid Argument, expected 2 parameters, more encountered\n"); + return ; + } + + // Parse ammount + iAmmount = atoi(ammount); + if( iAmmount <= 0 ) { + sendf(Client->Socket, "407 Invalid Argument, ammount must be > zero\n"); + return ; + } + + // Handle effective users + if( Client->EffectiveUID != -1 ) { + thisUid = Client->EffectiveUID; + } + else { + thisUid = Client->UID; + } + + // Do give + switch( DispenseDonate(Client->UID, thisUid, iAmmount, reason) ) + { + case 0: + sendf(Client->Socket, "200 Give OK\n"); + return ; + case 2: + sendf(Client->Socket, "402 Poor You\n"); + return ; + default: + sendf(Client->Socket, "500 Unknown error\n"); + return ; + } +} + void Server_Cmd_ADD(tClient *Client, char *Args) { char *user, *ammount, *reason; @@ -695,7 +754,7 @@ void Server_Cmd_ADD(tClient *Client, char *Args) } // Do give - switch( DispenseAdd(uid, Client->UID, iAmmount, reason) ) + switch( DispenseAdd(Client->UID, uid, iAmmount, reason) ) { case 0: sendf(Client->Socket, "200 Add OK\n"); @@ -815,13 +874,13 @@ void _SendUserInfo(tClient *Client, int UserID) type = "internal"; } else if( flags & USER_FLAG_COKE ) { - if( flags & USER_FLAG_WHEEL ) - type = "coke,wheel"; + if( flags & USER_FLAG_ADMIN ) + type = "coke,admin"; else type = "coke"; } - else if( flags & USER_FLAG_WHEEL ) { - type = "wheel"; + else if( flags & USER_FLAG_ADMIN ) { + type = "admin"; } else { type = "user"; @@ -845,8 +904,8 @@ void Server_Cmd_USERADD(tClient *Client, char *Args) char *username, *space; // Check permissions - if( !(Bank_GetFlags(Client->UID) & USER_FLAG_WHEEL) ) { - sendf(Client->Socket, "403 Not Wheel\n"); + if( !(Bank_GetFlags(Client->UID) & USER_FLAG_ADMIN) ) { + sendf(Client->Socket, "403 Not a coke admin\n"); return ; } @@ -873,8 +932,8 @@ void Server_Cmd_USERFLAGS(tClient *Client, char *Args) int uid; // Check permissions - if( !(Bank_GetFlags(Client->UID) & USER_FLAG_WHEEL) ) { - sendf(Client->Socket, "403 Not Wheel\n"); + if( !(Bank_GetFlags(Client->UID) & USER_FLAG_ADMIN) ) { + sendf(Client->Socket, "403 Not a coke admin\n"); return ; } @@ -902,55 +961,8 @@ void Server_Cmd_USERFLAGS(tClient *Client, char *Args) } // Parse flags - do { - int bRemove = 0; - int i; - struct { - const char *Name; - int Mask; - int Value; - } cFLAGS[] = { - {"disabled", USER_FLAG_DISABLED, USER_FLAG_DISABLED} - ,{"door", USER_FLAG_DOORGROUP, USER_FLAG_DOORGROUP} - ,{"coke", USER_FLAG_COKE, USER_FLAG_COKE} - ,{"wheel", USER_FLAG_WHEEL, USER_FLAG_WHEEL} - // ,{"internal", USER_FLAG_INTERNAL, USER_FLAG_INTERNAL} - }; - const int ciNumFlags = sizeof(cFLAGS)/sizeof(cFLAGS[0]); - - while( *flags == ' ' ) flags ++; // Eat whitespace - space = strchr(flags, ','); // Find the end of the flag - if(space) *space = '\0'; - - // Check for inversion/removal - if( *flags == '!' || *flags == '-' ) { - bRemove = 1; - flags ++; - } - else if( *flags == '+' ) { - flags ++; - } - - // Check flag values - for( i = 0; i < ciNumFlags; i ++ ) - { - if( strcmp(flags, cFLAGS[i].Name) == 0 ) { - mask |= cFLAGS[i].Mask; - value &= ~cFLAGS[i].Mask; - if( !bRemove ) - value |= cFLAGS[i].Value; - break; - } - } - - // Error check - if( i == ciNumFlags ) { - sendf(Client->Socket, "407 Unknown flag value '%s'\n", flags); - return ; - } - - flags = space + 1; - } while(space); + if( Server_int_ParseFlags(Client, flags, &mask, &value) ) + return ; // Apply flags Bank_SetFlags(uid, mask, value); @@ -982,3 +994,70 @@ int sendf(int Socket, const char *Format, ...) return send(Socket, buf, len, 0); } } + +int Server_int_ParseFlags(tClient *Client, const char *Str, int *Mask, int *Value) +{ + struct { + const char *Name; + int Mask; + int Value; + } cFLAGS[] = { + {"disabled", USER_FLAG_DISABLED, USER_FLAG_DISABLED} + ,{"door", USER_FLAG_DOORGROUP, USER_FLAG_DOORGROUP} + ,{"coke", USER_FLAG_COKE, USER_FLAG_COKE} + ,{"admin", USER_FLAG_ADMIN, USER_FLAG_ADMIN} + ,{"internal", USER_FLAG_INTERNAL, USER_FLAG_INTERNAL} + }; + const int ciNumFlags = sizeof(cFLAGS)/sizeof(cFLAGS[0]); + + char *space; + + *Mask = 0; + *Value = 0; + + do { + int bRemove = 0; + int i; + int len; + + while( *Str == ' ' ) Str ++; // Eat whitespace + space = strchr(Str, ','); // Find the end of the flag + if(space) + len = space - Str; + else + len = strlen(Str); + + // Check for inversion/removal + if( *Str == '!' || *Str == '-' ) { + bRemove = 1; + Str ++; + } + else if( *Str == '+' ) { + Str ++; + } + + // Check flag values + for( i = 0; i < ciNumFlags; i ++ ) + { + if( strncmp(Str, cFLAGS[i].Name, len) == 0 ) { + *Mask |= cFLAGS[i].Mask; + *Value &= ~cFLAGS[i].Mask; + if( !bRemove ) + *Value |= cFLAGS[i].Value; + break; + } + } + + // Error check + if( i == ciNumFlags ) { + char val[len+1]; + strncpy(val, Str, len+1); + sendf(Client->Socket, "407 Unknown flag value '%s'\n", val); + return -1; + } + + Str = space + 1; + } while(space); + + return 0; +} -- 2.20.1