SQLite cokebank feature complete, bugfix time
authorJohn Hodge <[email protected]>
Sun, 30 Jan 2011 10:55:58 +0000 (18:55 +0800)
committerJohn Hodge <[email protected]>
Sun, 30 Jan 2011 10:55:58 +0000 (18:55 +0800)
RunServerTest
notes/proto.txt
src/Makefile
src/cokebank [deleted symlink]
src/cokebank.h
src/cokebank_basic/Makefile
src/cokebank_basic/main.c
src/cokebank_sqlite/main.c
src/server/common.h
src/server/dispense.c
src/server/server.c

index 5bedb53..b117c70 100755 (executable)
@@ -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
index f82d27b..dc11783 100644 (file)
@@ -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 <username>\n
-s      100 SALT <string>\n or 100 User Set\n   (If no salt used)
+s      202 SALT <string>\n or 100 User Set\n   (If no salt used)
 c      PASS <hash>\n   (Hex-Encoded SHA-1 Hash of <username><salt><password>)
 s      200 Auth OK\n or 401 Auth Failure\n
 User is now authenticated
@@ -50,7 +51,7 @@ c     AUTOAUTH <username>\n
 s      200 Auth OK\n or 404 Bad Username\n or 401 Untrusted\n
 --- Alternate Method (MIFARE Authentication)
 c      MIFARE <card_id_hex>\n
-s      200 Auth OK as <username>\n or 404 Bad Card ID\n or 401 Untrusted\n
+s      200 Auth OK as <username>\n or 401 Untrusted\n or 404 Bad Card ID\n
 
 --- Set effective user (User in `dispense -u`) ---
 c      SETEUSER <username>\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 <item_id>\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 <user> <ammount> <reason>\n
 s      200 Give OK\n or 402 Poor You\n or 404 Bad User\n
+--- Donate to the club ---
+c      DONATE <ammount> <reason>\n
+s      200 Give OK\n or 402 Poor You\n
 --- Alter balance ---
 c      ADD <user> <ammount> <reason>\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 <item_id>\n
 s      202 Item <item_id> <price> <description>\n
 
 --- Get Users' Balances ---
- <max balance> and <min balance> can be '-' to indicate "none"
-c      ENUM_USERS[ <min balance> [<max balance>]]\n
+c      ENUM_USERS[ min:<balance>][ max:<balance>][ flags:<flagset>][ lastseen:<unix_timestamp>][ sort:<field>[-desc]]\n
 s      201 Users <count>\n
 s      202 User <username> <balance> <flags>\n
     ...
 s      200 List End\n
+<balance>      Integer balance value (in cents)
+<flagset>      Flag values (same format as USER_FLAGS)
+<unix_timestamp>       Number of seconds since 1/Jan/1970
+<field>        Sort field (name,balance,lastseen)
 --- Get a User's Balance ---
 c      USER_INFO\n
 s      202 User <username> <balance> <flags>\n
@@ -93,7 +100,7 @@ c    USER_ADD <username>\n
 s      200 User Added\n or 403 Not Wheel\n or 404 User Exists\n
 --- Set user flags ---
  <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 <username> <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 ---
index c1073d9..f577661 100644 (file)
@@ -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 (symlink)
index eff8399..0000000
+++ /dev/null
@@ -1 +0,0 @@
-cokebank_basic
\ No newline at end of file
index ae244a2..d94e36f 100644 (file)
@@ -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)
index 6145889..e5b4181 100644 (file)
@@ -1,5 +1,5 @@
 
-BIN := ../../cokebank.so
+BIN := ../../cokebank_basic.so
 OBJ := main.o
 
 CPPFLAGS := 
index d5d7f25..0e7956a 100644 (file)
@@ -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
index e962b9d..26282dc 100644 (file)
 #include "../cokebank.h"
 #include <sqlite3.h>
 
-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);
index ec9e38a..8aaf4c0 100644 (file)
@@ -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, ...);
index b77fa9c..d55c18d 100644 (file)
@@ -2,6 +2,10 @@
  */
 #include "common.h"
 #include <stdlib.h>
+#include <limits.h>
+
+ 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);
+}
index c997c43..0b9005d 100644 (file)
@@ -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;
+}

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