Misc changes - Fixed coke, shut up boot, fixed disabled users dispensing
[tpg/opendispense2.git] / src / server / dispense.c
1 /**
2  */
3 #include "common.h"
4 #include <stdlib.h>
5 #include <limits.h>
6 #include <string.h>
7
8  int    _GetMinBalance(int Account);
9  int    _CanTransfer(int Source, int Destination, int Ammount);
10  int    _Transfer(int Source, int Destination, int Ammount, const char *Reason);
11  int    _GetSalesAcct(tItem *Item);
12
13 // === CODE ===
14 /**
15  * \brief Dispense an item for a user
16  * 
17  * The core of the dispense system, I kinda like it :)
18  */
19 int DispenseItem(int ActualUser, int User, tItem *Item)
20 {
21          int    ret, salesAcct;
22         tHandler        *handler;
23         char    *username, *actualUsername;
24         
25         handler = Item->Handler;
26         
27         salesAcct = _GetSalesAcct(Item);
28
29         // Check if the user can afford it
30         if( Item->Price && !_CanTransfer(User, salesAcct, Item->Price) )
31         {
32                 return 2;       // 2: No balance
33         }
34         
35         // HACK: Naming a slot "dead" disables it
36         if( strcmp(Item->Name, "dead") == 0 )
37                 return 1;
38         
39         // Check if the dispense is possible
40         if( handler->CanDispense ) {
41                 ret = handler->CanDispense( User, Item->ID );
42                 if(ret) return 1;       // 1: Unable to dispense
43         }
44         
45         // Get username for debugging
46         username = Bank_GetAcctName(User);
47         
48         // Actually do the dispense
49         if( handler->DoDispense ) {
50                 ret = handler->DoDispense( User, Item->ID );
51                 if(ret) {
52                         Log_Error("Dispense failed (%s dispensing %s:%i '%s')",
53                                 username, Item->Handler->Name, Item->ID, Item->Name);
54                         free( username );
55                         return -1;      // 1: Unknown Error again
56                 }
57         }
58         
59         // Take away money
60         if( Item->Price )
61         {
62                 char    *reason;
63                 reason = mkstr("Dispense - %s:%i %s", handler->Name, Item->ID, Item->Name);
64                 _Transfer( User, salesAcct, Item->Price, reason );
65                 free(reason);
66         }
67         
68         actualUsername = Bank_GetAcctName(ActualUser);
69         
70         // And log that it happened
71         Log_Info("dispense '%s' (%s:%i) for %s by %s [cost %i, balance %i]",
72                 Item->Name, handler->Name, Item->ID,
73                 username, actualUsername, Item->Price, Bank_GetBalance(User)
74                 );
75         
76         free( username );
77         free( actualUsername );
78         return 0;       // 0: EOK
79 }
80
81 /**
82  * \brief Refund a dispense
83  */
84 int DispenseRefund(int ActualUser, int DestUser, tItem *Item, int OverridePrice)
85 {
86          int    ret;
87          int    src_acct, price;
88         char    *username, *actualUsername;
89
90         src_acct = _GetSalesAcct(Item);
91
92         if( OverridePrice > 0 )
93                 price = OverridePrice;
94         else
95                 price = Item->Price;
96
97         ret = _Transfer( src_acct, DestUser, price, "Refund");
98         if(ret) return ret;
99
100         username = Bank_GetAcctName(DestUser);
101         actualUsername = Bank_GetAcctName(ActualUser);
102         
103         Log_Info("refund '%s' (%s:%i) to %s by %s [cost %i, balance %i]",
104                 Item->Name, Item->Handler->Name, Item->ID,
105                 username, actualUsername, price, Bank_GetBalance(DestUser)
106                 );
107
108         free(username);
109         free(actualUsername);
110
111         return 0;
112 }
113
114 /**
115  * \brief Give money from one user to another
116  */
117 int DispenseGive(int ActualUser, int SrcUser, int DestUser, int Ammount, const char *ReasonGiven)
118 {
119          int    ret;
120         char    *actualUsername;
121         char    *srcName, *dstName;
122         
123         // HACK: Naming a slot "dead" disables it (catch for snack)
124         if( strcmp(ReasonGiven, "dead") == 0 )
125                 return 1;
126         
127         if( Ammount < 0 )       return 1;       // Um... negative give? Not on my watch!
128         
129         ret = _Transfer( SrcUser, DestUser, Ammount, ReasonGiven );
130         if(ret) return 2;       // No Balance
131         
132         
133         actualUsername = Bank_GetAcctName(ActualUser);
134         srcName = Bank_GetAcctName(SrcUser);
135         dstName = Bank_GetAcctName(DestUser);
136         
137         Log_Info("give %i from %s to %s by %s [balances %i, %i] - %s",
138                 Ammount, srcName, dstName, actualUsername,
139                 Bank_GetBalance(SrcUser), Bank_GetBalance(DestUser),
140                 ReasonGiven
141                 );
142         
143         free(srcName);
144         free(dstName);
145         free(actualUsername);
146         
147         return 0;
148 }
149
150 #if 0 // Dead Code
151 /**
152  * \brief Move money from one user to another (Admin Only)
153  */
154 int DispenseTransfer(int ActualUser, int SrcUser, int DestUser, int Ammount, const char *ReasonGiven)
155 {
156          int    ret;
157         char    *actualUsername;
158         char    *srcName, *dstName;
159
160         // Make sure the user is an admin
161         if( !(Bank_GetFlags(ActualUser) & USER_FLAG_ADMIN) )
162                 return 1;
163         
164         ret = _Transfer( SrcUser, DestUser, Ammount, ReasonGiven );
165         if(ret) return 2;       // No Balance
166         
167         
168         actualUsername = Bank_GetAcctName(ActualUser);
169         srcName = Bank_GetAcctName(SrcUser);
170         dstName = Bank_GetAcctName(DestUser);
171         
172         Log_Info("move %i from %s to %s by %s [balances %i, %i] - %s",
173                 Ammount, srcName, dstName, actualUsername,
174                 Bank_GetBalance(SrcUser), Bank_GetBalance(DestUser),
175                 ReasonGiven
176                 );
177         
178         free(srcName);
179         free(dstName);
180         free(actualUsername);
181         
182         return 0;
183 }
184 #endif
185
186 /**
187  * \brief Add money to an account
188  */
189 int DispenseAdd(int ActualUser, int User, int Ammount, const char *ReasonGiven)
190 {
191          int    ret;
192         char    *dstName, *byName;
193         
194 #if DISPENSE_ADD_BELOW_MIN
195         ret = _Transfer( Bank_GetAcctByName(COKEBANK_DEBT_ACCT,1), User, Ammount, ReasonGiven );
196 #else
197         ret = Bank_Transfer( Bank_GetAcctByName(COKEBANK_DEBT_ACCT,1), User, Ammount, ReasonGiven );
198 #endif
199         if(ret) return 2;
200         
201         byName = Bank_GetAcctName(ActualUser);
202         dstName = Bank_GetAcctName(User);
203         
204         Log_Info("add %i to %s by %s [balance %i] - %s",
205                 Ammount, dstName, byName, Bank_GetBalance(User), ReasonGiven
206                 );
207         
208         free(byName);
209         free(dstName);
210         
211         return 0;
212 }
213
214 int DispenseSet(int ActualUser, int User, int Balance, const char *ReasonGiven)
215 {
216          int    curBal = Bank_GetBalance(User);
217         char    *byName, *dstName;
218         
219         _Transfer( Bank_GetAcctByName(COKEBANK_DEBT_ACCT,1), User, Balance-curBal, ReasonGiven );
220         
221         byName = Bank_GetAcctName(ActualUser);
222         dstName = Bank_GetAcctName(User);
223         
224         Log_Info("set balance of %s to %i by %s [balance %i] - %s",
225                 dstName, Balance, byName, Bank_GetBalance(User), ReasonGiven
226                 );
227         
228         free(byName);
229         free(dstName);
230         
231         return 0;
232 }
233
234 /**
235  * \brief Donate money to the club
236  */
237 int DispenseDonate(int ActualUser, int User, int Ammount, const char *ReasonGiven)
238 {
239          int    ret;
240         char    *srcName, *byName;
241         
242         if( Ammount < 0 )       return 2;
243         
244         ret = _Transfer( User, Bank_GetAcctByName(COKEBANK_DONATE_ACCT,1), Ammount, ReasonGiven );
245         if(ret) return 2;
246         
247         byName = Bank_GetAcctName(ActualUser);
248         srcName = Bank_GetAcctName(User);
249         
250         Log_Info("donate %i from %s by %s [balance %i] - %s",
251                 Ammount, srcName, byName, Bank_GetBalance(User), ReasonGiven
252                 );
253         
254         free(byName);
255         free(srcName);
256         
257         return 0;
258 }
259
260 int DispenseUpdateItem(int User, tItem *Item, const char *NewName, int NewPrice)
261 {
262         char    *username;
263         
264         // Sanity checks
265         if( NewPrice < 0 )      return 2;
266         if( !Item )     return 2;
267         if( strlen(NewName) < 1 )       return 2;
268         
269         // Update the item
270         free(Item->Name);
271         Item->Name = strdup(NewName);
272         Item->Price = NewPrice;
273         
274         username = Bank_GetAcctName(User);
275         
276         Log_Info("item %s:%i updated to '%s' %i by %s",
277                 Item->Handler->Name, Item->ID,
278                 NewName, NewPrice, username
279                 );
280         
281         free(username);
282         
283         // Update item file
284         Items_UpdateFile();
285         
286         return 0;
287 }
288
289 // --- Internal Functions ---
290 int _GetMinBalance(int Account)
291 {
292          int    flags = Bank_GetFlags(Account);
293         
294         // Evil little piece of HACK:
295         // root's balance cannot be changed by any of the above functions
296         // - Stops dispenses as root by returning insufficent balance.
297         {
298                 char    *username = Bank_GetAcctName(Account);
299                 if( strcmp(username, "root") == 0 )
300                 {
301                         free(username);
302                         return INT_MAX;
303                 }
304                 free(username);
305         }
306         
307         // - Internal accounts have no lower bound
308         if( flags & USER_FLAG_INTERNAL )        return INT_MIN;
309         
310         // Admin to -$50
311 //      if( flags & USER_FLAG_ADMIN )   return -5000;
312         
313         // Coke to -$20
314 //      if( flags & USER_FLAG_COKE )    return -2000;
315         
316         // Anyone else, non-negative
317         return 0;
318 }
319
320 /**
321  * \brief Check if a transfer is possible
322  * \return Boolean success
323  */
324 int _CanTransfer(int Source, int Destination, int Ammount)
325 {
326         if( Bank_GetFlags(Source) & USER_FLAG_DISABLED )
327                 return 0;
328         if( Ammount > 0 )
329         {
330                 if( Bank_GetBalance(Source) - Ammount < _GetMinBalance(Source) )
331                         return 0;
332         }
333         else
334         {
335                 if( Bank_GetBalance(Destination) + Ammount < _GetMinBalance(Destination) )
336                         return 0;
337         }
338         return 1;
339 }
340
341 int _Transfer(int Source, int Destination, int Ammount, const char *Reason)
342 {
343         if( !_CanTransfer(Source, Destination, Ammount) )
344                 return 1;
345         return Bank_Transfer(Source, Destination, Ammount, Reason);
346 }
347
348 int _GetSalesAcct(tItem *Item)
349 {
350         char string[sizeof(COKEBANK_SALES_PREFIX)+strlen(Item->Handler->Name)];
351         strcpy(string, COKEBANK_SALES_PREFIX);
352         strcat(string, Item->Handler->Name);
353         return Bank_GetAcctByName(string, 1);
354 }

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