Merge branch 'master' of ssh.ucc.asn.au:tpg/opendispense2
[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         sendf(Client->Socket,
589                 "202 Item %s:%i %s %i %s\n",
590                 Item->Handler->Name, Item->ID, status, Item->Price, Item->Name
591                 );
592 }
593
594 /**
595  * \brief Enumerate the items that the server knows about
596  */
597 void Server_Cmd_ENUMITEMS(tClient *Client, char *Args)
598 {
599          int    i, count;
600
601         if( Args != NULL && strlen(Args) ) {
602                 sendf(Client->Socket, "407 ENUM_ITEMS takes no arguments\n");
603                 return ;
604         }
605         
606         // Count shown items
607         count = 0;
608         for( i = 0; i < giNumItems; i ++ ) {
609                 if( gaItems[i].bHidden )        continue;
610                 count ++;
611         }
612
613         sendf(Client->Socket, "201 Items %i\n", count);
614
615         for( i = 0; i < giNumItems; i ++ ) {
616                 if( gaItems[i].bHidden )        continue;
617                 Server_int_SendItem( Client, &gaItems[i] );
618         }
619
620         sendf(Client->Socket, "200 List end\n");
621 }
622
623 tItem *_GetItemFromString(char *String)
624 {
625         tHandler        *handler;
626         char    *type = String;
627         char    *colon = strchr(String, ':');
628          int    num, i;
629         
630         if( !colon ) {
631                 return NULL;
632         }
633
634         num = atoi(colon+1);
635         *colon = '\0';
636
637         // Find handler
638         handler = NULL;
639         for( i = 0; i < giNumHandlers; i ++ )
640         {
641                 if( strcmp(gaHandlers[i]->Name, type) == 0) {
642                         handler = gaHandlers[i];
643                         break;
644                 }
645         }
646         if( !handler ) {
647                 return NULL;
648         }
649
650         // Find item
651         for( i = 0; i < giNumItems; i ++ )
652         {
653                 if( gaItems[i].Handler != handler )     continue;
654                 if( gaItems[i].ID != num )      continue;
655                 return &gaItems[i];
656         }
657         return NULL;
658 }
659
660 /**
661  * \brief Fetch information on a specific item
662  */
663 void Server_Cmd_ITEMINFO(tClient *Client, char *Args)
664 {
665         tItem   *item;
666         char    *itemname;
667         
668         if( Server_int_ParseArgs(0, Args, &itemname, NULL) ) {
669                 sendf(Client->Socket, "407 ITEMINFO takes 1 argument\n");
670                 return ;
671         }
672         item = _GetItemFromString(Args);
673         
674         if( !item ) {
675                 sendf(Client->Socket, "406 Bad Item ID\n");
676                 return ;
677         }
678         
679         Server_int_SendItem( Client, item );
680 }
681
682 void Server_Cmd_DISPENSE(tClient *Client, char *Args)
683 {
684         tItem   *item;
685          int    ret;
686          int    uid;
687         char    *itemname;
688         
689         if( Server_int_ParseArgs(0, Args, &itemname, NULL) ) {
690                 sendf(Client->Socket, "407 DISPENSE takes only 1 argument\n");
691                 return ;
692         }
693          
694         if( !Client->bIsAuthed ) {
695                 sendf(Client->Socket, "401 Not Authenticated\n");
696                 return ;
697         }
698
699         item = _GetItemFromString(itemname);
700         if( !item ) {
701                 sendf(Client->Socket, "406 Bad Item ID\n");
702                 return ;
703         }
704         
705         if( Client->EffectiveUID != -1 ) {
706                 uid = Client->EffectiveUID;
707         }
708         else {
709                 uid = Client->UID;
710         }
711
712         switch( ret = DispenseItem( Client->UID, uid, item ) )
713         {
714         case 0: sendf(Client->Socket, "200 Dispense OK\n");     return ;
715         case 1: sendf(Client->Socket, "501 Unable to dispense\n");      return ;
716         case 2: sendf(Client->Socket, "402 Poor You\n");        return ;
717         default:
718                 sendf(Client->Socket, "500 Dispense Error\n");
719                 return ;
720         }
721 }
722
723 void Server_Cmd_REFUND(tClient *Client, char *Args)
724 {
725         tItem   *item;
726          int    uid, price_override = 0;
727         char    *username, *itemname, *price_str;
728
729         if( Server_int_ParseArgs(0, Args, &username, &itemname, &price_str, NULL) ) {
730                 if( !itemname || price_str ) {
731                         sendf(Client->Socket, "407 REFUND takes 2 or 3 arguments\n");
732                         return ;
733                 }
734         }
735
736         if( !Client->bIsAuthed ) {
737                 sendf(Client->Socket, "401 Not Authenticated\n");
738                 return ;
739         }
740
741         // Check user permissions
742         if( !(Bank_GetFlags(Client->UID) & (USER_FLAG_COKE|USER_FLAG_ADMIN))  ) {
743                 sendf(Client->Socket, "403 Not in coke\n");
744                 return ;
745         }
746
747         uid = Bank_GetAcctByName(username);
748         if( uid == -1 ) {
749                 sendf(Client->Socket, "404 Unknown user\n");
750                 return ;
751         }
752         
753         item = _GetItemFromString(itemname);
754         if( !item ) {
755                 sendf(Client->Socket, "406 Bad Item ID\n");
756                 return ;
757         }
758
759         if( price_str )
760                 price_override = atoi(price_str);
761
762         switch( DispenseRefund( Client->UID, uid, item, price_override ) )
763         {
764         case 0: sendf(Client->Socket, "200 Item Refunded\n");   return ;
765         default:
766                 sendf(Client->Socket, "500 Dispense Error\n");
767                 return;
768         }
769 }
770
771 void Server_Cmd_GIVE(tClient *Client, char *Args)
772 {
773         char    *recipient, *ammount, *reason;
774          int    uid, iAmmount;
775          int    thisUid;
776         
777         // Parse arguments
778         if( Server_int_ParseArgs(1, Args, &recipient, &ammount, &reason, NULL) ) {
779                 sendf(Client->Socket, "407 GIVE takes only 3 arguments\n");
780                 return ;
781         }
782         // Check for authed
783         if( !Client->bIsAuthed ) {
784                 sendf(Client->Socket, "401 Not Authenticated\n");
785                 return ;
786         }
787
788         // Get recipient
789         uid = Bank_GetAcctByName(recipient);
790         if( uid == -1 ) {
791                 sendf(Client->Socket, "404 Invalid target user\n");
792                 return ;
793         }
794         
795         // You can't alter an internal account
796 //      if( Bank_GetFlags(uid) & USER_FLAG_INTERNAL ) {
797 //              sendf(Client->Socket, "404 Invalid target user\n");
798 //              return ;
799 //      }
800
801         // Parse ammount
802         iAmmount = atoi(ammount);
803         if( iAmmount <= 0 ) {
804                 sendf(Client->Socket, "407 Invalid Argument, ammount must be > zero\n");
805                 return ;
806         }
807         
808         if( Client->EffectiveUID != -1 ) {
809                 thisUid = Client->EffectiveUID;
810         }
811         else {
812                 thisUid = Client->UID;
813         }
814
815         // Do give
816         switch( DispenseGive(Client->UID, thisUid, uid, iAmmount, reason) )
817         {
818         case 0:
819                 sendf(Client->Socket, "200 Give OK\n");
820                 return ;
821         case 2:
822                 sendf(Client->Socket, "402 Poor You\n");
823                 return ;
824         default:
825                 sendf(Client->Socket, "500 Unknown error\n");
826                 return ;
827         }
828 }
829
830 void Server_Cmd_DONATE(tClient *Client, char *Args)
831 {
832         char    *ammount, *reason;
833          int    iAmmount;
834          int    thisUid;
835         
836         // Parse arguments
837         if( Server_int_ParseArgs(1, Args, &ammount, &reason, NULL) ) {
838                 sendf(Client->Socket, "407 DONATE takes 2 arguments\n");
839                 return ;
840         }
841         
842         if( !Client->bIsAuthed ) {
843                 sendf(Client->Socket, "401 Not Authenticated\n");
844                 return ;
845         }
846
847         // Parse ammount
848         iAmmount = atoi(ammount);
849         if( iAmmount <= 0 ) {
850                 sendf(Client->Socket, "407 Invalid Argument, ammount must be > zero\n");
851                 return ;
852         }
853         
854         // Handle effective users
855         if( Client->EffectiveUID != -1 ) {
856                 thisUid = Client->EffectiveUID;
857         }
858         else {
859                 thisUid = Client->UID;
860         }
861
862         // Do give
863         switch( DispenseDonate(Client->UID, thisUid, iAmmount, reason) )
864         {
865         case 0:
866                 sendf(Client->Socket, "200 Give OK\n");
867                 return ;
868         case 2:
869                 sendf(Client->Socket, "402 Poor You\n");
870                 return ;
871         default:
872                 sendf(Client->Socket, "500 Unknown error\n");
873                 return ;
874         }
875 }
876
877 void Server_Cmd_ADD(tClient *Client, char *Args)
878 {
879         char    *user, *ammount, *reason;
880          int    uid, iAmmount;
881         
882         // Parse arguments
883         if( Server_int_ParseArgs(1, Args, &user, &ammount, &reason, NULL) ) {
884                 sendf(Client->Socket, "407 ADD takes 3 arguments\n");
885                 return ;
886         }
887         
888         if( !Client->bIsAuthed ) {
889                 sendf(Client->Socket, "401 Not Authenticated\n");
890                 return ;
891         }
892
893         // Check user permissions
894         if( !(Bank_GetFlags(Client->UID) & (USER_FLAG_COKE|USER_FLAG_ADMIN))  ) {
895                 sendf(Client->Socket, "403 Not in coke\n");
896                 return ;
897         }
898
899         // Get recipient
900         uid = Bank_GetAcctByName(user);
901         if( uid == -1 ) {
902                 sendf(Client->Socket, "404 Invalid user\n");
903                 return ;
904         }
905         
906         // You can't alter an internal account
907         if( !(Bank_GetFlags(Client->UID) & USER_FLAG_ADMIN) )
908         {
909                 if( Bank_GetFlags(uid) & USER_FLAG_INTERNAL ) {
910                         sendf(Client->Socket, "404 Invalid user\n");
911                         return ;
912                 }
913                 // TODO: Maybe disallow changes to disabled?
914         }
915
916         // Parse ammount
917         iAmmount = atoi(ammount);
918         if( iAmmount == 0 && ammount[0] != '0' ) {
919                 sendf(Client->Socket, "407 Invalid Argument\n");
920                 return ;
921         }
922
923         // Do give
924         switch( DispenseAdd(Client->UID, uid, iAmmount, reason) )
925         {
926         case 0:
927                 sendf(Client->Socket, "200 Add OK\n");
928                 return ;
929         case 2:
930                 sendf(Client->Socket, "402 Poor Guy\n");
931                 return ;
932         default:
933                 sendf(Client->Socket, "500 Unknown error\n");
934                 return ;
935         }
936 }
937
938 void Server_Cmd_SET(tClient *Client, char *Args)
939 {
940         char    *user, *ammount, *reason;
941          int    uid, iAmmount;
942         
943         // Parse arguments
944         if( Server_int_ParseArgs(1, Args, &user, &ammount, &reason, NULL) ) {
945                 sendf(Client->Socket, "407 SET takes 3 arguments\n");
946                 return ;
947         }
948         
949         if( !Client->bIsAuthed ) {
950                 sendf(Client->Socket, "401 Not Authenticated\n");
951                 return ;
952         }
953
954         // Check user permissions
955         if( !(Bank_GetFlags(Client->UID) & USER_FLAG_ADMIN)  ) {
956                 sendf(Client->Socket, "403 Not an admin\n");
957                 return ;
958         }
959
960         // Get recipient
961         uid = Bank_GetAcctByName(user);
962         if( uid == -1 ) {
963                 sendf(Client->Socket, "404 Invalid user\n");
964                 return ;
965         }
966
967         // Parse ammount
968         iAmmount = atoi(ammount);
969         if( iAmmount == 0 && ammount[0] != '0' ) {
970                 sendf(Client->Socket, "407 Invalid Argument\n");
971                 return ;
972         }
973
974         // Do give
975         switch( DispenseSet(Client->UID, uid, iAmmount, reason) )
976         {
977         case 0:
978                 sendf(Client->Socket, "200 Add OK\n");
979                 return ;
980         case 2:
981                 sendf(Client->Socket, "402 Poor Guy\n");
982                 return ;
983         default:
984                 sendf(Client->Socket, "500 Unknown error\n");
985                 return ;
986         }
987 }
988
989 void Server_Cmd_ENUMUSERS(tClient *Client, char *Args)
990 {
991          int    i, numRet = 0;
992         tAcctIterator   *it;
993          int    maxBal = INT_MAX, minBal = INT_MIN;
994          int    flagMask = 0, flagVal = 0;
995          int    sort = BANK_ITFLAG_SORT_NAME;
996         time_t  lastSeenAfter=0, lastSeenBefore=0;
997         
998          int    flags;  // Iterator flags
999          int    balValue;       // Balance value for iterator
1000         time_t  timeValue;      // Time value for iterator
1001         
1002         // Parse arguments
1003         if( Args && strlen(Args) )
1004         {
1005                 char    *space = Args, *type, *val;
1006                 do
1007                 {
1008                         type = space;
1009                         while(*type == ' ')     type ++;
1010                         // Get next space
1011                         space = strchr(space, ' ');
1012                         if(space)       *space = '\0';
1013                         
1014                         // Get type
1015                         val = strchr(type, ':');
1016                         if( val ) {
1017                                 *val = '\0';
1018                                 val ++;
1019                                 
1020                                 // Types
1021                                 // - Minium Balance
1022                                 if( strcmp(type, "min_balance") == 0 ) {
1023                                         minBal = atoi(val);
1024                                 }
1025                                 // - Maximum Balance
1026                                 else if( strcmp(type, "max_balance") == 0 ) {
1027                                         maxBal = atoi(val);
1028                                 }
1029                                 // - Flags
1030                                 else if( strcmp(type, "flags") == 0 ) {
1031                                         if( Server_int_ParseFlags(Client, val, &flagMask, &flagVal) )
1032                                                 return ;
1033                                 }
1034                                 // - Last seen before timestamp
1035                                 else if( strcmp(type, "last_seen_before") == 0 ) {
1036                                         lastSeenAfter = atoll(val);
1037                                 }
1038                                 // - Last seen after timestamp
1039                                 else if( strcmp(type, "last_seen_after") == 0 ) {
1040                                         lastSeenAfter = atoll(val);
1041                                 }
1042                                 // - Sorting 
1043                                 else if( strcmp(type, "sort") == 0 ) {
1044                                         char    *dash = strchr(val, '-');
1045                                         if( dash ) {
1046                                                 *dash = '\0';
1047                                                 dash ++;
1048                                         }
1049                                         if( strcmp(val, "name") == 0 ) {
1050                                                 sort = BANK_ITFLAG_SORT_NAME;
1051                                         }
1052                                         else if( strcmp(val, "balance") == 0 ) {
1053                                                 sort = BANK_ITFLAG_SORT_BAL;
1054                                         }
1055                                         else if( strcmp(val, "lastseen") == 0 ) {
1056                                                 sort = BANK_ITFLAG_SORT_LASTSEEN;
1057                                         }
1058                                         else {
1059                                                 sendf(Client->Socket, "407 Unknown sort field ('%s')\n", val);
1060                                                 return ;
1061                                         }
1062                                         // Handle sort direction
1063                                         if( dash ) {
1064                                                 if( strcmp(dash, "desc") == 0 ) {
1065                                                         sort |= BANK_ITFLAG_REVSORT;
1066                                                 }
1067                                                 else {
1068                                                         sendf(Client->Socket, "407 Unknown sort direction '%s'\n", dash);
1069                                                         return ;
1070                                                 }
1071                                                 dash[-1] = '-';
1072                                         }
1073                                 }
1074                                 else {
1075                                         sendf(Client->Socket, "407 Unknown argument to ENUM_USERS '%s:%s'\n", type, val);
1076                                         return ;
1077                                 }
1078                                 
1079                                 val[-1] = ':';
1080                         }
1081                         else {
1082                                 sendf(Client->Socket, "407 Unknown argument to ENUM_USERS '%s'\n", type);
1083                                 return ;
1084                         }
1085                         
1086                         // Eat whitespace
1087                         if( space ) {
1088                                 *space = ' ';   // Repair (to be nice)
1089                                 space ++;
1090                                 while(*space == ' ')    space ++;
1091                         }
1092                 }       while(space);
1093         }
1094         
1095         // Create iterator
1096         if( maxBal != INT_MAX ) {
1097                 flags = sort|BANK_ITFLAG_MAXBALANCE;
1098                 balValue = maxBal;
1099         }
1100         else if( minBal != INT_MIN ) {
1101                 flags = sort|BANK_ITFLAG_MINBALANCE;
1102                 balValue = minBal;
1103         }
1104         else {
1105                 flags = sort;
1106                 balValue = 0;
1107         }
1108         if( lastSeenBefore ) {
1109                 timeValue = lastSeenBefore;
1110                 flags |= BANK_ITFLAG_SEENBEFORE;
1111         }
1112         else if( lastSeenAfter ) {
1113                 timeValue = lastSeenAfter;
1114                 flags |= BANK_ITFLAG_SEENAFTER;
1115         }
1116         else {
1117                 timeValue = 0;
1118         }
1119         it = Bank_Iterator(flagMask, flagVal, flags, balValue, timeValue);
1120         
1121         // Get return number
1122         while( (i = Bank_IteratorNext(it)) != -1 )
1123         {
1124                 int bal = Bank_GetBalance(i);
1125                 
1126                 if( bal == INT_MIN )    continue;
1127                 
1128                 if( bal < minBal )      continue;
1129                 if( bal > maxBal )      continue;
1130                 
1131                 numRet ++;
1132         }
1133         
1134         Bank_DelIterator(it);
1135         
1136         // Send count
1137         sendf(Client->Socket, "201 Users %i\n", numRet);
1138         
1139         
1140         // Create iterator
1141         it = Bank_Iterator(flagMask, flagVal, flags, balValue, timeValue);
1142         
1143         while( (i = Bank_IteratorNext(it)) != -1 )
1144         {
1145                 int bal = Bank_GetBalance(i);
1146                 
1147                 if( bal == INT_MIN )    continue;
1148                 
1149                 if( bal < minBal )      continue;
1150                 if( bal > maxBal )      continue;
1151                 
1152                 _SendUserInfo(Client, i);
1153         }
1154         
1155         Bank_DelIterator(it);
1156         
1157         sendf(Client->Socket, "200 List End\n");
1158 }
1159
1160 void Server_Cmd_USERINFO(tClient *Client, char *Args)
1161 {
1162          int    uid;
1163         char    *user;
1164         
1165         // Parse arguments
1166         if( Server_int_ParseArgs(0, Args, &user, NULL) ) {
1167                 sendf(Client->Socket, "407 USER_INFO takes 1 argument\n");
1168                 return ;
1169         }
1170         
1171         if( giDebugLevel )      Debug(Client, "User Info '%s'", user);
1172         
1173         // Get recipient
1174         uid = Bank_GetAcctByName(user);
1175         
1176         if( giDebugLevel >= 2 ) Debug(Client, "uid = %i", uid);
1177         if( uid == -1 ) {
1178                 sendf(Client->Socket, "404 Invalid user\n");
1179                 return ;
1180         }
1181         
1182         _SendUserInfo(Client, uid);
1183 }
1184
1185 void _SendUserInfo(tClient *Client, int UserID)
1186 {
1187         char    *type, *disabled="", *door="";
1188          int    flags = Bank_GetFlags(UserID);
1189         
1190         if( flags & USER_FLAG_INTERNAL ) {
1191                 type = "internal";
1192         }
1193         else if( flags & USER_FLAG_COKE ) {
1194                 if( flags & USER_FLAG_ADMIN )
1195                         type = "coke,admin";
1196                 else
1197                         type = "coke";
1198         }
1199         else if( flags & USER_FLAG_ADMIN ) {
1200                 type = "admin";
1201         }
1202         else {
1203                 type = "user";
1204         }
1205         
1206         if( flags & USER_FLAG_DISABLED )
1207                 disabled = ",disabled";
1208         if( flags & USER_FLAG_DOORGROUP )
1209                 door = ",door";
1210         
1211         // TODO: User flags/type
1212         sendf(
1213                 Client->Socket, "202 User %s %i %s%s%s\n",
1214                 Bank_GetAcctName(UserID), Bank_GetBalance(UserID),
1215                 type, disabled, door
1216                 );
1217 }
1218
1219 void Server_Cmd_USERADD(tClient *Client, char *Args)
1220 {
1221         char    *username;
1222         
1223         // Parse arguments
1224         if( Server_int_ParseArgs(0, Args, &username, NULL) ) {
1225                 sendf(Client->Socket, "407 USER_ADD takes 1 argument\n");
1226                 return ;
1227         }
1228         
1229         // Check permissions
1230         if( !(Bank_GetFlags(Client->UID) & USER_FLAG_ADMIN) ) {
1231                 sendf(Client->Socket, "403 Not a coke admin\n");
1232                 return ;
1233         }
1234         
1235         // Try to create user
1236         if( Bank_CreateAcct(username) == -1 ) {
1237                 sendf(Client->Socket, "404 User exists\n");
1238                 return ;
1239         }
1240         
1241         {
1242                 char    *thisName = Bank_GetAcctName(Client->UID);
1243                 Log_Info("Account '%s' created by '%s'", username, thisName);
1244                 free(thisName);
1245         }
1246         
1247         sendf(Client->Socket, "200 User Added\n");
1248 }
1249
1250 void Server_Cmd_USERFLAGS(tClient *Client, char *Args)
1251 {
1252         char    *username, *flags;
1253          int    mask=0, value=0;
1254          int    uid;
1255         
1256         // Parse arguments
1257         if( Server_int_ParseArgs(0, Args, &username, &flags, NULL) ) {
1258                 sendf(Client->Socket, "407 USER_FLAGS takes 2 arguments\n");
1259                 return ;
1260         }
1261         
1262         // Check permissions
1263         if( !(Bank_GetFlags(Client->UID) & USER_FLAG_ADMIN) ) {
1264                 sendf(Client->Socket, "403 Not a coke admin\n");
1265                 return ;
1266         }
1267         
1268         // Get UID
1269         uid = Bank_GetAcctByName(username);
1270         if( uid == -1 ) {
1271                 sendf(Client->Socket, "404 User '%s' not found\n", username);
1272                 return ;
1273         }
1274         
1275         // Parse flags
1276         if( Server_int_ParseFlags(Client, flags, &mask, &value) )
1277                 return ;
1278         
1279         if( giDebugLevel )
1280                 Debug(Client, "Set %i(%s) flags to %x (masked %x)\n",
1281                         uid, username, mask, value);
1282         
1283         // Apply flags
1284         Bank_SetFlags(uid, mask, value);
1285         
1286         // Return OK
1287         sendf(Client->Socket, "200 User Updated\n");
1288 }
1289
1290 void Server_Cmd_UPDATEITEM(tClient *Client, char *Args)
1291 {
1292         char    *itemname, *price_str, *description;
1293          int    price;
1294         tItem   *item;
1295         
1296         if( Server_int_ParseArgs(1, Args, &itemname, &price_str, &description, NULL) ) {
1297                 sendf(Client->Socket, "407 UPDATE_ITEM takes 3 arguments\n");
1298                 return ;
1299         }
1300         
1301         if( !Client->bIsAuthed ) {
1302                 sendf(Client->Socket, "401 Not Authenticated\n");
1303                 return ;
1304         }
1305
1306         // Check user permissions
1307         if( !(Bank_GetFlags(Client->UID) & (USER_FLAG_COKE|USER_FLAG_ADMIN))  ) {
1308                 sendf(Client->Socket, "403 Not in coke\n");
1309                 return ;
1310         }
1311         
1312         item = _GetItemFromString(itemname);
1313         if( !item ) {
1314                 // TODO: Create item?
1315                 sendf(Client->Socket, "406 Bad Item ID\n");
1316                 return ;
1317         }
1318         
1319         price = atoi(price_str);
1320         if( price <= 0 && price_str[0] != '0' ) {
1321                 sendf(CLient->Socket, "407 Invalid price set\n");
1322         }
1323         
1324         // Update the item
1325         free(item->Name);
1326         item->Name = strdup(description);
1327         item->Price = price;
1328         
1329         // Update item file
1330         Items_UpdateFile();
1331         
1332         // Return OK
1333         sendf(Client->Socket, "200 Item updated\n");
1334 }
1335
1336 // --- INTERNAL HELPERS ---
1337 void Debug(tClient *Client, const char *Format, ...)
1338 {
1339         va_list args;
1340         //printf("%010i [%i] ", (int)time(NULL), Client->ID);
1341         printf("[%i] ", Client->ID);
1342         va_start(args, Format);
1343         vprintf(Format, args);
1344         va_end(args);
1345         printf("\n");
1346 }
1347
1348 int sendf(int Socket, const char *Format, ...)
1349 {
1350         va_list args;
1351          int    len;
1352         
1353         va_start(args, Format);
1354         len = vsnprintf(NULL, 0, Format, args);
1355         va_end(args);
1356         
1357         {
1358                 char    buf[len+1];
1359                 va_start(args, Format);
1360                 vsnprintf(buf, len+1, Format, args);
1361                 va_end(args);
1362                 
1363                 #if DEBUG_TRACE_CLIENT
1364                 printf("sendf: %s", buf);
1365                 #endif
1366                 
1367                 return send(Socket, buf, len, 0);
1368         }
1369 }
1370
1371 // Takes a series of char *'s in
1372 /**
1373  * \brief Parse space-separated entries into 
1374  */
1375 int Server_int_ParseArgs(int bUseLongLast, char *ArgStr, ...)
1376 {
1377         va_list args;
1378         char    savedChar;
1379         char    **dest;
1380         va_start(args, ArgStr);
1381
1382         // Check for null
1383         if( !ArgStr )
1384         {
1385                 while( (dest = va_arg(args, char **)) )
1386                         *dest = NULL;
1387                 va_end(args);
1388                 return 1;
1389         }
1390
1391         savedChar = *ArgStr;
1392         
1393         while( (dest = va_arg(args, char **)) )
1394         {
1395                 // Trim leading spaces
1396                 while( *ArgStr == ' ' || *ArgStr == '\t' )
1397                         ArgStr ++;
1398                 
1399                 // ... oops, not enough arguments
1400                 if( *ArgStr == '\0' )
1401                 {
1402                         // NULL unset arguments
1403                         do {
1404                                 *dest = NULL;
1405                         }       while( (dest = va_arg(args, char **)) );
1406                 va_end(args);
1407                         return -1;
1408                 }
1409                 
1410                 if( *ArgStr == '"' )
1411                 {
1412                         ArgStr ++;
1413                         *dest = ArgStr;
1414                         // Read until quote
1415                         while( *ArgStr && *ArgStr != '"' )
1416                                 ArgStr ++;
1417                 }
1418                 else
1419                 {
1420                         // Set destination
1421                         *dest = ArgStr;
1422                         // Read until a space
1423                         while( *ArgStr && *ArgStr != ' ' && *ArgStr != '\t' )
1424                                 ArgStr ++;
1425                 }
1426                 savedChar = *ArgStr;    // savedChar is used to un-mangle the last string
1427                 *ArgStr = '\0';
1428                 ArgStr ++;
1429         }
1430         va_end(args);
1431         
1432         // Oops, extra arguments, and greedy not set
1433         if( (savedChar == ' ' || savedChar == '\t') && !bUseLongLast ) {
1434                 return -1;
1435         }
1436         
1437         // Un-mangle last
1438         if(bUseLongLast) {
1439                 ArgStr --;
1440                 *ArgStr = savedChar;
1441         }
1442         
1443         return 0;       // Success!
1444 }
1445
1446 int Server_int_ParseFlags(tClient *Client, const char *Str, int *Mask, int *Value)
1447 {
1448         struct {
1449                 const char      *Name;
1450                  int    Mask;
1451                  int    Value;
1452         }       cFLAGS[] = {
1453                  {"disabled", USER_FLAG_DISABLED, USER_FLAG_DISABLED}
1454                 ,{"door", USER_FLAG_DOORGROUP, USER_FLAG_DOORGROUP}
1455                 ,{"coke", USER_FLAG_COKE, USER_FLAG_COKE}
1456                 ,{"admin", USER_FLAG_ADMIN, USER_FLAG_ADMIN}
1457                 ,{"internal", USER_FLAG_INTERNAL, USER_FLAG_INTERNAL}
1458         };
1459         const int       ciNumFlags = sizeof(cFLAGS)/sizeof(cFLAGS[0]);
1460         
1461         char    *space;
1462         
1463         *Mask = 0;
1464         *Value = 0;
1465         
1466         do {
1467                  int    bRemove = 0;
1468                  int    i;
1469                  int    len;
1470                 
1471                 while( *Str == ' ' )    Str ++; // Eat whitespace
1472                 space = strchr(Str, ',');       // Find the end of the flag
1473                 if(space)
1474                         len = space - Str;
1475                 else
1476                         len = strlen(Str);
1477                 
1478                 // Check for inversion/removal
1479                 if( *Str == '!' || *Str == '-' ) {
1480                         bRemove = 1;
1481                         Str ++;
1482                 }
1483                 else if( *Str == '+' ) {
1484                         Str ++;
1485                 }
1486                 
1487                 // Check flag values
1488                 for( i = 0; i < ciNumFlags; i ++ )
1489                 {
1490                         if( strncmp(Str, cFLAGS[i].Name, len) == 0 ) {
1491                                 *Mask |= cFLAGS[i].Mask;
1492                                 *Value &= ~cFLAGS[i].Mask;
1493                                 if( !bRemove )
1494                                         *Value |= cFLAGS[i].Value;
1495                                 break;
1496                         }
1497                 }
1498                 
1499                 // Error check
1500                 if( i == ciNumFlags ) {
1501                         char    val[len+1];
1502                         strncpy(val, Str, len+1);
1503                         sendf(Client->Socket, "407 Unknown flag value '%s'\n", val);
1504                         return -1;
1505                 }
1506                 
1507                 Str = space + 1;
1508         } while(space);
1509         
1510         return 0;
1511 }

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