From: John Hodge Date: Thu, 6 Jan 2011 04:25:45 +0000 (+0800) Subject: User enumeration implemented X-Git-Url: https://git.ucc.asn.au/?a=commitdiff_plain;h=ca788d1ed62b100c213fa9de432f969d85b136e9;p=tpg%2Fopendispense2.git User enumeration implemented - TODO: Implement and - 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) --- diff --git a/proto.txt b/proto.txt index 2565ad0..ac2d139 100644 --- 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 ( ...) +201 Command succeeded, multiple lines follow () 202 Command succeeded, per-command format 400 Unknown Command 401 Not Authenticated (or Authentication failure) @@ -57,11 +57,12 @@ s 201 Items ...\n c ITEM_INFO \n s 202 Item \n --- Get Users' Balances --- -c ENUM_USERS[ ]\n + and can be '-' to indicate "none" +c ENUM_USERS[ []]\n s 201 Users \n s 202 User \n ... -s 200 List End +s 200 List End\n --- Get a User's Balance --- c USER_INFO\n s 202 User \n diff --git a/src/client/main.c b/src/client/main.c index d4af6dd..e1de523 100644 --- a/src/client/main.c +++ b/src/client/main.c @@ -25,6 +25,7 @@ #include // 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 diff --git a/src/cokebank_basic/bank.c b/src/cokebank_basic/bank.c index 1b356e7..86a94b1 100644 --- a/src/cokebank_basic/bank.c +++ b/src/cokebank_basic/bank.c @@ -15,14 +15,6 @@ #include #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 { diff --git a/src/cokebank_basic/common.h b/src/cokebank_basic/common.h index ce6854f..f7744dd 100644 --- a/src/cokebank_basic/common.h +++ b/src/cokebank_basic/common.h @@ -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; diff --git a/src/cokebank_basic/main.c b/src/cokebank_basic/main.c index b8aba3f..9892231 100644 --- a/src/cokebank_basic/main.c +++ b/src/cokebank_basic/main.c @@ -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; +} + diff --git a/src/server/common.h b/src/server/common.h index 7020dc8..106597e 100644 --- a/src/server/common.h +++ b/src/server/common.h @@ -10,6 +10,7 @@ #define _COMMON_H_ #include +#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 diff --git a/src/server/dispense.c b/src/server/dispense.c index ae93793..77f07ac 100644 --- a/src/server/dispense.c +++ b/src/server/dispense.c @@ -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; diff --git a/src/server/server.c b/src/server/server.c index 875dd04..2ee3fa7 100644 --- a/src/server/server.c +++ b/src/server/server.c @@ -15,11 +15,15 @@ #include #include #include +#include +#include // 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) {