User enumeration implemented
authorJohn Hodge <[email protected]>
Thu, 6 Jan 2011 04:25:45 +0000 (12:25 +0800)
committerJohn Hodge <[email protected]>
Thu, 6 Jan 2011 04:25:45 +0000 (12:25 +0800)
- TODO: Implement <min balance> and <max balance>
- Updated protocol spec for extended ENUM_USERS
- Client Dispense_EnumUsers implemented
 > Changed padding in Dispense_ShowUser
- Fixed client's ReadLine
 > Was not using the remaining data in the buffer
- Created a common "cokebank.h" for server
 > Moved pseudo-account names into this file
- Implemented ENUM_USERS at server side
 > Copied sendf to the server to improve response times (and memory
   usage)

proto.txt
src/client/main.c
src/cokebank_basic/bank.c
src/cokebank_basic/common.h
src/cokebank_basic/main.c
src/server/common.h
src/server/dispense.c
src/server/server.c

index 2565ad0..ac2d139 100644 (file)
--- a/proto.txt
+++ b/proto.txt
@@ -7,7 +7,7 @@ All server responses are on one line and are prefixed by a three digit response
 == Response Codes ==
 100    Information
 200    Command succeeded, no extra information
-201    Command succeeded, array follows (<length> <items> <items> ...)
+201    Command succeeded, multiple lines follow (<length>)
 202    Command succeeded, per-command format
 400    Unknown Command
 401    Not Authenticated (or Authentication failure)
@@ -57,11 +57,12 @@ s   201 Items <count> <item_id> <item_id> ...\n
 c      ITEM_INFO <item_id>\n
 s      202 Item <item_id> <price> <description>\n
 --- Get Users' Balances ---
-c      ENUM_USERS[ <max balance>]\n
+ <max balance> and <min balance> can be '-' to indicate "none"
+c      ENUM_USERS[ <min balance> [<max balance>]]\n
 s      201 Users <count>\n
 s      202 User <username> <balance> <flags>\n
     ...
-s   200 List End
+s   200 List End\n
 --- Get a User's Balance ---
 c      USER_INFO\n
 s      202 User <username> <balance> <flags>\n
index d4af6dd..e1de523 100644 (file)
@@ -25,6 +25,7 @@
 #include <openssl/sha.h>       // SHA1
 
 #define        USE_NCURSES_INTERFACE   0
+#define DEBUG_TRACE_SERVER     0
 
 // === TYPES ===
 typedef struct sItem {
@@ -855,8 +856,54 @@ int Dispense_SetBalance(int Socket, const char *Username, int Ammount, const cha
 
 int Dispense_EnumUsers(int Socket)
 {
-       printf("TODO: Dispense_EnumUsers\n");
-       return -1;
+       char    *buf;
+        int    responseCode;
+        int    nUsers;
+       regmatch_t      matches[4];
+       
+       sendf(Socket, "ENUM_USERS\n");
+       buf = ReadLine(Socket);
+       responseCode = atoi(buf);
+       
+       switch(responseCode)
+       {
+       case 201:       break;  // Ok, length follows
+       
+       default:
+               fprintf(stderr, "Unknown response code %i\n%s\n", responseCode, buf);
+               free(buf);
+               return -1;
+       }
+       
+       // Get count (not actually used)
+       RunRegex(&gArrayRegex, buf, 4, matches, "Malformed server response");
+       nUsers = atoi( buf + matches[3].rm_so );
+       printf("%i users returned\n", nUsers);
+       
+       // Free string
+       free(buf);
+       
+       // Read returned users
+       do {
+               buf = ReadLine(Socket);
+               responseCode = atoi(buf);
+               
+               if( responseCode != 202 )       break;
+               
+               _PrintUserLine(buf);
+               free(buf);
+       } while(responseCode == 202);
+       
+       // Check final response
+       if( responseCode != 200 ) {
+               fprintf(stderr, "Unknown response code %i\n%s\n", responseCode, buf);
+               free(buf);
+               return -1;
+       }
+       
+       free(buf);
+       
+       return 0;
 }
 
 int Dispense_ShowUser(int Socket, const char *Username)
@@ -913,7 +960,7 @@ void _PrintUserLine(const char *Line)
                flags[flagsLen] = '\0';
                
                bal = atoi(Line + matches[4].rm_so);
-               printf("%-15s: $%i.%02i (%s)\n", username, bal/100, bal%100, flags);
+               printf("%-15s: $%4i.%02i (%s)\n", username, bal/100, bal%100, flags);
        }
 }
 
@@ -924,12 +971,13 @@ char *ReadLine(int Socket)
 {
        static char     buf[BUFSIZ];
        static int      bufPos = 0;
+       static int      bufValid = 0;
         int    len;
        char    *newline = NULL;
         int    retLen = 0;
        char    *ret = malloc(10);
        
-       #if DBG_TRACE_SERVER
+       #if DEBUG_TRACE_SERVER
        printf("ReadLine: ");
        #endif
        fflush(stdout);
@@ -938,8 +986,13 @@ char *ReadLine(int Socket)
        
        while( !newline )
        {
-               len = recv(Socket, buf+bufPos, BUFSIZ-1-bufPos, 0);
-               buf[bufPos+len] = '\0';
+               if( bufValid ) {
+                       len = bufValid;
+               }
+               else {
+                       len = recv(Socket, buf+bufPos, BUFSIZ-1-bufPos, 0);
+                       buf[bufPos+len] = '\0';
+               }
                
                newline = strchr( buf+bufPos, '\n' );
                if( newline ) {
@@ -951,12 +1004,14 @@ char *ReadLine(int Socket)
                strcat( ret, buf+bufPos );
                
                if( newline ) {
-                       bufPos += newline - (buf+bufPos) + 1;
+                        int    newLen = newline - (buf+bufPos) + 1;
+                       bufValid = len - newLen;
+                       bufPos += newLen;
                }
                if( len + bufPos == BUFSIZ - 1 )        bufPos = 0;
        }
        
-       #if DBG_TRACE_SERVER
+       #if DEBUG_TRACE_SERVER
        printf("%i '%s'\n", retLen, ret);
        #endif
        
@@ -978,7 +1033,7 @@ int sendf(int Socket, const char *Format, ...)
                vsnprintf(buf, len+1, Format, args);
                va_end(args);
                
-               #if DBG_TRACE_SERVER
+               #if DEBUG_TRACE_SERVER
                printf("sendf: %s", buf);
                #endif
                
index 1b356e7..86a94b1 100644 (file)
 #include <pwd.h>
 #include "common.h"
 
-enum {
-       FLAG_TYPEMASK    = 0x03,
-       USER_TYPE_NORMAL = 0x00,
-       USER_TYPE_COKE   = 0x01,
-       USER_TYPE_WHEEL  = 0x02,
-       USER_TYPE_GOD    = 0x03
-};
-
 // === PROTOTYPES ===
 static int     GetUnixID(const char *Username);
 
@@ -131,7 +123,7 @@ int Bank_AddUser(const char *Username)
        gaBank_Users[giBank_NumUsers].Balance = 0;
        gaBank_Users[giBank_NumUsers].Flags = 0;
        
-       if( strcmp(Username, ">liability") == 0 ) {
+       if( strcmp(Username, COKEBANK_DEBT_ACCT) == 0 ) {
                gaBank_Users[giBank_NumUsers].Flags = USER_TYPE_GOD;    // No minium
        }
        else if( strcmp(Username, "root") == 0 ) {
@@ -160,10 +152,10 @@ char *Bank_GetUserName(int ID)
                return NULL;
        
        if( gaBank_Users[ID].UnixID == -1 )
-               return strdup(">sales");
+               return strdup(COKEBANK_SALES_ACCT);
 
        if( gaBank_Users[ID].UnixID == -2 )
-               return strdup(">liability");
+               return strdup(COKEBANK_DEBT_ACCT);
 
        pwd = getpwuid(gaBank_Users[ID].UnixID);
        if( !pwd )      return NULL;
@@ -175,10 +167,10 @@ static int GetUnixID(const char *Username)
 {
         int    uid;
 
-       if( strcmp(Username, ">sales") == 0 ) { // Pseudo account that sales are made into
+       if( strcmp(Username, COKEBANK_SALES_ACCT) == 0 ) {      // Pseudo account that sales are made into
                uid = -1;
        }
-       else if( strcmp(Username, ">liability") == 0 ) {        // Pseudo acount that money is added from
+       else if( strcmp(Username, COKEBANK_DEBT_ACCT) == 0 ) {  // Pseudo acount that money is added from
                uid = -2;
        }
        else {
index ce6854f..f7744dd 100644 (file)
@@ -2,7 +2,7 @@
  * OpenDispense 2 
  * UCC (University [of WA] Computer Club) Electronic Accounting System
  *
- * cokebank.c - Coke-Bank management
+ * cokebank_basic/common.h - Coke-Bank management
  *
  * This file is licenced under the 3-clause BSD Licence. See the file COPYING
  * for full details.
@@ -10,6 +10,8 @@
 #ifndef _COKEBANK_COMMON_H_
 #define _COKEBANK_COMMON_H_
 
+#include "../cokebank.h"
+
 typedef struct sUser {
         int    UnixID;
         int    Balance;
index b8aba3f..9892231 100644 (file)
@@ -20,6 +20,7 @@ void  Init_Cokebank(const char *Argument);
 char   *GetUserName(int User);
  int   GetUserID(const char *Username); 
  int   GetUserAuth(const char *Username, const char *Password);
+ int   GetMaxID(void);
 
 // === GLOBALS ===
 FILE   *gBank_LogFile;
@@ -109,3 +110,8 @@ int GetUserID(const char *Username)
        return ret;
 }
 
+int GetMaxID(void)
+{
+       return giBank_NumUsers;
+}
+
index 7020dc8..106597e 100644 (file)
@@ -10,6 +10,7 @@
 #define _COMMON_H_
 
 #include <regex.h>
+#include "../cokebank.h"
 
 // === CONSTANTS ===
 #define        DEFAULT_CONFIG_FILE     "/etc/opendispense/main.cfg"
@@ -83,11 +84,4 @@ extern int   DispenseAdd(int User, int ByUser, int Ammount, const char *ReasonGive
 extern void    Log_Error(const char *Format, ...);
 extern void    Log_Info(const char *Format, ...);
 
-// --- Cokebank Functions ---
-extern int     Transfer(int SourceUser, int DestUser, int Ammount, const char *Reason);
-extern int     GetFlags(int User);
-extern int     GetBalance(int User);
-extern char    *GetUserName(int User);
-extern int     GetUserID(const char *Username);
-
 #endif
index ae93793..77f07ac 100644 (file)
@@ -27,7 +27,7 @@ int DispenseItem(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 = Transfer( User, GetUserID(">sales"), Item->Price, reason);
+       ret = Transfer( User, GetUserID(COKEBANK_SALES_ACCT), Item->Price, reason);
        free(reason);
        if(ret) return 2;       // 2: No balance
        
@@ -40,7 +40,7 @@ int DispenseItem(int User, tItem *Item)
                if(ret) {
                        Log_Error("Dispense failed after deducting cost (%s dispensing %s - %ic)",
                                username, Item->Name, Item->Price);
-                       Transfer( GetUserID(">sales"), User, Item->Price, "rollback" );
+                       Transfer( GetUserID(COKEBANK_SALES_ACCT), User, Item->Price, "rollback" );
                        free( username );
                        return -1;      // 1: Unkown Error again
                }
@@ -78,7 +78,7 @@ int DispenseAdd(int User, int ByUser, int Ammount, const char *ReasonGiven)
 {
         int    ret;
        
-       ret = Transfer( GetUserID(">liability"), User, Ammount, ReasonGiven );
+       ret = Transfer( GetUserID(COKEBANK_DEBT_ACCT), User, Ammount, ReasonGiven );
        
        if(ret) return 2;
        
index 875dd04..2ee3fa7 100644 (file)
 #include <arpa/inet.h>
 #include <unistd.h>
 #include <string.h>
+#include <limits.h>
+#include <stdarg.h>
 
 // HACKS
 #define HACK_TPG_NOAUTH        1
 #define HACK_ROOT_NOAUTH       1
 
+#define        DEBUG_TRACE_CLIENT      1
+
 // Statistics
 #define MAX_CONNECTION_QUEUE   5
 #define INPUT_BUFFER_SIZE      256
@@ -32,6 +36,7 @@
 // === TYPES ===
 typedef struct sClient
 {
+        int    Socket; // Client socket ID
         int    ID;     // Client ID
         
         int    bIsTrusted;     // Is the connection from a trusted host/port
@@ -57,8 +62,10 @@ char *Server_Cmd_ITEMINFO(tClient *Client, char *Args);
 char   *Server_Cmd_DISPENSE(tClient *Client, char *Args);
 char   *Server_Cmd_GIVE(tClient *Client, char *Args);
 char   *Server_Cmd_ADD(tClient *Client, char *Args);
+char   *Server_Cmd_ENUMUSERS(tClient *Client, char *Args);
 char   *Server_Cmd_USERINFO(tClient *Client, char *Args);
 // --- Helpers ---
+ int   sendf(int Socket, const char *Format, ...);
  int   GetUserAuth(const char *Salt, const char *Username, const uint8_t *Hash);
 void   HexBin(uint8_t *Dest, char *Src, int BufSize);
 
@@ -78,6 +85,7 @@ struct sClientCommand {
        {"DISPENSE", Server_Cmd_DISPENSE},
        {"GIVE", Server_Cmd_GIVE},
        {"ADD", Server_Cmd_ADD},
+       {"ENUM_USERS", Server_Cmd_ENUMUSERS},
        {"USER_INFO", Server_Cmd_USERINFO}
 };
 #define NUM_COMMANDS   (sizeof(gaServer_Commands)/sizeof(gaServer_Commands[0]))
@@ -185,6 +193,7 @@ void Server_HandleClient(int Socket, int bTrusted)
        tClient clientInfo = {0};
        
        // Initialise Client info
+       clientInfo.Socket = Socket;
        clientInfo.ID = giServer_NextClientID ++;
        clientInfo.bIsTrusted = bTrusted;
        
@@ -210,7 +219,7 @@ void Server_HandleClient(int Socket, int bTrusted)
                        ret = Server_ParseClientCommand(&clientInfo, start);
                        
                        #if DEBUG_TRACE_CLIENT
-                       //printf("ret = %s", ret);
+                       printf("send : %s", ret);
                        #endif
                        
                        // `ret` is a string on the heap
@@ -574,6 +583,47 @@ char *Server_Cmd_ADD(tClient *Client, char *Args)
        }
 }
 
+char *Server_Cmd_ENUMUSERS(tClient *Client, char *Args)
+{
+        int    i, numRet = 0;
+        int    maxBal = INT_MAX, minBal = INT_MIN;
+        int    numUsr = GetMaxID();
+       
+       // Parse arguments
+       //minBal = atoi(Args);
+       
+       // Get return number
+       for( i = 0; i < numUsr; i ++ )
+       {
+               int bal = GetBalance(i);
+               
+               if( bal == INT_MIN )    continue;
+               
+               if( bal < minBal )      continue;
+               if( bal > maxBal )      continue;
+               
+               numRet ++;
+       }
+       
+       // Send count
+       sendf(Client->Socket, "201 Users %i\n", numRet);
+       
+       for( i = 0; i < numUsr; i ++ )
+       {
+               int bal = GetBalance(i);
+               
+               if( bal == INT_MIN )    continue;
+               
+               if( bal < minBal )      continue;
+               if( bal > maxBal )      continue;
+               
+               // TODO: User flags
+               sendf(Client->Socket, "202 User %s %i user\n", GetUserName(i), GetBalance(i));
+       }
+       
+       return strdup("200 List End\n");
+}
+
 char *Server_Cmd_USERINFO(tClient *Client, char *Args)
 {
         int    uid;
@@ -587,6 +637,7 @@ char *Server_Cmd_USERINFO(tClient *Client, char *Args)
        uid = GetUserID(user);
        if( uid == -1 ) return strdup("404 Invalid user");
 
+       // TODO: User flags/type
        return mkstr("202 User %s %i user\n", user, GetBalance(uid));
 }
 
@@ -632,6 +683,29 @@ int GetUserAuth(const char *Salt, const char *Username, const uint8_t *ProvidedH
 }
 
 // --- INTERNAL HELPERS ---
+int sendf(int Socket, const char *Format, ...)
+{
+       va_list args;
+        int    len;
+       
+       va_start(args, Format);
+       len = vsnprintf(NULL, 0, Format, args);
+       va_end(args);
+       
+       {
+               char    buf[len+1];
+               va_start(args, Format);
+               vsnprintf(buf, len+1, Format, args);
+               va_end(args);
+               
+               #if DEBUG_TRACE_CLIENT
+               printf("sendf: %s", buf);
+               #endif
+               
+               return send(Socket, buf, len, 0);
+       }
+}
+
 // TODO: Move to another file
 void HexBin(uint8_t *Dest, char *Src, int BufSize)
 {

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