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

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