From df71d48cc0082a5333203384cfbce040cf54a155 Mon Sep 17 00:00:00 2001
From: John Hodge <tpg@mutabah.net>
Date: Wed, 2 Feb 2011 21:24:15 +0800
Subject: [PATCH] Fixes to bugs pointed by [BOB]

- Error in SQL code (assumed time_t was 64-bits)
- Lack of `dispense acct =` (still needs client support)
- Bumped the coke read timeout down
- Made balance alter happen after the dispense
---
 cokebank.db                |  1 +
 cokebank.so                |  1 +
 src/cokebank_sqlite/main.c |  5 +--
 src/server/common.h        |  1 +
 src/server/dispense.c      | 74 +++++++++++++++++++++++++++-----------
 src/server/handler_coke.c  |  4 ++-
 src/server/server.c        | 69 +++++++++++++++++++++++++++++++++++
 7 files changed, 132 insertions(+), 23 deletions(-)
 create mode 120000 cokebank.db
 create mode 120000 cokebank.so

diff --git a/cokebank.db b/cokebank.db
new file mode 120000
index 0000000..0508c2f
--- /dev/null
+++ b/cokebank.db
@@ -0,0 +1 @@
+cokebank_sqlite.db
\ No newline at end of file
diff --git a/cokebank.so b/cokebank.so
new file mode 120000
index 0000000..4548509
--- /dev/null
+++ b/cokebank.so
@@ -0,0 +1 @@
+cokebank_sqlite.so
\ No newline at end of file
diff --git a/src/cokebank_sqlite/main.c b/src/cokebank_sqlite/main.c
index c112260..0d5e139 100644
--- a/src/cokebank_sqlite/main.c
+++ b/src/cokebank_sqlite/main.c
@@ -7,6 +7,7 @@
  * This file is licenced under the 3-clause BSD Licence. See the file
  * COPYING for full details.
  */
+#include <inttypes.h>
 #include <stdlib.h>
 #include <limits.h>
 #include <stdio.h>
@@ -386,7 +387,7 @@ tAcctIterator *Bank_Iterator(int FlagMask, int FlagValues, int Flags, int MinMax
 	query = mkstr("SELECT acct_id FROM accounts WHERE 1=1"
 		"%s%s%s%s%s"	// Flags
 		"%s%i"	// Balance
-		"%sdatetime(%lli,'unixepoch')"	// Last seen
+		"%sdatetime(%"PRIu64",'unixepoch')"	// Last seen
 		"%s%s"	// Sort and direction
 		,
 		MAP_FLAG("acct_is_coke", USER_FLAG_COKE),
@@ -395,7 +396,7 @@ tAcctIterator *Bank_Iterator(int FlagMask, int FlagValues, int Flags, int MinMax
 		MAP_FLAG("acct_is_internal", USER_FLAG_INTERNAL),
 		MAP_FLAG("acct_is_disabled", USER_FLAG_DISABLED),
 		balanceClause, MinMaxBalance,
-		lastSeenClause, LastSeen,
+		lastSeenClause, (uint64_t)LastSeen,
 		orderClause, revSort
 		);
 	//printf("query = \"%s\"\n", query);
diff --git a/src/server/common.h b/src/server/common.h
index 8aaf4c0..06a9100 100644
--- a/src/server/common.h
+++ b/src/server/common.h
@@ -81,6 +81,7 @@ extern char	*mkstr(const char *Format, ...);
 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 ActualUser, int User, int Ammount, const char *ReasonGiven);
+extern int	DispenseSet(int ActualUser, int User, int Balance, const char *ReasonGiven);
 extern int	DispenseDonate(int ActualUser, int User, int Ammount, const char *ReasonGiven);
 
 // --- Logging ---
diff --git a/src/server/dispense.c b/src/server/dispense.c
index 09fe567..52d978c 100644
--- a/src/server/dispense.c
+++ b/src/server/dispense.c
@@ -5,6 +5,7 @@
 #include <limits.h>
 
  int	_GetMinBalance(int Account);
+ int	_CanTransfer(int Source, int Destination, int Ammount);
  int	_Transfer(int Source, int Destination, int Ammount, const char *Reason);
 
 // === CODE ===
@@ -15,10 +16,17 @@
  */
 int DispenseItem(int ActualUser, int User, tItem *Item)
 {
-	 int	ret;
+	 int	ret, salesAcct;
 	tHandler	*handler;
 	char	*username, *actualUsername;
-	char	*reason;
+	
+	salesAcct = Bank_GetAcctByName(COKEBANK_SALES_ACCT);
+
+	// Check if the user can afford it
+	if( Item->Price && !_CanTransfer(User, salesAcct, Item->Price) )
+	{
+		return 2;	// 2: No balance
+	}
 	
 	handler = Item->Handler;
 	
@@ -27,16 +35,6 @@ int DispenseItem(int ActualUser, int User, tItem *Item)
 		ret = handler->CanDispense( User, Item->ID );
 		if(ret)	return 1;	// 1: Unable to dispense
 	}
-
-	// Subtract the balance
-	if( Item->Price )
-	{
-		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, Bank_GetAcctByName(COKEBANK_SALES_ACCT), Item->Price, reason);
-		free(reason);
-		if(ret)	return 2;	// 2: No balance
-	}
 	
 	// Get username for debugging
 	username = Bank_GetAcctName(User);
@@ -45,15 +43,22 @@ int DispenseItem(int ActualUser, int User, tItem *Item)
 	if( handler->DoDispense ) {
 		ret = handler->DoDispense( User, Item->ID );
 		if(ret) {
-			Log_Error("Dispense failed after deducting cost (%s dispensing %s - %ic)",
+			Log_Error("Dispense failed (%s dispensing '%s' - %ic)",
 				username, Item->Name, Item->Price);
-			if( Item->Price )
-				_Transfer( Bank_GetAcctByName(COKEBANK_SALES_ACCT), User, Item->Price, "rollback" );
 			free( username );
-			return -1;	// 1: Unkown Error again
+			return -1;	// 1: Unknown Error again
 		}
 	}
 	
+	// Take away money
+	if( Item->Price )
+	{
+		char	*reason;
+		reason = mkstr("Dispense - %s:%i %s", handler->Name, Item->ID, Item->Name);
+		_Transfer( User, salesAcct, Item->Price, reason );
+		free(reason);
+	}
+	
 	actualUsername = Bank_GetAcctName(ActualUser);
 	
 	// And log that it happened
@@ -123,6 +128,26 @@ int DispenseAdd(int ActualUser, int User, int Ammount, const char *ReasonGiven)
 	return 0;
 }
 
+int DispenseSet(int ActualUser, int User, int Balance, const char *ReasonGiven)
+{
+	 int	curBal = Bank_GetBalance(User);
+	char	*byName, *dstName;
+	
+	_Transfer( Bank_GetAcctByName(COKEBANK_DEBT_ACCT), User, Balance-curBal, ReasonGiven );
+	
+	byName = Bank_GetAcctName(ActualUser);
+	dstName = Bank_GetAcctName(User);
+	
+	Log_Info("set balance of %s to %i by %s [balance %i] - %s",
+		dstName, Balance, byName, Bank_GetBalance(User), ReasonGiven
+		);
+	
+	free(byName);
+	free(dstName);
+	
+	return 0;
+}
+
 /**
  * \brief Donate money to the club
  */
@@ -167,18 +192,27 @@ int _GetMinBalance(int Account)
 	return 0;
 }
 
-int _Transfer(int Source, int Destination, int Ammount, const char *Reason)
+/**
+ * \brief Check if a transfer is possible
+ */
+int _CanTransfer(int Source, int Destination, int Ammount)
 {
 	if( Ammount > 0 )
 	{
 		if( Bank_GetBalance(Source) + Ammount < _GetMinBalance(Source) )
-			return 1;
+			return 0;
 	}
 	else
 	{
 		if( Bank_GetBalance(Destination) - Ammount < _GetMinBalance(Destination) )
-			return 1;
+			return 0;
 	}
-	
+	return 1;
+}
+
+int _Transfer(int Source, int Destination, int Ammount, const char *Reason)
+{
+	if( !_CanTransfer(Source, Destination, Ammount) )
+		return 1;
 	return Bank_Transfer(Source, Destination, Ammount, Reason);
 }
diff --git a/src/server/handler_coke.c b/src/server/handler_coke.c
index a6b3cb9..4043cfc 100644
--- a/src/server/handler_coke.c
+++ b/src/server/handler_coke.c
@@ -18,6 +18,8 @@
 #include <fcntl.h>
 #include <regex.h>
 
+#define READ_TIMEOUT	2	// 2 seconds for ReadChar
+
 // === IMPORTS ===
 
 // === PROTOTYPES ===
@@ -191,7 +193,7 @@ char ReadChar()
 	 int	ret;
 	struct timeval	timeout;
 	
-	timeout.tv_sec = 5;	// 5 second timeout
+	timeout.tv_sec = READ_TIMEOUT;
 	timeout.tv_usec = 0;
 	
 	FD_ZERO(&readfs);
diff --git a/src/server/server.c b/src/server/server.c
index 9c36f3d..ca4aceb 100644
--- a/src/server/server.c
+++ b/src/server/server.c
@@ -771,6 +771,75 @@ void Server_Cmd_ADD(tClient *Client, char *Args)
 	}
 }
 
+void Server_Cmd_SET(tClient *Client, char *Args)
+{
+	char	*user, *ammount, *reason;
+	 int	uid, iAmmount;
+	
+	if( !Client->bIsAuthed ) {
+		sendf(Client->Socket, "401 Not Authenticated\n");
+		return ;
+	}
+
+	user = Args;
+
+	ammount = strchr(Args, ' ');
+	if( !ammount ) {
+		sendf(Client->Socket, "407 Invalid Argument, expected 3 parameters, 1 encountered\n");
+		return ;
+	}
+	*ammount = '\0';
+	ammount ++;
+
+	reason = strchr(ammount, ' ');
+	if( !reason ) {
+		sendf(Client->Socket, "407 Invalid Argument, expected 3 parameters, 2 encountered\n");
+		return ;
+	}
+	*reason = '\0';
+	reason ++;
+
+	// Check user permissions
+	if( !(Bank_GetFlags(Client->UID) & USER_FLAG_ADMIN)  ) {
+		sendf(Client->Socket, "403 Not an admin\n");
+		return ;
+	}
+
+	// Get recipient
+	uid = Bank_GetAcctByName(user);
+	if( uid == -1 ) {
+		sendf(Client->Socket, "404 Invalid user\n");
+		return ;
+	}
+	
+	// You can't alter an internal account
+	if( Bank_GetFlags(uid) & USER_FLAG_INTERNAL ) {
+		sendf(Client->Socket, "404 Invalid user\n");
+		return ;
+	}
+
+	// Parse ammount
+	iAmmount = atoi(ammount);
+	if( iAmmount == 0 && ammount[0] != '0' ) {
+		sendf(Client->Socket, "407 Invalid Argument\n");
+		return ;
+	}
+
+	// Do give
+	switch( DispenseSet(Client->UID, uid, iAmmount, reason) )
+	{
+	case 0:
+		sendf(Client->Socket, "200 Add OK\n");
+		return ;
+	case 2:
+		sendf(Client->Socket, "402 Poor Guy\n");
+		return ;
+	default:
+		sendf(Client->Socket, "500 Unknown error\n");
+		return ;
+	}
+}
+
 void Server_Cmd_ENUMUSERS(tClient *Client, char *Args)
 {
 	 int	i, numRet = 0;
-- 
GitLab