== 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)
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
#include <openssl/sha.h> // SHA1
#define USE_NCURSES_INTERFACE 0
+#define DEBUG_TRACE_SERVER 0
// === TYPES ===
typedef struct sItem {
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)
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);
}
}
{
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);
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 ) {
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
vsnprintf(buf, len+1, Format, args);
va_end(args);
- #if DBG_TRACE_SERVER
+ #if DEBUG_TRACE_SERVER
printf("sendf: %s", buf);
#endif
#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);
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 ) {
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;
{
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 {
* 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.
#ifndef _COKEBANK_COMMON_H_
#define _COKEBANK_COMMON_H_
+#include "../cokebank.h"
+
typedef struct sUser {
int UnixID;
int Balance;
char *GetUserName(int User);
int GetUserID(const char *Username);
int GetUserAuth(const char *Username, const char *Password);
+ int GetMaxID(void);
// === GLOBALS ===
FILE *gBank_LogFile;
return ret;
}
+int GetMaxID(void)
+{
+ return giBank_NumUsers;
+}
+
#define _COMMON_H_
#include <regex.h>
+#include "../cokebank.h"
// === CONSTANTS ===
#define DEFAULT_CONFIG_FILE "/etc/opendispense/main.cfg"
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
// 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
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
}
{
int ret;
- ret = Transfer( GetUserID(">liability"), User, Ammount, ReasonGiven );
+ ret = Transfer( GetUserID(COKEBANK_DEBT_ACCT), User, Ammount, ReasonGiven );
if(ret) return 2;
#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
// === TYPES ===
typedef struct sClient
{
+ int Socket; // Client socket ID
int ID; // Client ID
int bIsTrusted; // Is the connection from a trusted host/port
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);
{"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]))
tClient clientInfo = {0};
// Initialise Client info
+ clientInfo.Socket = Socket;
clientInfo.ID = giServer_NextClientID ++;
clientInfo.bIsTrusted = 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
}
}
+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;
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));
}
}
// --- 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)
{