Server - Added bac to mark zero priced slots as erroring
[tpg/opendispense2.git] / src / server / server.c
1 /*
2  * OpenDispense 2 
3  * UCC (University [of WA] Computer Club) Electronic Accounting System
4  *
5  * server.c - Client Server Code
6  *
7  * This file is licenced under the 3-clause BSD Licence. See the file
8  * COPYING for full details.
9  */
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include "common.h"
13 #include <sys/socket.h>
14 #include <netinet/in.h>
15 #include <arpa/inet.h>
16 #include <unistd.h>
17 #include <fcntl.h>      // O_*
18 #include <string.h>
19 #include <limits.h>
20 #include <stdarg.h>
21 #include <signal.h>     // Signal handling
22 #include <ident.h>      // AUTHIDENT
23 #include <time.h>       // time(2)
24
25 #define DEBUG_TRACE_CLIENT      0
26 #define HACK_NO_REFUNDS 1
27
28 // Statistics
29 #define MAX_CONNECTION_QUEUE    5
30 #define INPUT_BUFFER_SIZE       256
31 #define CLIENT_TIMEOUT  10      // Seconds
32
33 #define HASH_TYPE       SHA1
34 #define HASH_LENGTH     20
35
36 #define MSG_STR_TOO_LONG        "499 Command too long (limit "EXPSTR(INPUT_BUFFER_SIZE)")\n"
37
38 #define IDENT_TRUSTED_NETWORK 0x825F0D00
39 #define IDENT_TRUSTED_NETMASK 0xFFFFFFC0
40
41 // === TYPES ===
42 typedef struct sClient
43 {
44          int    Socket; // Client socket ID
45          int    ID;     // Client ID
46          
47          int    bIsTrusted;     // Is the connection from a trusted host/port
48         
49         char    *Username;
50         char    Salt[9];
51         
52          int    UID;
53          int    EffectiveUID;
54          int    bIsAuthed;
55 }       tClient;
56
57 // === PROTOTYPES ===
58 void    Server_Start(void);
59 void    Server_Cleanup(void);
60 void    Server_HandleClient(int Socket, int bTrusted);
61 void    Server_ParseClientCommand(tClient *Client, char *CommandString);
62 // --- Commands ---
63 void    Server_Cmd_USER(tClient *Client, char *Args);
64 void    Server_Cmd_PASS(tClient *Client, char *Args);
65 void    Server_Cmd_AUTOAUTH(tClient *Client, char *Args);
66 void    Server_Cmd_AUTHIDENT(tClient *Client, char *Args);
67 void    Server_Cmd_SETEUSER(tClient *Client, char *Args);
68 void    Server_Cmd_ENUMITEMS(tClient *Client, char *Args);
69 void    Server_Cmd_ITEMINFO(tClient *Client, char *Args);
70 void    Server_Cmd_DISPENSE(tClient *Client, char *Args);
71 void    Server_Cmd_REFUND(tClient *Client, char *Args);
72 void    Server_Cmd_GIVE(tClient *Client, char *Args);
73 void    Server_Cmd_DONATE(tClient *Client, char *Args);
74 void    Server_Cmd_ADD(tClient *Client, char *Args);
75 void    Server_Cmd_SET(tClient *Client, char *Args);
76 void    Server_Cmd_ENUMUSERS(tClient *Client, char *Args);
77 void    Server_Cmd_USERINFO(tClient *Client, char *Args);
78 void    _SendUserInfo(tClient *Client, int UserID);
79 void    Server_Cmd_USERADD(tClient *Client, char *Args);
80 void    Server_Cmd_USERFLAGS(tClient *Client, char *Args);
81 void    Server_Cmd_UPDATEITEM(tClient *Client, char *Args);
82 // --- Helpers ---
83 void    Debug(tClient *Client, const char *Format, ...);
84  int    sendf(int Socket, const char *Format, ...);
85  int    Server_int_ParseArgs(int bUseLongArg, char *ArgStr, ...);
86  int    Server_int_ParseFlags(tClient *Client, const char *Str, int *Mask, int *Value);
87
88 // === CONSTANTS ===
89 // - Commands
90 const struct sClientCommand {
91         const char      *Name;
92         void    (*Function)(tClient *Client, char *Arguments);
93 }       gaServer_Commands[] = {
94         {"USER", Server_Cmd_USER},
95         {"PASS", Server_Cmd_PASS},
96         {"AUTOAUTH", Server_Cmd_AUTOAUTH},
97         {"AUTHIDENT", Server_Cmd_AUTHIDENT},
98         {"SETEUSER", Server_Cmd_SETEUSER},
99         {"ENUM_ITEMS", Server_Cmd_ENUMITEMS},
100         {"ITEM_INFO", Server_Cmd_ITEMINFO},
101         {"DISPENSE", Server_Cmd_DISPENSE},
102         {"REFUND", Server_Cmd_REFUND},
103         {"GIVE", Server_Cmd_GIVE},
104         {"DONATE", Server_Cmd_DONATE},
105         {"ADD", Server_Cmd_ADD},
106         {"SET", Server_Cmd_SET},
107         {"ENUM_USERS", Server_Cmd_ENUMUSERS},
108         {"USER_INFO", Server_Cmd_USERINFO},
109         {"USER_ADD", Server_Cmd_USERADD},
110         {"USER_FLAGS", Server_Cmd_USERFLAGS},
111         {"UPDATE_ITEM", Server_Cmd_UPDATEITEM}
112 };
113 #define NUM_COMMANDS    ((int)(sizeof(gaServer_Commands)/sizeof(gaServer_Commands[0])))
114
115 // === GLOBALS ===
116 // - Configuration
117  int    giServer_Port = 11020;
118  int    gbServer_RunInBackground = 0;
119 char    *gsServer_LogFile = "/var/log/dispsrv.log";
120 char    *gsServer_ErrorLog = "/var/log/dispsrv.err";
121 // - State variables
122  int    giServer_Socket;        // Server socket
123  int    giServer_NextClientID = 1;      // Debug client ID
124  
125
126 // === CODE ===
127 /**
128  * \brief Open listenting socket and serve connections
129  */
130 void Server_Start(void)
131 {
132          int    client_socket;
133         struct sockaddr_in      server_addr, client_addr;
134
135         atexit(Server_Cleanup);
136         // Ignore SIGPIPE (stops crashes when the client exits early)
137         signal(SIGPIPE, SIG_IGN);
138
139         // Create Server
140         giServer_Socket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
141         if( giServer_Socket < 0 ) {
142                 fprintf(stderr, "ERROR: Unable to create server socket\n");
143                 return ;
144         }
145         
146         // Make listen address
147         memset(&server_addr, 0, sizeof(server_addr));
148         server_addr.sin_family = AF_INET;       // Internet Socket
149         server_addr.sin_addr.s_addr = htonl(INADDR_ANY);        // Listen on all interfaces
150         server_addr.sin_port = htons(giServer_Port);    // Port
151
152         // Bind
153         if( bind(giServer_Socket, (struct sockaddr *) &server_addr, sizeof(server_addr)) < 0 ) {
154                 fprintf(stderr, "ERROR: Unable to bind to 0.0.0.0:%i\n", giServer_Port);
155                 perror("Binding");
156                 return ;
157         }
158
159         // Fork into background
160         if( gbServer_RunInBackground )
161         {
162                 int pid = fork();
163                 if( pid == -1 ) {
164                         fprintf(stderr, "ERROR: Unable to fork\n");
165                         perror("fork background");
166                         exit(-1);
167                 }
168                 if( pid != 0 ) {
169                         // Parent, quit
170                         printf("Forked child %i\n", pid);
171                         exit(0);
172                 }
173                 // In child
174                 // - Sort out stdin/stdout
175                 #if 0
176                 dup2( open("/dev/null", O_RDONLY, 0644), STDIN_FILENO );
177                 dup2( open(gsServer_LogFile, O_CREAT|O_APPEND, 0644), STDOUT_FILENO );
178                 dup2( open(gsServer_ErrorLog, O_CREAT|O_APPEND, 0644), STDERR_FILENO );
179                 #else
180                 freopen("/dev/null", "r", stdin);
181                 freopen(gsServer_LogFile, "a", stdout);
182                 freopen(gsServer_ErrorLog, "a", stderr);
183                 fprintf(stdout, "OpenDispense 2 Server Started at %lld\n", (long long)time(NULL));
184                 fprintf(stderr, "OpenDispense 2 Server Started at %lld\n", (long long)time(NULL));
185                 #endif
186         }
187
188         // Start the helper thread
189         StartPeriodicThread();
190         
191         // Listen
192         if( listen(giServer_Socket, MAX_CONNECTION_QUEUE) < 0 ) {
193                 fprintf(stderr, "ERROR: Unable to listen to socket\n");
194                 perror("Listen");
195                 return ;
196         }
197         
198         printf("Listening on 0.0.0.0:%i\n", giServer_Port);
199         
200         // write pidfile
201         {
202                 FILE *fp = fopen("/var/run/dispsrv.pid", "w");
203                 if( fp ) {
204                         fprintf(fp, "%i", getpid());
205                         fclose(fp);
206                 }
207         }
208
209         for(;;)
210         {
211                 uint    len = sizeof(client_addr);
212                  int    bTrusted = 0;
213                 
214                 // Accept a connection
215                 client_socket = accept(giServer_Socket, (struct sockaddr *) &client_addr, &len);
216                 if(client_socket < 0) {
217                         fprintf(stderr, "ERROR: Unable to accept client connection\n");
218                         return ;
219                 }
220                 
221                 // Set a timeout on the user conneciton
222                 {
223                         struct timeval tv;
224                         tv.tv_sec = CLIENT_TIMEOUT;
225                         tv.tv_usec = 0;
226                         if( setsockopt(client_socket, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) )
227                         {
228                                 perror("setsockopt");
229                                 return ;
230                         }
231                 }
232                 
233                 // Debug: Print the connection string
234                 if(giDebugLevel >= 2) {
235                         char    ipstr[INET_ADDRSTRLEN];
236                         inet_ntop(AF_INET, &client_addr.sin_addr, ipstr, INET_ADDRSTRLEN);
237                         printf("Client connection from %s:%i\n",
238                                 ipstr, ntohs(client_addr.sin_port));
239                 }
240                 
241                 // Doesn't matter what, localhost is trusted
242                 if( ntohl( client_addr.sin_addr.s_addr ) == 0x7F000001 )
243                         bTrusted = 1;
244                 
245                 // Trusted Connections
246                 if( ntohs(client_addr.sin_port) < 1024 )
247                 {
248                         // TODO: Make this runtime configurable
249                         switch( ntohl( client_addr.sin_addr.s_addr ) )
250                         {
251                         case 0x7F000001:        // 127.0.0.1    localhost
252                 //      case 0x825F0D00:        // 130.95.13.0
253                         case 0x825F0D04:        // 130.95.13.4  merlo
254                 //      case 0x825F0D05:        // 130.95.13.5  heathred (MR)
255                         case 0x825F0D07:        // 130.95.13.7  motsugo
256                         case 0x825F0D11:        // 130.95.13.17 mermaid
257                         case 0x825F0D12:        // 130.95.13.18 mussel
258                         case 0x825F0D17:        // 130.95.13.23 martello
259                         case 0x825F0D2A:        // 130.95.13.42 meersau
260                 //      case 0x825F0D42:        // 130.95.13.66 heathred (Clubroom)
261                                 bTrusted = 1;
262                                 break;
263                         default:
264                                 break;
265                         }
266                 }
267                 
268                 // TODO: Multithread this?
269                 Server_HandleClient(client_socket, bTrusted);
270                 
271                 close(client_socket);
272         }
273 }
274
275 void Server_Cleanup(void)
276 {
277         printf("\nClose(%i)\n", giServer_Socket);
278         close(giServer_Socket);
279         unlink("/var/run/dispsrv.pid");
280 }
281
282 /**
283  * \brief Reads from a client socket and parses the command strings
284  * \param Socket        Client socket number/handle
285  * \param bTrusted      Is the client trusted?
286  */
287 void Server_HandleClient(int Socket, int bTrusted)
288 {
289         char    inbuf[INPUT_BUFFER_SIZE];
290         char    *buf = inbuf;
291          int    remspace = INPUT_BUFFER_SIZE-1;
292          int    bytes = -1;
293         tClient clientInfo;
294         
295         memset(&clientInfo, 0, sizeof(clientInfo));
296         
297         // Initialise Client info
298         clientInfo.Socket = Socket;
299         clientInfo.ID = giServer_NextClientID ++;
300         clientInfo.bIsTrusted = bTrusted;
301         clientInfo.EffectiveUID = -1;
302         
303         // Read from client
304         /*
305          * Notes:
306          * - The `buf` and `remspace` variables allow a line to span several
307          *   calls to recv(), if a line is not completed in one recv() call
308          *   it is saved to the beginning of `inbuf` and `buf` is updated to
309          *   the end of it.
310          */
311         // TODO: Use select() instead (to give a timeout)
312         while( (bytes = recv(Socket, buf, remspace, 0)) > 0 )
313         {
314                 char    *eol, *start;
315                 buf[bytes] = '\0';      // Allow us to use stdlib string functions on it
316                 
317                 // Split by lines
318                 start = inbuf;
319                 while( (eol = strchr(start, '\n')) )
320                 {
321                         *eol = '\0';
322                         
323                         Server_ParseClientCommand(&clientInfo, start);
324                         
325                         start = eol + 1;
326                 }
327                 
328                 // Check if there was an incomplete line
329                 if( *start != '\0' ) {
330                          int    tailBytes = bytes - (start-buf);
331                         // Roll back in buffer
332                         memcpy(inbuf, start, tailBytes);
333                         remspace -= tailBytes;
334                         if(remspace == 0) {
335                                 send(Socket, MSG_STR_TOO_LONG, sizeof(MSG_STR_TOO_LONG), 0);
336                                 buf = inbuf;
337                                 remspace = INPUT_BUFFER_SIZE - 1;
338                         }
339                 }
340                 else {
341                         buf = inbuf;
342                         remspace = INPUT_BUFFER_SIZE - 1;
343                 }
344         }
345         
346         // Check for errors
347         if( bytes < 0 ) {
348                 fprintf(stderr, "ERROR: Unable to recieve from client on socket %i\n", Socket);
349                 return ;
350         }
351         
352         if(giDebugLevel >= 2) {
353                 printf("Client %i: Disconnected\n", clientInfo.ID);
354         }
355 }
356
357 /**
358  * \brief Parses a client command and calls the required helper function
359  * \param Client        Pointer to client state structure
360  * \param CommandString Command from client (single line of the command)
361  * \return Heap String to return to the client
362  */
363 void Server_ParseClientCommand(tClient *Client, char *CommandString)
364 {
365         char    *command, *args;
366          int    i;
367         
368         if( giDebugLevel >= 2 )
369                 Debug(Client, "Server_ParseClientCommand: (CommandString = '%s')", CommandString);
370         
371         if( Server_int_ParseArgs(1, CommandString, &command, &args, NULL) )
372         {
373                 if( command == NULL )   return ;
374                 // Is this an error? (just ignore for now)
375         }
376         
377         
378         // Find command
379         for( i = 0; i < NUM_COMMANDS; i++ )
380         {
381                 if(strcmp(command, gaServer_Commands[i].Name) == 0) {
382                         if( giDebugLevel >= 2 )
383                                 Debug(Client, "CMD %s - \"%s\"", command, args);
384                         gaServer_Commands[i].Function(Client, args);
385                         return ;
386                 }
387         }
388         
389         sendf(Client->Socket, "400 Unknown Command\n");
390 }
391
392 // ---
393 // Commands
394 // ---
395 /**
396  * \brief Set client username
397  * 
398  * Usage: USER <username>
399  */
400 void Server_Cmd_USER(tClient *Client, char *Args)
401 {
402         char    *username;
403         
404         if( Server_int_ParseArgs(0, Args, &username, NULL) )
405         {
406                 sendf(Client->Socket, "407 USER takes 1 argument\n");
407                 return ;
408         }
409         
410         // Debug!
411         if( giDebugLevel )
412                 Debug(Client, "Authenticating as '%s'", username);
413         
414         // Save username
415         if(Client->Username)
416                 free(Client->Username);
417         Client->Username = strdup(username);
418         
419         #if USE_SALT
420         // Create a salt (that changes if the username is changed)
421         // Yes, I know, I'm a little paranoid, but who isn't?
422         Client->Salt[0] = 0x21 + (rand()&0x3F);
423         Client->Salt[1] = 0x21 + (rand()&0x3F);
424         Client->Salt[2] = 0x21 + (rand()&0x3F);
425         Client->Salt[3] = 0x21 + (rand()&0x3F);
426         Client->Salt[4] = 0x21 + (rand()&0x3F);
427         Client->Salt[5] = 0x21 + (rand()&0x3F);
428         Client->Salt[6] = 0x21 + (rand()&0x3F);
429         Client->Salt[7] = 0x21 + (rand()&0x3F);
430         
431         // TODO: Also send hash type to use, (SHA1 or crypt according to [DAA])
432         sendf(Client->Socket, "100 SALT %s\n", Client->Salt);
433         #else
434         sendf(Client->Socket, "100 User Set\n");
435         #endif
436 }
437
438 /**
439  * \brief Authenticate as a user
440  * 
441  * Usage: PASS <hash>
442  */
443 void Server_Cmd_PASS(tClient *Client, char *Args)
444 {
445         char    *passhash;
446          int    flags;
447
448         if( Server_int_ParseArgs(0, Args, &passhash, NULL) )
449         {
450                 sendf(Client->Socket, "407 PASS takes 1 argument\n");
451                 return ;
452         }
453         
454         // Pass on to cokebank
455         Client->UID = Bank_GetUserAuth(Client->Salt, Client->Username, passhash);
456
457         if( Client->UID == -1 ) {
458                 sendf(Client->Socket, "401 Auth Failure\n");
459                 return ;
460         }
461
462         flags = Bank_GetFlags(Client->UID);
463         if( flags & USER_FLAG_DISABLED ) {
464                 Client->UID = -1;
465                 sendf(Client->Socket, "403 Account Disabled\n");
466                 return ;
467         }
468         if( flags & USER_FLAG_INTERNAL ) {
469                 Client->UID = -1;
470                 sendf(Client->Socket, "403 Internal account\n");
471                 return ;
472         }
473         
474         Client->bIsAuthed = 1;
475         sendf(Client->Socket, "200 Auth OK\n");
476 }
477
478 /**
479  * \brief Authenticate as a user without a password
480  * 
481  * Usage: AUTOAUTH <user>
482  */
483 void Server_Cmd_AUTOAUTH(tClient *Client, char *Args)
484 {
485         char    *username;
486          int    userflags;
487         
488         if( Server_int_ParseArgs(0, Args, &username, NULL) )
489         {
490                 sendf(Client->Socket, "407 AUTOAUTH takes 1 argument\n");
491                 return ;
492         }
493         
494         // Check if trusted
495         if( !Client->bIsTrusted ) {
496                 if(giDebugLevel)
497                         Debug(Client, "Untrusted client attempting to AUTOAUTH");
498                 sendf(Client->Socket, "401 Untrusted\n");
499                 return ;
500         }
501         
502         // Get UID
503         Client->UID = Bank_GetAcctByName( username, 0 );        
504         if( Client->UID < 0 ) {
505                 if(giDebugLevel)
506                         Debug(Client, "Unknown user '%s'", username);
507                 sendf(Client->Socket, "403 Auth Failure\n");
508                 return ;
509         }
510         
511         userflags = Bank_GetFlags(Client->UID);
512         // You can't be an internal account
513         if( userflags & USER_FLAG_INTERNAL ) {
514                 if(giDebugLevel)
515                         Debug(Client, "Autoauth as '%s', not allowed", username);
516                 Client->UID = -1;
517                 sendf(Client->Socket, "403 Account is internal\n");
518                 return ;
519         }
520
521         // Disabled accounts
522         if( userflags & USER_FLAG_DISABLED ) {
523                 Client->UID = -1;
524                 sendf(Client->Socket, "403 Account disabled\n");
525                 return ;
526         }
527
528         // Save username
529         if(Client->Username)
530                 free(Client->Username);
531         Client->Username = strdup(username);
532
533         Client->bIsAuthed = 1;
534         
535         if(giDebugLevel)
536                 Debug(Client, "Auto authenticated as '%s' (%i)", username, Client->UID);
537         
538         sendf(Client->Socket, "200 Auth OK\n");
539 }
540
541 /**
542  * \brief Authenticate as a user using the IDENT protocol
543  *
544  * Usage: AUTHIDENT
545  */
546 void Server_Cmd_AUTHIDENT(tClient *Client, char *Args)
547 {
548         char    *username;
549          int    userflags;
550         const int ident_timeout = 5;
551         socklen_t len;
552         struct sockaddr_in client_addr;
553         uint32_t  client_ip;
554
555         if( Args != NULL && strlen(Args) ) {
556                 sendf(Client->Socket, "407 AUTHIDENT takes no arguments\n");
557                 return ;
558         }
559
560         // Check if trusted (only works with INET sockets at present)
561         len = sizeof(client_addr);
562         if( getpeername(Client->Socket, (struct sockaddr*)&client_addr, &len) == -1 ) {
563                 Debug(Client, "500 getpeername() failed\n");
564                 perror("Getting AUTHIDENT peer name");
565                 sendf(Client->Socket, "500 getpeername() failed\n");
566                 return ;
567         }
568
569         client_ip = client_addr.sin_addr.s_addr;
570         if(giDebugLevel >= 2) {
571                 Debug(Client, "client_ip = %x, ntohl(client_ip) = %x", client_ip, ntohl(client_ip));
572         }
573         if( ntohl(client_ip) != 0x7F000001 && (ntohl(client_ip) & IDENT_TRUSTED_NETMASK) != IDENT_TRUSTED_NETWORK ) {
574                         if(giDebugLevel)
575                                 Debug(Client, "Untrusted client attempting to AUTHIDENT");
576                         sendf(Client->Socket, "401 Untrusted\n");
577                         return ;
578         }
579
580         // Get username via IDENT
581         username = ident_id(Client->Socket, ident_timeout);
582         if( !username ) {
583                 sendf(Client->Socket, "403 Authentication failure: IDENT auth timed out\n");
584                 return ;
585         }
586
587         // Get UID
588         Client->UID = Bank_GetAcctByName( username, 0 );
589         if( Client->UID < 0 ) {
590                 if(giDebugLevel)
591                         Debug(Client, "Unknown user '%s'", username);
592                 sendf(Client->Socket, "403 Authentication failure: unknown account\n");
593                 free(username);
594                 return ;
595         }
596
597         userflags = Bank_GetFlags(Client->UID);
598         // You can't be an internal account
599         if( userflags & USER_FLAG_INTERNAL ) {
600                 if(giDebugLevel)
601                         Debug(Client, "IDENT auth as '%s', not allowed", username);
602                 Client->UID = -1;
603                 sendf(Client->Socket, "403 Authentication failure: that account is internal\n");
604                 free(username);
605                 return ;
606         }
607
608         // Disabled accounts
609         if( userflags & USER_FLAG_DISABLED ) {
610                 Client->UID = -1;
611                 sendf(Client->Socket, "403 Authentication failure: account disabled\n");
612                 free(username);
613                 return ;
614         }
615
616         // Save username
617         if(Client->Username)
618                 free(Client->Username);
619         Client->Username = strdup(username);
620
621         Client->bIsAuthed = 1;
622
623         if(giDebugLevel)
624                 Debug(Client, "IDENT authenticated as '%s' (%i)", username, Client->UID);
625         free(username);
626
627         sendf(Client->Socket, "200 Auth OK\n");
628 }
629
630 /**
631  * \brief Set effective user
632  */
633 void Server_Cmd_SETEUSER(tClient *Client, char *Args)
634 {
635         char    *username;
636          int    eUserFlags, userFlags;
637         
638         if( Server_int_ParseArgs(0, Args, &username, NULL) )
639         {
640                 sendf(Client->Socket, "407 SETEUSER takes 1 argument\n");
641                 return ;
642         }
643         
644         if( !strlen(Args) ) {
645                 sendf(Client->Socket, "407 SETEUSER expects an argument\n");
646                 return ;
647         }
648         
649         // Check authentication
650         if( !Client->bIsAuthed ) {
651                 sendf(Client->Socket, "401 Not Authenticated\n");
652                 return ;
653         }
654
655         // Check user permissions
656         userFlags = Bank_GetFlags(Client->UID);
657         if( !(userFlags & (USER_FLAG_COKE|USER_FLAG_ADMIN)) ) {
658                 sendf(Client->Socket, "403 Not in coke\n");
659                 return ;
660         }
661         
662         // Set id
663         Client->EffectiveUID = Bank_GetAcctByName(username, 0);
664         if( Client->EffectiveUID == -1 ) {
665                 sendf(Client->Socket, "404 User not found\n");
666                 return ;
667         }
668         
669         // You can't be an internal account
670         if( !(userFlags & USER_FLAG_ADMIN) )
671         {
672                 eUserFlags = Bank_GetFlags(Client->EffectiveUID);
673                 if( eUserFlags & USER_FLAG_INTERNAL ) {
674                         Client->EffectiveUID = -1;
675                         sendf(Client->Socket, "404 User not found\n");
676                         return ;
677                 }
678                 // Disabled only avaliable to admins
679                 if( eUserFlags & USER_FLAG_DISABLED ) {
680                         Client->EffectiveUID = -1;
681                         sendf(Client->Socket, "403 Account disabled\n");
682                         return ;
683                 }
684         }
685
686         // Disabled accounts
687         if( userFlags & USER_FLAG_DISABLED ) {
688                 Client->UID = -1;
689                 sendf(Client->Socket, "403 Account disabled\n");
690                 return ;
691         }
692         
693         sendf(Client->Socket, "200 User set\n");
694 }
695
696 /**
697  * \brief Send an item status to the client
698  * \param Client        Who to?
699  * \param Item  Item to send
700  */
701 void Server_int_SendItem(tClient *Client, tItem *Item)
702 {
703         char    *status = "avail";
704         
705         if( Item->Handler->CanDispense )
706         {
707                 switch(Item->Handler->CanDispense(Client->UID, Item->ID))
708                 {
709                 case  0:        status = "avail";       break;
710                 case  1:        status = "sold";        break;
711                 default:
712                 case -1:        status = "error";       break;
713                 }
714         }
715         
716         if( Item->Price == 0 )
717                 status = "error";
718         // KNOWN HACK: Naming a slot 'dead' disables it
719         if( strcmp(Item->Name, "dead") == 0 )
720                 status = "sold";        // Another status?
721         
722         sendf(Client->Socket,
723                 "202 Item %s:%i %s %i %s\n",
724                 Item->Handler->Name, Item->ID, status, Item->Price, Item->Name
725                 );
726 }
727
728 /**
729  * \brief Enumerate the items that the server knows about
730  */
731 void Server_Cmd_ENUMITEMS(tClient *Client, char *Args)
732 {
733          int    i, count;
734
735         if( Args != NULL && strlen(Args) ) {
736                 sendf(Client->Socket, "407 ENUM_ITEMS takes no arguments\n");
737                 return ;
738         }
739         
740         // Count shown items
741         count = 0;
742         for( i = 0; i < giNumItems; i ++ ) {
743                 if( gaItems[i].bHidden )        continue;
744                 count ++;
745         }
746
747         sendf(Client->Socket, "201 Items %i\n", count);
748
749         for( i = 0; i < giNumItems; i ++ ) {
750                 if( gaItems[i].bHidden )        continue;
751                 Server_int_SendItem( Client, &gaItems[i] );
752         }
753
754         sendf(Client->Socket, "200 List end\n");
755 }
756
757 tItem *_GetItemFromString(char *String)
758 {
759         tHandler        *handler;
760         char    *type = String;
761         char    *colon = strchr(String, ':');
762          int    num, i;
763         
764         if( !colon ) {
765                 return NULL;
766         }
767
768         num = atoi(colon+1);
769         *colon = '\0';
770
771         // Find handler
772         handler = NULL;
773         for( i = 0; i < giNumHandlers; i ++ )
774         {
775                 if( strcmp(gaHandlers[i]->Name, type) == 0) {
776                         handler = gaHandlers[i];
777                         break;
778                 }
779         }
780         if( !handler ) {
781                 return NULL;
782         }
783
784         // Find item
785         for( i = 0; i < giNumItems; i ++ )
786         {
787                 if( gaItems[i].Handler != handler )     continue;
788                 if( gaItems[i].ID != num )      continue;
789                 return &gaItems[i];
790         }
791         return NULL;
792 }
793
794 /**
795  * \brief Fetch information on a specific item
796  */
797 void Server_Cmd_ITEMINFO(tClient *Client, char *Args)
798 {
799         tItem   *item;
800         char    *itemname;
801         
802         if( Server_int_ParseArgs(0, Args, &itemname, NULL) ) {
803                 sendf(Client->Socket, "407 ITEMINFO takes 1 argument\n");
804                 return ;
805         }
806         item = _GetItemFromString(Args);
807         
808         if( !item ) {
809                 sendf(Client->Socket, "406 Bad Item ID\n");
810                 return ;
811         }
812         
813         Server_int_SendItem( Client, item );
814 }
815
816 void Server_Cmd_DISPENSE(tClient *Client, char *Args)
817 {
818         tItem   *item;
819          int    ret;
820          int    uid;
821         char    *itemname;
822         
823         if( Server_int_ParseArgs(0, Args, &itemname, NULL) ) {
824                 sendf(Client->Socket, "407 DISPENSE takes only 1 argument\n");
825                 return ;
826         }
827          
828         if( !Client->bIsAuthed ) {
829                 sendf(Client->Socket, "401 Not Authenticated\n");
830                 return ;
831         }
832
833         item = _GetItemFromString(itemname);
834         if( !item ) {
835                 sendf(Client->Socket, "406 Bad Item ID\n");
836                 return ;
837         }
838         
839         if( Client->EffectiveUID != -1 ) {
840                 uid = Client->EffectiveUID;
841         }
842         else {
843                 uid = Client->UID;
844         }
845
846         switch( ret = DispenseItem( Client->UID, uid, item ) )
847         {
848         case 0: sendf(Client->Socket, "200 Dispense OK\n");     return ;
849         case 1: sendf(Client->Socket, "501 Unable to dispense\n");      return ;
850         case 2: sendf(Client->Socket, "402 Poor You\n");        return ;
851         default:
852                 sendf(Client->Socket, "500 Dispense Error (%i)\n", ret);
853                 return ;
854         }
855 }
856
857 void Server_Cmd_REFUND(tClient *Client, char *Args)
858 {
859         tItem   *item;
860          int    uid, price_override = 0;
861         char    *username, *itemname, *price_str;
862
863         if( Server_int_ParseArgs(0, Args, &username, &itemname, &price_str, NULL) ) {
864                 if( !itemname || price_str ) {
865                         sendf(Client->Socket, "407 REFUND takes 2 or 3 arguments\n");
866                         return ;
867                 }
868         }
869
870         if( !Client->bIsAuthed ) {
871                 sendf(Client->Socket, "401 Not Authenticated\n");
872                 return ;
873         }
874
875         // Check user permissions
876         if( !(Bank_GetFlags(Client->UID) & (USER_FLAG_COKE|USER_FLAG_ADMIN))  ) {
877                 sendf(Client->Socket, "403 Not in coke\n");
878                 return ;
879         }
880
881         uid = Bank_GetAcctByName(username, 0);
882         if( uid == -1 ) {
883                 sendf(Client->Socket, "404 Unknown user\n");
884                 return ;
885         }
886         
887         item = _GetItemFromString(itemname);
888         if( !item ) {
889                 sendf(Client->Socket, "406 Bad Item ID\n");
890                 return ;
891         }
892
893         if( price_str )
894                 price_override = atoi(price_str);
895
896         switch( DispenseRefund( Client->UID, uid, item, price_override ) )
897         {
898         case 0: sendf(Client->Socket, "200 Item Refunded\n");   return ;
899         default:
900                 sendf(Client->Socket, "500 Dispense Error\n");
901                 return;
902         }
903 }
904
905 void Server_Cmd_GIVE(tClient *Client, char *Args)
906 {
907         char    *recipient, *ammount, *reason;
908          int    uid, iAmmount;
909          int    thisUid;
910         
911         // Parse arguments
912         if( Server_int_ParseArgs(1, Args, &recipient, &ammount, &reason, NULL) ) {
913                 sendf(Client->Socket, "407 GIVE takes only 3 arguments\n");
914                 return ;
915         }
916         
917         // Check for authed
918         if( !Client->bIsAuthed ) {
919                 sendf(Client->Socket, "401 Not Authenticated\n");
920                 return ;
921         }
922
923         // Get recipient
924         uid = Bank_GetAcctByName(recipient, 0);
925         if( uid == -1 ) {
926                 sendf(Client->Socket, "404 Invalid target user\n");
927                 return ;
928         }
929         
930         // You can't alter an internal account
931 //      if( Bank_GetFlags(uid) & USER_FLAG_INTERNAL ) {
932 //              sendf(Client->Socket, "404 Invalid target user\n");
933 //              return ;
934 //      }
935
936         // Parse ammount
937         iAmmount = atoi(ammount);
938         if( iAmmount <= 0 ) {
939                 sendf(Client->Socket, "407 Invalid Argument, ammount must be > zero\n");
940                 return ;
941         }
942         
943         if( Client->EffectiveUID != -1 ) {
944                 thisUid = Client->EffectiveUID;
945         }
946         else {
947                 thisUid = Client->UID;
948         }
949
950         // Do give
951         switch( DispenseGive(Client->UID, thisUid, uid, iAmmount, reason) )
952         {
953         case 0:
954                 sendf(Client->Socket, "200 Give OK\n");
955                 return ;
956         case 2:
957                 sendf(Client->Socket, "402 Poor You\n");
958                 return ;
959         default:
960                 sendf(Client->Socket, "500 Unknown error\n");
961                 return ;
962         }
963 }
964
965 void Server_Cmd_DONATE(tClient *Client, char *Args)
966 {
967         char    *ammount, *reason;
968          int    iAmmount;
969          int    thisUid;
970         
971         // Parse arguments
972         if( Server_int_ParseArgs(1, Args, &ammount, &reason, NULL) ) {
973                 sendf(Client->Socket, "407 DONATE takes 2 arguments\n");
974                 return ;
975         }
976         
977         if( !Client->bIsAuthed ) {
978                 sendf(Client->Socket, "401 Not Authenticated\n");
979                 return ;
980         }
981
982         // Parse ammount
983         iAmmount = atoi(ammount);
984         if( iAmmount <= 0 ) {
985                 sendf(Client->Socket, "407 Invalid Argument, ammount must be > zero\n");
986                 return ;
987         }
988         
989         // Handle effective users
990         if( Client->EffectiveUID != -1 ) {
991                 thisUid = Client->EffectiveUID;
992         }
993         else {
994                 thisUid = Client->UID;
995         }
996
997         // Do give
998         switch( DispenseDonate(Client->UID, thisUid, iAmmount, reason) )
999         {
1000         case 0:
1001                 sendf(Client->Socket, "200 Give OK\n");
1002                 return ;
1003         case 2:
1004                 sendf(Client->Socket, "402 Poor You\n");
1005                 return ;
1006         default:
1007                 sendf(Client->Socket, "500 Unknown error\n");
1008                 return ;
1009         }
1010 }
1011
1012 void Server_Cmd_ADD(tClient *Client, char *Args)
1013 {
1014         char    *user, *ammount, *reason;
1015          int    uid, iAmmount;
1016         
1017         // Parse arguments
1018         if( Server_int_ParseArgs(1, Args, &user, &ammount, &reason, NULL) ) {
1019                 sendf(Client->Socket, "407 ADD takes 3 arguments\n");
1020                 return ;
1021         }
1022         
1023         if( !Client->bIsAuthed ) {
1024                 sendf(Client->Socket, "401 Not Authenticated\n");
1025                 return ;
1026         }
1027
1028         // Check user permissions
1029         if( !(Bank_GetFlags(Client->UID) & (USER_FLAG_COKE|USER_FLAG_ADMIN))  ) {
1030                 sendf(Client->Socket, "403 Not in coke\n");
1031                 return ;
1032         }
1033
1034         #if !ROOT_CAN_ADD
1035         if( strcmp( Client->Username, "root" ) == 0 ) {
1036                 // Allow adding for new users
1037                 if( strcmp(reason, "treasurer: new user") != 0 ) {
1038                         sendf(Client->Socket, "403 Root may not add\n");
1039                         return ;
1040                 }
1041         }
1042         #endif
1043
1044         #if HACK_NO_REFUNDS
1045         if( strstr(reason, "refund") != NULL || strstr(reason, "misdispense") != NULL )
1046         {
1047                 sendf(Client->Socket, "499 Don't use `dispense acct` for refunds, use `dispense refund` (and `dispense -G` to get item IDs)\n");
1048                 return ;
1049         }
1050         #endif
1051
1052         // Get recipient
1053         uid = Bank_GetAcctByName(user, 0);
1054         if( uid == -1 ) {
1055                 sendf(Client->Socket, "404 Invalid user\n");
1056                 return ;
1057         }
1058         
1059         // You can't alter an internal account
1060         if( !(Bank_GetFlags(Client->UID) & USER_FLAG_ADMIN) )
1061         {
1062                 if( Bank_GetFlags(uid) & USER_FLAG_INTERNAL ) {
1063                         sendf(Client->Socket, "404 Invalid user\n");
1064                         return ;
1065                 }
1066                 // TODO: Maybe disallow changes to disabled?
1067         }
1068
1069         // Parse ammount
1070         iAmmount = atoi(ammount);
1071         if( iAmmount == 0 && ammount[0] != '0' ) {
1072                 sendf(Client->Socket, "407 Invalid Argument\n");
1073                 return ;
1074         }
1075
1076         // Do give
1077         switch( DispenseAdd(Client->UID, uid, iAmmount, reason) )
1078         {
1079         case 0:
1080                 sendf(Client->Socket, "200 Add OK\n");
1081                 return ;
1082         case 2:
1083                 sendf(Client->Socket, "402 Poor Guy\n");
1084                 return ;
1085         default:
1086                 sendf(Client->Socket, "500 Unknown error\n");
1087                 return ;
1088         }
1089 }
1090
1091 void Server_Cmd_SET(tClient *Client, char *Args)
1092 {
1093         char    *user, *ammount, *reason;
1094          int    uid, iAmmount;
1095         
1096         // Parse arguments
1097         if( Server_int_ParseArgs(1, Args, &user, &ammount, &reason, NULL) ) {
1098                 sendf(Client->Socket, "407 SET takes 3 arguments\n");
1099                 return ;
1100         }
1101         
1102         if( !Client->bIsAuthed ) {
1103                 sendf(Client->Socket, "401 Not Authenticated\n");
1104                 return ;
1105         }
1106
1107         // Check user permissions
1108         if( !(Bank_GetFlags(Client->UID) & USER_FLAG_ADMIN)  ) {
1109                 sendf(Client->Socket, "403 Not an admin\n");
1110                 return ;
1111         }
1112
1113         // Get recipient
1114         uid = Bank_GetAcctByName(user, 0);
1115         if( uid == -1 ) {
1116                 sendf(Client->Socket, "404 Invalid user\n");
1117                 return ;
1118         }
1119
1120         // Parse ammount
1121         iAmmount = atoi(ammount);
1122         if( iAmmount == 0 && ammount[0] != '0' ) {
1123                 sendf(Client->Socket, "407 Invalid Argument\n");
1124                 return ;
1125         }
1126
1127         // Do give
1128         switch( DispenseSet(Client->UID, uid, iAmmount, reason) )
1129         {
1130         case 0:
1131                 sendf(Client->Socket, "200 Add OK\n");
1132                 return ;
1133         case 2:
1134                 sendf(Client->Socket, "402 Poor Guy\n");
1135                 return ;
1136         default:
1137                 sendf(Client->Socket, "500 Unknown error\n");
1138                 return ;
1139         }
1140 }
1141
1142 void Server_Cmd_ENUMUSERS(tClient *Client, char *Args)
1143 {
1144          int    i, numRet = 0;
1145         tAcctIterator   *it;
1146          int    maxBal = INT_MAX, minBal = INT_MIN;
1147          int    flagMask = 0, flagVal = 0;
1148          int    sort = BANK_ITFLAG_SORT_NAME;
1149         time_t  lastSeenAfter=0, lastSeenBefore=0;
1150         
1151          int    flags;  // Iterator flags
1152          int    balValue;       // Balance value for iterator
1153         time_t  timeValue;      // Time value for iterator
1154         
1155         // Parse arguments
1156         if( Args && strlen(Args) )
1157         {
1158                 char    *space = Args, *type, *val;
1159                 do
1160                 {
1161                         type = space;
1162                         while(*type == ' ')     type ++;
1163                         // Get next space
1164                         space = strchr(space, ' ');
1165                         if(space)       *space = '\0';
1166                         
1167                         // Get type
1168                         val = strchr(type, ':');
1169                         if( val ) {
1170                                 *val = '\0';
1171                                 val ++;
1172                                 
1173                                 // Types
1174                                 // - Minium Balance
1175                                 if( strcmp(type, "min_balance") == 0 ) {
1176                                         minBal = atoi(val);
1177                                 }
1178                                 // - Maximum Balance
1179                                 else if( strcmp(type, "max_balance") == 0 ) {
1180                                         maxBal = atoi(val);
1181                                 }
1182                                 // - Flags
1183                                 else if( strcmp(type, "flags") == 0 ) {
1184                                         if( Server_int_ParseFlags(Client, val, &flagMask, &flagVal) )
1185                                                 return ;
1186                                 }
1187                                 // - Last seen before timestamp
1188                                 else if( strcmp(type, "last_seen_before") == 0 ) {
1189                                         lastSeenAfter = atoll(val);
1190                                 }
1191                                 // - Last seen after timestamp
1192                                 else if( strcmp(type, "last_seen_after") == 0 ) {
1193                                         lastSeenAfter = atoll(val);
1194                                 }
1195                                 // - Sorting 
1196                                 else if( strcmp(type, "sort") == 0 ) {
1197                                         char    *dash = strchr(val, '-');
1198                                         if( dash ) {
1199                                                 *dash = '\0';
1200                                                 dash ++;
1201                                         }
1202                                         if( strcmp(val, "name") == 0 ) {
1203                                                 sort = BANK_ITFLAG_SORT_NAME;
1204                                         }
1205                                         else if( strcmp(val, "balance") == 0 ) {
1206                                                 sort = BANK_ITFLAG_SORT_BAL;
1207                                         }
1208                                         else if( strcmp(val, "lastseen") == 0 ) {
1209                                                 sort = BANK_ITFLAG_SORT_LASTSEEN;
1210                                         }
1211                                         else {
1212                                                 sendf(Client->Socket, "407 Unknown sort field ('%s')\n", val);
1213                                                 return ;
1214                                         }
1215                                         // Handle sort direction
1216                                         if( dash ) {
1217                                                 if( strcmp(dash, "desc") == 0 ) {
1218                                                         sort |= BANK_ITFLAG_REVSORT;
1219                                                 }
1220                                                 else {
1221                                                         sendf(Client->Socket, "407 Unknown sort direction '%s'\n", dash);
1222                                                         return ;
1223                                                 }
1224                                                 dash[-1] = '-';
1225                                         }
1226                                 }
1227                                 else {
1228                                         sendf(Client->Socket, "407 Unknown argument to ENUM_USERS '%s:%s'\n", type, val);
1229                                         return ;
1230                                 }
1231                                 
1232                                 val[-1] = ':';
1233                         }
1234                         else {
1235                                 sendf(Client->Socket, "407 Unknown argument to ENUM_USERS '%s'\n", type);
1236                                 return ;
1237                         }
1238                         
1239                         // Eat whitespace
1240                         if( space ) {
1241                                 *space = ' ';   // Repair (to be nice)
1242                                 space ++;
1243                                 while(*space == ' ')    space ++;
1244                         }
1245                 }       while(space);
1246         }
1247         
1248         // Create iterator
1249         if( maxBal != INT_MAX ) {
1250                 flags = sort|BANK_ITFLAG_MAXBALANCE;
1251                 balValue = maxBal;
1252         }
1253         else if( minBal != INT_MIN ) {
1254                 flags = sort|BANK_ITFLAG_MINBALANCE;
1255                 balValue = minBal;
1256         }
1257         else {
1258                 flags = sort;
1259                 balValue = 0;
1260         }
1261         if( lastSeenBefore ) {
1262                 timeValue = lastSeenBefore;
1263                 flags |= BANK_ITFLAG_SEENBEFORE;
1264         }
1265         else if( lastSeenAfter ) {
1266                 timeValue = lastSeenAfter;
1267                 flags |= BANK_ITFLAG_SEENAFTER;
1268         }
1269         else {
1270                 timeValue = 0;
1271         }
1272         it = Bank_Iterator(flagMask, flagVal, flags, balValue, timeValue);
1273         
1274         // Get return number
1275         while( (i = Bank_IteratorNext(it)) != -1 )
1276         {
1277                 int bal = Bank_GetBalance(i);
1278                 
1279                 if( bal == INT_MIN )    continue;
1280                 
1281                 if( bal < minBal )      continue;
1282                 if( bal > maxBal )      continue;
1283                 
1284                 numRet ++;
1285         }
1286         
1287         Bank_DelIterator(it);
1288         
1289         // Send count
1290         sendf(Client->Socket, "201 Users %i\n", numRet);
1291         
1292         
1293         // Create iterator
1294         it = Bank_Iterator(flagMask, flagVal, flags, balValue, timeValue);
1295         
1296         while( (i = Bank_IteratorNext(it)) != -1 )
1297         {
1298                 int bal = Bank_GetBalance(i);
1299                 
1300                 if( bal == INT_MIN )    continue;
1301                 
1302                 if( bal < minBal )      continue;
1303                 if( bal > maxBal )      continue;
1304                 
1305                 _SendUserInfo(Client, i);
1306         }
1307         
1308         Bank_DelIterator(it);
1309         
1310         sendf(Client->Socket, "200 List End\n");
1311 }
1312
1313 void Server_Cmd_USERINFO(tClient *Client, char *Args)
1314 {
1315          int    uid;
1316         char    *user;
1317         
1318         // Parse arguments
1319         if( Server_int_ParseArgs(0, Args, &user, NULL) ) {
1320                 sendf(Client->Socket, "407 USER_INFO takes 1 argument\n");
1321                 return ;
1322         }
1323         
1324         if( giDebugLevel )      Debug(Client, "User Info '%s'", user);
1325         
1326         // Get recipient
1327         uid = Bank_GetAcctByName(user, 0);
1328         
1329         if( giDebugLevel >= 2 ) Debug(Client, "uid = %i", uid);
1330         if( uid == -1 ) {
1331                 sendf(Client->Socket, "404 Invalid user\n");
1332                 return ;
1333         }
1334         
1335         _SendUserInfo(Client, uid);
1336 }
1337
1338 void _SendUserInfo(tClient *Client, int UserID)
1339 {
1340         char    *type, *disabled="", *door="";
1341          int    flags = Bank_GetFlags(UserID);
1342         
1343         if( flags & USER_FLAG_INTERNAL ) {
1344                 type = "internal";
1345         }
1346         else if( flags & USER_FLAG_COKE ) {
1347                 if( flags & USER_FLAG_ADMIN )
1348                         type = "coke,admin";
1349                 else
1350                         type = "coke";
1351         }
1352         else if( flags & USER_FLAG_ADMIN ) {
1353                 type = "admin";
1354         }
1355         else {
1356                 type = "user";
1357         }
1358         
1359         if( flags & USER_FLAG_DISABLED )
1360                 disabled = ",disabled";
1361         if( flags & USER_FLAG_DOORGROUP )
1362                 door = ",door";
1363         
1364         // TODO: User flags/type
1365         sendf(
1366                 Client->Socket, "202 User %s %i %s%s%s\n",
1367                 Bank_GetAcctName(UserID), Bank_GetBalance(UserID),
1368                 type, disabled, door
1369                 );
1370 }
1371
1372 void Server_Cmd_USERADD(tClient *Client, char *Args)
1373 {
1374         char    *username;
1375         
1376         // Parse arguments
1377         if( Server_int_ParseArgs(0, Args, &username, NULL) ) {
1378                 sendf(Client->Socket, "407 USER_ADD takes 1 argument\n");
1379                 return ;
1380         }
1381         
1382         // Check authentication
1383         if( !Client->bIsAuthed ) {
1384                 sendf(Client->Socket, "401 Not Authenticated\n");
1385                 return ;
1386         }
1387         
1388         // Check permissions
1389         if( !(Bank_GetFlags(Client->UID) & USER_FLAG_ADMIN) ) {
1390                 sendf(Client->Socket, "403 Not a coke admin\n");
1391                 return ;
1392         }
1393         
1394         // Try to create user
1395         if( Bank_CreateAcct(username) == -1 ) {
1396                 sendf(Client->Socket, "404 User exists\n");
1397                 return ;
1398         }
1399         
1400         {
1401                 char    *thisName = Bank_GetAcctName(Client->UID);
1402                 Log_Info("Account '%s' created by '%s'", username, thisName);
1403                 free(thisName);
1404         }
1405         
1406         sendf(Client->Socket, "200 User Added\n");
1407 }
1408
1409 void Server_Cmd_USERFLAGS(tClient *Client, char *Args)
1410 {
1411         char    *username, *flags, *reason=NULL;
1412          int    mask=0, value=0;
1413          int    uid;
1414         
1415         // Parse arguments
1416         if( Server_int_ParseArgs(1, Args, &username, &flags, &reason, NULL) ) {
1417                 if( !flags ) {
1418                         sendf(Client->Socket, "407 USER_FLAGS takes at least 2 arguments\n");
1419                         return ;
1420                 }
1421                 reason = "";
1422         }
1423         
1424         // Check authentication
1425         if( !Client->bIsAuthed ) {
1426                 sendf(Client->Socket, "401 Not Authenticated\n");
1427                 return ;
1428         }
1429         
1430         // Check permissions
1431         if( !(Bank_GetFlags(Client->UID) & USER_FLAG_ADMIN) ) {
1432                 sendf(Client->Socket, "403 Not a coke admin\n");
1433                 return ;
1434         }
1435         
1436         // Get UID
1437         uid = Bank_GetAcctByName(username, 0);
1438         if( uid == -1 ) {
1439                 sendf(Client->Socket, "404 User '%s' not found\n", username);
1440                 return ;
1441         }
1442         
1443         // Parse flags
1444         if( Server_int_ParseFlags(Client, flags, &mask, &value) )
1445                 return ;
1446         
1447         if( giDebugLevel )
1448                 Debug(Client, "Set %i(%s) flags to %x (masked %x)\n",
1449                         uid, username, mask, value);
1450         
1451         // Apply flags
1452         Bank_SetFlags(uid, mask, value);
1453
1454         // Log the change
1455         Log_Info("Updated '%s' with flag set '%s' by '%s' - Reason: %s",
1456                 username, flags, Client->Username, reason);
1457         
1458         // Return OK
1459         sendf(Client->Socket, "200 User Updated\n");
1460 }
1461
1462 void Server_Cmd_UPDATEITEM(tClient *Client, char *Args)
1463 {
1464         char    *itemname, *price_str, *description;
1465          int    price;
1466         tItem   *item;
1467         
1468         if( Server_int_ParseArgs(1, Args, &itemname, &price_str, &description, NULL) ) {
1469                 sendf(Client->Socket, "407 UPDATE_ITEM takes 3 arguments\n");
1470                 return ;
1471         }
1472         
1473         if( !Client->bIsAuthed ) {
1474                 sendf(Client->Socket, "401 Not Authenticated\n");
1475                 return ;
1476         }
1477
1478         // Check user permissions
1479         if( !(Bank_GetFlags(Client->UID) & (USER_FLAG_COKE|USER_FLAG_ADMIN))  ) {
1480                 sendf(Client->Socket, "403 Not in coke\n");
1481                 return ;
1482         }
1483         
1484         item = _GetItemFromString(itemname);
1485         if( !item ) {
1486                 // TODO: Create item?
1487                 sendf(Client->Socket, "406 Bad Item ID\n");
1488                 return ;
1489         }
1490         
1491         price = atoi(price_str);
1492         if( price <= 0 && price_str[0] != '0' ) {
1493                 sendf(Client->Socket, "407 Invalid price set\n");
1494         }
1495         
1496         switch( DispenseUpdateItem( Client->UID, item, description, price ) )
1497         {
1498         case 0:
1499                 // Return OK
1500                 sendf(Client->Socket, "200 Item updated\n");
1501                 break;
1502         default:
1503                 break;
1504         }
1505 }
1506
1507 // --- INTERNAL HELPERS ---
1508 void Debug(tClient *Client, const char *Format, ...)
1509 {
1510         va_list args;
1511         //printf("%010i [%i] ", (int)time(NULL), Client->ID);
1512         printf("[%i] ", Client->ID);
1513         va_start(args, Format);
1514         vprintf(Format, args);
1515         va_end(args);
1516         printf("\n");
1517 }
1518
1519 int sendf(int Socket, const char *Format, ...)
1520 {
1521         va_list args;
1522          int    len;
1523         
1524         va_start(args, Format);
1525         len = vsnprintf(NULL, 0, Format, args);
1526         va_end(args);
1527         
1528         {
1529                 char    buf[len+1];
1530                 va_start(args, Format);
1531                 vsnprintf(buf, len+1, Format, args);
1532                 va_end(args);
1533                 
1534                 #if DEBUG_TRACE_CLIENT
1535                 printf("sendf: %s", buf);
1536                 #endif
1537                 
1538                 return send(Socket, buf, len, 0);
1539         }
1540 }
1541
1542 // Takes a series of char *'s in
1543 /**
1544  * \brief Parse space-separated entries into 
1545  */
1546 int Server_int_ParseArgs(int bUseLongLast, char *ArgStr, ...)
1547 {
1548         va_list args;
1549         char    savedChar;
1550         char    **dest;
1551         va_start(args, ArgStr);
1552
1553         // Check for null
1554         if( !ArgStr )
1555         {
1556                 while( (dest = va_arg(args, char **)) )
1557                         *dest = NULL;
1558                 va_end(args);
1559                 return 1;
1560         }
1561
1562         savedChar = *ArgStr;
1563         
1564         while( (dest = va_arg(args, char **)) )
1565         {
1566                 // Trim leading spaces
1567                 while( *ArgStr == ' ' || *ArgStr == '\t' )
1568                         ArgStr ++;
1569                 
1570                 // ... oops, not enough arguments
1571                 if( *ArgStr == '\0' )
1572                 {
1573                         // NULL unset arguments
1574                         do {
1575                                 *dest = NULL;
1576                         }       while( (dest = va_arg(args, char **)) );
1577                 va_end(args);
1578                         return -1;
1579                 }
1580                 
1581                 if( *ArgStr == '"' )
1582                 {
1583                         ArgStr ++;
1584                         *dest = ArgStr;
1585                         // Read until quote
1586                         while( *ArgStr && *ArgStr != '"' )
1587                                 ArgStr ++;
1588                 }
1589                 else
1590                 {
1591                         // Set destination
1592                         *dest = ArgStr;
1593                         // Read until a space
1594                         while( *ArgStr && *ArgStr != ' ' && *ArgStr != '\t' )
1595                                 ArgStr ++;
1596                 }
1597                 savedChar = *ArgStr;    // savedChar is used to un-mangle the last string
1598                 *ArgStr = '\0';
1599                 ArgStr ++;
1600         }
1601         va_end(args);
1602         
1603         // Oops, extra arguments, and greedy not set
1604         if( (savedChar == ' ' || savedChar == '\t') && !bUseLongLast ) {
1605                 return -1;
1606         }
1607         
1608         // Un-mangle last
1609         if(bUseLongLast) {
1610                 ArgStr --;
1611                 *ArgStr = savedChar;
1612         }
1613         
1614         return 0;       // Success!
1615 }
1616
1617 int Server_int_ParseFlags(tClient *Client, const char *Str, int *Mask, int *Value)
1618 {
1619         struct {
1620                 const char      *Name;
1621                  int    Mask;
1622                  int    Value;
1623         }       cFLAGS[] = {
1624                  {"disabled", USER_FLAG_DISABLED, USER_FLAG_DISABLED}
1625                 ,{"door", USER_FLAG_DOORGROUP, USER_FLAG_DOORGROUP}
1626                 ,{"coke", USER_FLAG_COKE, USER_FLAG_COKE}
1627                 ,{"admin", USER_FLAG_ADMIN, USER_FLAG_ADMIN}
1628                 ,{"internal", USER_FLAG_INTERNAL, USER_FLAG_INTERNAL}
1629         };
1630         const int       ciNumFlags = sizeof(cFLAGS)/sizeof(cFLAGS[0]);
1631         
1632         char    *space;
1633         
1634         *Mask = 0;
1635         *Value = 0;
1636         
1637         do {
1638                  int    bRemove = 0;
1639                  int    i;
1640                  int    len;
1641                 
1642                 while( *Str == ' ' )    Str ++; // Eat whitespace
1643                 space = strchr(Str, ',');       // Find the end of the flag
1644                 if(space)
1645                         len = space - Str;
1646                 else
1647                         len = strlen(Str);
1648                 
1649                 // Check for inversion/removal
1650                 if( *Str == '!' || *Str == '-' ) {
1651                         bRemove = 1;
1652                         Str ++;
1653                 }
1654                 else if( *Str == '+' ) {
1655                         Str ++;
1656                 }
1657                 
1658                 // Check flag values
1659                 for( i = 0; i < ciNumFlags; i ++ )
1660                 {
1661                         if( strncmp(Str, cFLAGS[i].Name, len) == 0 ) {
1662                                 *Mask |= cFLAGS[i].Mask;
1663                                 *Value &= ~cFLAGS[i].Mask;
1664                                 if( !bRemove )
1665                                         *Value |= cFLAGS[i].Value;
1666                                 break;
1667                         }
1668                 }
1669                 
1670                 // Error check
1671                 if( i == ciNumFlags ) {
1672                         char    val[len+1];
1673                         strncpy(val, Str, len+1);
1674                         sendf(Client->Socket, "407 Unknown flag value '%s'\n", val);
1675                         return -1;
1676                 }
1677                 
1678                 Str = space + 1;
1679         } while(space);
1680         
1681         return 0;
1682 }

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