8ec7c2e9c81af6576666aba3aa3b0ae8ce963ad0
[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         if( gbNoCostMode )
72         {
73                 // Special format for zero cost dispenses
74                 Log_Info("test dispense '%s' (%s:%i) for %s by %s [no change]",
75                         Item->Name, handler->Name, Item->ID,
76                         username, actualUsername
77                         );
78         }
79         else
80         {
81                 Log_Info("dispense '%s' (%s:%i) for %s by %s [cost %i, balance %i]",
82                         Item->Name, handler->Name, Item->ID,
83                         username, actualUsername, Item->Price, Bank_GetBalance(User)
84                         );
85         }
86         
87         free( username );
88         free( actualUsername );
89         return 0;       // 0: EOK
90 }
91
92 /**
93  * \brief Refund a dispense
94  */
95 int DispenseRefund(int ActualUser, int DestUser, tItem *Item, int OverridePrice)
96 {
97          int    ret;
98          int    src_acct, price;
99         char    *username, *actualUsername;
100
101         src_acct = _GetSalesAcct(Item);
102
103         if( OverridePrice > 0 )
104                 price = OverridePrice;
105         else
106                 price = Item->Price;
107
108         ret = _Transfer( src_acct, DestUser, price, "Refund");
109         if(ret) return ret;
110
111         username = Bank_GetAcctName(DestUser);
112         actualUsername = Bank_GetAcctName(ActualUser);
113         
114         Log_Info("refund '%s' (%s:%i) to %s by %s [cost %i, balance %i]",
115                 Item->Name, Item->Handler->Name, Item->ID,
116                 username, actualUsername, price, Bank_GetBalance(DestUser)
117                 );
118
119         free(username);
120         free(actualUsername);
121
122         return 0;
123 }
124
125 /**
126  * \brief Give money from one user to another
127  */
128 int DispenseGive(int ActualUser, int SrcUser, int DestUser, int Ammount, const char *ReasonGiven)
129 {
130          int    ret;
131         char    *actualUsername;
132         char    *srcName, *dstName;
133         
134         // HACK: Naming a slot "dead" disables it (catch for snack)
135         if( strcmp(ReasonGiven, "dead") == 0 )
136                 return 1;
137         
138         if( Ammount < 0 )       return 1;       // Um... negative give? Not on my watch!
139         
140         ret = _Transfer( SrcUser, DestUser, Ammount, ReasonGiven );
141         if(ret) return 2;       // No Balance
142         
143         
144         actualUsername = Bank_GetAcctName(ActualUser);
145         srcName = Bank_GetAcctName(SrcUser);
146         dstName = Bank_GetAcctName(DestUser);
147         
148         Log_Info("give %i from %s to %s by %s [balances %i, %i] - %s",
149                 Ammount, srcName, dstName, actualUsername,
150                 Bank_GetBalance(SrcUser), Bank_GetBalance(DestUser),
151                 ReasonGiven
152                 );
153         
154         free(srcName);
155         free(dstName);
156         free(actualUsername);
157         
158         return 0;
159 }
160
161 #if 0 // Dead Code
162 /**
163  * \brief Move money from one user to another (Admin Only)
164  */
165 int DispenseTransfer(int ActualUser, int SrcUser, int DestUser, int Ammount, const char *ReasonGiven)
166 {
167          int    ret;
168         char    *actualUsername;
169         char    *srcName, *dstName;
170
171         // Make sure the user is an admin
172         if( !(Bank_GetFlags(ActualUser) & USER_FLAG_ADMIN) )
173                 return 1;
174         
175         ret = _Transfer( SrcUser, DestUser, Ammount, ReasonGiven );
176         if(ret) return 2;       // No Balance
177         
178         
179         actualUsername = Bank_GetAcctName(ActualUser);
180         srcName = Bank_GetAcctName(SrcUser);
181         dstName = Bank_GetAcctName(DestUser);
182         
183         Log_Info("move %i from %s to %s by %s [balances %i, %i] - %s",
184                 Ammount, srcName, dstName, actualUsername,
185                 Bank_GetBalance(SrcUser), Bank_GetBalance(DestUser),
186                 ReasonGiven
187                 );
188         
189         free(srcName);
190         free(dstName);
191         free(actualUsername);
192         
193         return 0;
194 }
195 #endif
196
197 /**
198  * \brief Add money to an account
199  */
200 int DispenseAdd(int ActualUser, int User, int Ammount, const char *ReasonGiven)
201 {
202          int    ret;
203         char    *dstName, *byName;
204         
205 #if DISPENSE_ADD_BELOW_MIN
206         ret = _Transfer( Bank_GetAcctByName(COKEBANK_ADDSRC_ACCT,1), User, Ammount, ReasonGiven );
207 #else
208         ret = Bank_Transfer( Bank_GetAcctByName(COKEBANK_ADDSRC_ACCT,1), User, Ammount, ReasonGiven );
209 #endif
210         if(ret) return 2;
211         
212         byName = Bank_GetAcctName(ActualUser);
213         dstName = Bank_GetAcctName(User);
214         
215         Log_Info("add %i to %s by %s [balance %i] - %s",
216                 Ammount, dstName, byName, Bank_GetBalance(User), ReasonGiven
217                 );
218         
219         free(byName);
220         free(dstName);
221         
222         return 0;
223 }
224
225 int DispenseSet(int ActualUser, int User, int Balance, const char *ReasonGiven, int *OrigBalance)
226 {
227          int    curBal = Bank_GetBalance(User);
228         char    *byName, *dstName;
229         
230         _Transfer( Bank_GetAcctByName(COKEBANK_DEBT_ACCT,1), User, Balance-curBal, ReasonGiven );
231         
232         byName = Bank_GetAcctName(ActualUser);
233         dstName = Bank_GetAcctName(User);
234         
235         Log_Info("set balance of %s to %i by %s [was %i, balance %i] - %s",
236                 dstName, Balance, byName, curBal, Bank_GetBalance(User), ReasonGiven
237                 );
238         
239         *OrigBalance = curBal;
240         free(byName);
241         free(dstName);
242         
243         return 0;
244 }
245
246 /**
247  * \brief Donate money to the club
248  */
249 int DispenseDonate(int ActualUser, int User, int Ammount, const char *ReasonGiven)
250 {
251          int    ret;
252         char    *srcName, *byName;
253         
254         if( Ammount < 0 )       return 2;
255         
256         ret = _Transfer( User, Bank_GetAcctByName(COKEBANK_DONATE_ACCT,1), Ammount, ReasonGiven );
257         if(ret) return 2;
258         
259         byName = Bank_GetAcctName(ActualUser);
260         srcName = Bank_GetAcctName(User);
261         
262         Log_Info("donate %i from %s by %s [balance %i] - %s",
263                 Ammount, srcName, byName, Bank_GetBalance(User), ReasonGiven
264                 );
265         
266         free(byName);
267         free(srcName);
268         
269         return 0;
270 }
271
272 int DispenseUpdateItem(int User, tItem *Item, const char *NewName, int NewPrice)
273 {
274         char    *username;
275         
276         // Sanity checks
277         if( NewPrice < 0 )      return 2;
278         if( !Item )     return 2;
279         if( strlen(NewName) < 1 )       return 2;
280         
281         // Update the item
282         free(Item->Name);
283         Item->Name = strdup(NewName);
284         Item->Price = NewPrice;
285         
286         username = Bank_GetAcctName(User);
287         
288         Log_Info("item %s:%i updated to '%s' %i by %s",
289                 Item->Handler->Name, Item->ID,
290                 NewName, NewPrice, username
291                 );
292         
293         free(username);
294         
295         // Update item file
296         Items_UpdateFile();
297         
298         return 0;
299 }
300
301 // --- Internal Functions ---
302 int _GetMinBalance(int Account)
303 {
304          int    flags = Bank_GetFlags(Account);
305         
306         // Evil little piece of HACK:
307         // root's balance cannot be changed by any of the above functions
308         // - Stops dispenses as root by returning insufficent balance.
309         {
310                 char    *username = Bank_GetAcctName(Account);
311                 if( strcmp(username, "root") == 0 )
312                 {
313                         free(username);
314                         return INT_MAX;
315                 }
316                 free(username);
317         }
318         
319         // - Internal accounts have no lower bound
320         if( flags & USER_FLAG_INTERNAL )        return INT_MIN;
321         
322         // Admin to -$50
323 //      if( flags & USER_FLAG_ADMIN )   return -5000;
324         
325         // Coke to -$20
326 //      if( flags & USER_FLAG_COKE )    return -2000;
327         
328         // Anyone else, non-negative
329         return 0;
330 }
331
332 /**
333  * \brief Check if a transfer is possible
334  * \return Boolean success
335  */
336 int _CanTransfer(int Source, int Destination, int Ammount)
337 {
338 //      if( Bank_GetFlags(Source) & USER_FLAG_DISABLED )
339 //              return 0;
340         if( Ammount > 0 )
341         {
342                 if( Bank_GetBalance(Source) - Ammount < _GetMinBalance(Source) )
343                         return 0;
344         }
345         else
346         {
347                 if( Bank_GetBalance(Destination) + Ammount < _GetMinBalance(Destination) )
348                         return 0;
349         }
350         return 1;
351 }
352
353 int _Transfer(int Source, int Destination, int Ammount, const char *Reason)
354 {
355         if( !_CanTransfer(Source, Destination, Ammount) )
356                 return 1;
357         return Bank_Transfer(Source, Destination, Ammount, Reason);
358 }
359
360 int _GetSalesAcct(tItem *Item)
361 {
362         char string[sizeof(COKEBANK_SALES_PREFIX)+strlen(Item->Handler->Name)];
363         strcpy(string, COKEBANK_SALES_PREFIX);
364         strcat(string, Item->Handler->Name);
365         return Bank_GetAcctByName(string, 1);
366 }

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