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

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