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

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