Fixing bugs and slight cleanup
[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         // Fork into background
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                 // Is this an error? (just ignore for now)
357         }
358         
359         
360         // Find command
361         for( i = 0; i < NUM_COMMANDS; i++ )
362         {
363                 if(strcmp(command, gaServer_Commands[i].Name) == 0) {
364                         if( giDebugLevel >= 2 )
365                                 Debug(Client, "CMD %s - \"%s\"", command, args);
366                         gaServer_Commands[i].Function(Client, args);
367                         return ;
368                 }
369         }
370         
371         sendf(Client->Socket, "400 Unknown Command\n");
372 }
373
374 // ---
375 // Commands
376 // ---
377 /**
378  * \brief Set client username
379  * 
380  * Usage: USER <username>
381  */
382 void Server_Cmd_USER(tClient *Client, char *Args)
383 {
384         char    *username;
385         
386         if( Server_int_ParseArgs(0, Args, &username, NULL) )
387         {
388                 sendf(Client->Socket, "407 USER takes 1 argument\n");
389                 return ;
390         }
391         
392         // Debug!
393         if( giDebugLevel )
394                 Debug(Client, "Authenticating as '%s'", username);
395         
396         // Save username
397         if(Client->Username)
398                 free(Client->Username);
399         Client->Username = strdup(username);
400         
401         #if USE_SALT
402         // Create a salt (that changes if the username is changed)
403         // Yes, I know, I'm a little paranoid, but who isn't?
404         Client->Salt[0] = 0x21 + (rand()&0x3F);
405         Client->Salt[1] = 0x21 + (rand()&0x3F);
406         Client->Salt[2] = 0x21 + (rand()&0x3F);
407         Client->Salt[3] = 0x21 + (rand()&0x3F);
408         Client->Salt[4] = 0x21 + (rand()&0x3F);
409         Client->Salt[5] = 0x21 + (rand()&0x3F);
410         Client->Salt[6] = 0x21 + (rand()&0x3F);
411         Client->Salt[7] = 0x21 + (rand()&0x3F);
412         
413         // TODO: Also send hash type to use, (SHA1 or crypt according to [DAA])
414         sendf(Client->Socket, "100 SALT %s\n", Client->Salt);
415         #else
416         sendf(Client->Socket, "100 User Set\n");
417         #endif
418 }
419
420 /**
421  * \brief Authenticate as a user
422  * 
423  * Usage: PASS <hash>
424  */
425 void Server_Cmd_PASS(tClient *Client, char *Args)
426 {
427         char    *passhash;
428          int    flags;
429
430         if( Server_int_ParseArgs(0, Args, &passhash, NULL) )
431         {
432                 sendf(Client->Socket, "407 PASS takes 1 argument\n");
433                 return ;
434         }
435         
436         // Pass on to cokebank
437         Client->UID = Bank_GetUserAuth(Client->Salt, Client->Username, passhash);
438
439         if( Client->UID == -1 ) {
440                 sendf(Client->Socket, "401 Auth Failure\n");
441                 return ;
442         }
443
444         flags = Bank_GetFlags(Client->UID);
445         if( flags & USER_FLAG_DISABLED ) {
446                 Client->UID = -1;
447                 sendf(Client->Socket, "403 Account Disabled\n");
448                 return ;
449         }
450         if( flags & USER_FLAG_INTERNAL ) {
451                 Client->UID = -1;
452                 sendf(Client->Socket, "403 Internal account\n");
453                 return ;
454         }
455         
456         Client->bIsAuthed = 1;
457         sendf(Client->Socket, "200 Auth OK\n");
458 }
459
460 /**
461  * \brief Authenticate as a user without a password
462  * 
463  * Usage: AUTOAUTH <user>
464  */
465 void Server_Cmd_AUTOAUTH(tClient *Client, char *Args)
466 {
467         char    *username;
468          int    userflags;
469         
470         if( Server_int_ParseArgs(0, Args, &username, NULL) )
471         {
472                 sendf(Client->Socket, "407 AUTOAUTH takes 1 argument\n");
473                 return ;
474         }
475         
476         // Check if trusted
477         if( !Client->bIsTrusted ) {
478                 if(giDebugLevel)
479                         Debug(Client, "Untrusted client attempting to AUTOAUTH");
480                 sendf(Client->Socket, "401 Untrusted\n");
481                 return ;
482         }
483         
484         // Get UID
485         Client->UID = Bank_GetAcctByName( username, 0 );        
486         if( Client->UID < 0 ) {
487                 if(giDebugLevel)
488                         Debug(Client, "Unknown user '%s'", username);
489                 sendf(Client->Socket, "403 Auth Failure\n");
490                 return ;
491         }
492         
493         userflags = Bank_GetFlags(Client->UID);
494         // You can't be an internal account
495         if( userflags & USER_FLAG_INTERNAL ) {
496                 if(giDebugLevel)
497                         Debug(Client, "Autoauth as '%s', not allowed", username);
498                 Client->UID = -1;
499                 sendf(Client->Socket, "403 Account is internal\n");
500                 return ;
501         }
502
503         // Disabled accounts
504         if( userflags & USER_FLAG_DISABLED ) {
505                 Client->UID = -1;
506                 sendf(Client->Socket, "403 Account disabled\n");
507                 return ;
508         }
509
510         Client->bIsAuthed = 1;
511         
512         if(giDebugLevel)
513                 Debug(Client, "Auto authenticated as '%s' (%i)", username, Client->UID);
514         
515         sendf(Client->Socket, "200 Auth OK\n");
516 }
517
518 /**
519  * \brief Set effective user
520  */
521 void Server_Cmd_SETEUSER(tClient *Client, char *Args)
522 {
523         char    *username;
524          int    eUserFlags, userFlags;
525         
526         if( Server_int_ParseArgs(0, Args, &username, NULL) )
527         {
528                 sendf(Client->Socket, "407 SETEUSER takes 1 argument\n");
529                 return ;
530         }
531         
532         if( !strlen(Args) ) {
533                 sendf(Client->Socket, "407 SETEUSER expects an argument\n");
534                 return ;
535         }
536         
537         // Check authentication
538         if( !Client->bIsAuthed ) {
539                 sendf(Client->Socket, "401 Not Authenticated\n");
540                 return ;
541         }
542
543         // Check user permissions
544         userFlags = Bank_GetFlags(Client->UID);
545         if( !(userFlags & (USER_FLAG_COKE|USER_FLAG_ADMIN)) ) {
546                 sendf(Client->Socket, "403 Not in coke\n");
547                 return ;
548         }
549         
550         // Set id
551         Client->EffectiveUID = Bank_GetAcctByName(username, 0);
552         if( Client->EffectiveUID == -1 ) {
553                 sendf(Client->Socket, "404 User not found\n");
554                 return ;
555         }
556         
557         // You can't be an internal account
558         if( !(userFlags & USER_FLAG_ADMIN) )
559         {
560                 eUserFlags = Bank_GetFlags(Client->EffectiveUID);
561                 if( eUserFlags & USER_FLAG_INTERNAL ) {
562                         Client->EffectiveUID = -1;
563                         sendf(Client->Socket, "404 User not found\n");
564                         return ;
565                 }
566                 // Disabled only avaliable to admins
567                 if( eUserFlags & USER_FLAG_DISABLED ) {
568                         Client->EffectiveUID = -1;
569                         sendf(Client->Socket, "403 Account disabled\n");
570                         return ;
571                 }
572         }
573         
574         sendf(Client->Socket, "200 User set\n");
575 }
576
577 /**
578  * \brief Send an item status to the client
579  * \param Client        Who to?
580  * \param Item  Item to send
581  */
582 void Server_int_SendItem(tClient *Client, tItem *Item)
583 {
584         char    *status = "avail";
585         
586         if( Item->Handler->CanDispense )
587         {
588                 switch(Item->Handler->CanDispense(Client->UID, Item->ID))
589                 {
590                 case  0:        status = "avail";       break;
591                 case  1:        status = "sold";        break;
592                 default:
593                 case -1:        status = "error";       break;
594                 }
595         }
596         
597         // KNOWN HACK: Naming a slot 'dead' disables it
598         if( strcmp(Item->Name, "dead") == 0 )
599                 status = "sold";        // Another status?
600         
601         sendf(Client->Socket,
602                 "202 Item %s:%i %s %i %s\n",
603                 Item->Handler->Name, Item->ID, status, Item->Price, Item->Name
604                 );
605 }
606
607 /**
608  * \brief Enumerate the items that the server knows about
609  */
610 void Server_Cmd_ENUMITEMS(tClient *Client, char *Args)
611 {
612          int    i, count;
613
614         if( Args != NULL && strlen(Args) ) {
615                 sendf(Client->Socket, "407 ENUM_ITEMS takes no arguments\n");
616                 return ;
617         }
618         
619         // Count shown items
620         count = 0;
621         for( i = 0; i < giNumItems; i ++ ) {
622                 if( gaItems[i].bHidden )        continue;
623                 count ++;
624         }
625
626         sendf(Client->Socket, "201 Items %i\n", count);
627
628         for( i = 0; i < giNumItems; i ++ ) {
629                 if( gaItems[i].bHidden )        continue;
630                 Server_int_SendItem( Client, &gaItems[i] );
631         }
632
633         sendf(Client->Socket, "200 List end\n");
634 }
635
636 tItem *_GetItemFromString(char *String)
637 {
638         tHandler        *handler;
639         char    *type = String;
640         char    *colon = strchr(String, ':');
641          int    num, i;
642         
643         if( !colon ) {
644                 return NULL;
645         }
646
647         num = atoi(colon+1);
648         *colon = '\0';
649
650         // Find handler
651         handler = NULL;
652         for( i = 0; i < giNumHandlers; i ++ )
653         {
654                 if( strcmp(gaHandlers[i]->Name, type) == 0) {
655                         handler = gaHandlers[i];
656                         break;
657                 }
658         }
659         if( !handler ) {
660                 return NULL;
661         }
662
663         // Find item
664         for( i = 0; i < giNumItems; i ++ )
665         {
666                 if( gaItems[i].Handler != handler )     continue;
667                 if( gaItems[i].ID != num )      continue;
668                 return &gaItems[i];
669         }
670         return NULL;
671 }
672
673 /**
674  * \brief Fetch information on a specific item
675  */
676 void Server_Cmd_ITEMINFO(tClient *Client, char *Args)
677 {
678         tItem   *item;
679         char    *itemname;
680         
681         if( Server_int_ParseArgs(0, Args, &itemname, NULL) ) {
682                 sendf(Client->Socket, "407 ITEMINFO takes 1 argument\n");
683                 return ;
684         }
685         item = _GetItemFromString(Args);
686         
687         if( !item ) {
688                 sendf(Client->Socket, "406 Bad Item ID\n");
689                 return ;
690         }
691         
692         Server_int_SendItem( Client, item );
693 }
694
695 void Server_Cmd_DISPENSE(tClient *Client, char *Args)
696 {
697         tItem   *item;
698          int    ret;
699          int    uid;
700         char    *itemname;
701         
702         if( Server_int_ParseArgs(0, Args, &itemname, NULL) ) {
703                 sendf(Client->Socket, "407 DISPENSE takes only 1 argument\n");
704                 return ;
705         }
706          
707         if( !Client->bIsAuthed ) {
708                 sendf(Client->Socket, "401 Not Authenticated\n");
709                 return ;
710         }
711
712         item = _GetItemFromString(itemname);
713         if( !item ) {
714                 sendf(Client->Socket, "406 Bad Item ID\n");
715                 return ;
716         }
717         
718         if( Client->EffectiveUID != -1 ) {
719                 uid = Client->EffectiveUID;
720         }
721         else {
722                 uid = Client->UID;
723         }
724
725         switch( ret = DispenseItem( Client->UID, uid, item ) )
726         {
727         case 0: sendf(Client->Socket, "200 Dispense OK\n");     return ;
728         case 1: sendf(Client->Socket, "501 Unable to dispense\n");      return ;
729         case 2: sendf(Client->Socket, "402 Poor You\n");        return ;
730         default:
731                 sendf(Client->Socket, "500 Dispense Error\n");
732                 return ;
733         }
734 }
735
736 void Server_Cmd_REFUND(tClient *Client, char *Args)
737 {
738         tItem   *item;
739          int    uid, price_override = 0;
740         char    *username, *itemname, *price_str;
741
742         if( Server_int_ParseArgs(0, Args, &username, &itemname, &price_str, NULL) ) {
743                 if( !itemname || price_str ) {
744                         sendf(Client->Socket, "407 REFUND takes 2 or 3 arguments\n");
745                         return ;
746                 }
747         }
748
749         if( !Client->bIsAuthed ) {
750                 sendf(Client->Socket, "401 Not Authenticated\n");
751                 return ;
752         }
753
754         // Check user permissions
755         if( !(Bank_GetFlags(Client->UID) & (USER_FLAG_COKE|USER_FLAG_ADMIN))  ) {
756                 sendf(Client->Socket, "403 Not in coke\n");
757                 return ;
758         }
759
760         uid = Bank_GetAcctByName(username, 0);
761         if( uid == -1 ) {
762                 sendf(Client->Socket, "404 Unknown user\n");
763                 return ;
764         }
765         
766         item = _GetItemFromString(itemname);
767         if( !item ) {
768                 sendf(Client->Socket, "406 Bad Item ID\n");
769                 return ;
770         }
771
772         if( price_str )
773                 price_override = atoi(price_str);
774
775         switch( DispenseRefund( Client->UID, uid, item, price_override ) )
776         {
777         case 0: sendf(Client->Socket, "200 Item Refunded\n");   return ;
778         default:
779                 sendf(Client->Socket, "500 Dispense Error\n");
780                 return;
781         }
782 }
783
784 void Server_Cmd_GIVE(tClient *Client, char *Args)
785 {
786         char    *recipient, *ammount, *reason;
787          int    uid, iAmmount;
788          int    thisUid;
789         
790         // Parse arguments
791         if( Server_int_ParseArgs(1, Args, &recipient, &ammount, &reason, NULL) ) {
792                 sendf(Client->Socket, "407 GIVE takes only 3 arguments\n");
793                 return ;
794         }
795         
796         // Check for authed
797         if( !Client->bIsAuthed ) {
798                 sendf(Client->Socket, "401 Not Authenticated\n");
799                 return ;
800         }
801
802         // Get recipient
803         uid = Bank_GetAcctByName(recipient, 0);
804         if( uid == -1 ) {
805                 sendf(Client->Socket, "404 Invalid target user\n");
806                 return ;
807         }
808         
809         // You can't alter an internal account
810 //      if( Bank_GetFlags(uid) & USER_FLAG_INTERNAL ) {
811 //              sendf(Client->Socket, "404 Invalid target user\n");
812 //              return ;
813 //      }
814
815         // Parse ammount
816         iAmmount = atoi(ammount);
817         if( iAmmount <= 0 ) {
818                 sendf(Client->Socket, "407 Invalid Argument, ammount must be > zero\n");
819                 return ;
820         }
821         
822         if( Client->EffectiveUID != -1 ) {
823                 thisUid = Client->EffectiveUID;
824         }
825         else {
826                 thisUid = Client->UID;
827         }
828
829         // Do give
830         switch( DispenseGive(Client->UID, thisUid, uid, iAmmount, reason) )
831         {
832         case 0:
833                 sendf(Client->Socket, "200 Give OK\n");
834                 return ;
835         case 2:
836                 sendf(Client->Socket, "402 Poor You\n");
837                 return ;
838         default:
839                 sendf(Client->Socket, "500 Unknown error\n");
840                 return ;
841         }
842 }
843
844 void Server_Cmd_DONATE(tClient *Client, char *Args)
845 {
846         char    *ammount, *reason;
847          int    iAmmount;
848          int    thisUid;
849         
850         // Parse arguments
851         if( Server_int_ParseArgs(1, Args, &ammount, &reason, NULL) ) {
852                 sendf(Client->Socket, "407 DONATE takes 2 arguments\n");
853                 return ;
854         }
855         
856         if( !Client->bIsAuthed ) {
857                 sendf(Client->Socket, "401 Not Authenticated\n");
858                 return ;
859         }
860
861         // Parse ammount
862         iAmmount = atoi(ammount);
863         if( iAmmount <= 0 ) {
864                 sendf(Client->Socket, "407 Invalid Argument, ammount must be > zero\n");
865                 return ;
866         }
867         
868         // Handle effective users
869         if( Client->EffectiveUID != -1 ) {
870                 thisUid = Client->EffectiveUID;
871         }
872         else {
873                 thisUid = Client->UID;
874         }
875
876         // Do give
877         switch( DispenseDonate(Client->UID, thisUid, iAmmount, reason) )
878         {
879         case 0:
880                 sendf(Client->Socket, "200 Give OK\n");
881                 return ;
882         case 2:
883                 sendf(Client->Socket, "402 Poor You\n");
884                 return ;
885         default:
886                 sendf(Client->Socket, "500 Unknown error\n");
887                 return ;
888         }
889 }
890
891 void Server_Cmd_ADD(tClient *Client, char *Args)
892 {
893         char    *user, *ammount, *reason;
894          int    uid, iAmmount;
895         
896         // Parse arguments
897         if( Server_int_ParseArgs(1, Args, &user, &ammount, &reason, NULL) ) {
898                 sendf(Client->Socket, "407 ADD takes 3 arguments\n");
899                 return ;
900         }
901         
902         if( !Client->bIsAuthed ) {
903                 sendf(Client->Socket, "401 Not Authenticated\n");
904                 return ;
905         }
906
907         // Check user permissions
908         if( !(Bank_GetFlags(Client->UID) & (USER_FLAG_COKE|USER_FLAG_ADMIN))  ) {
909                 sendf(Client->Socket, "403 Not in coke\n");
910                 return ;
911         }
912
913         // Get recipient
914         uid = Bank_GetAcctByName(user, 0);
915         if( uid == -1 ) {
916                 sendf(Client->Socket, "404 Invalid user\n");
917                 return ;
918         }
919         
920         // You can't alter an internal account
921         if( !(Bank_GetFlags(Client->UID) & USER_FLAG_ADMIN) )
922         {
923                 if( Bank_GetFlags(uid) & USER_FLAG_INTERNAL ) {
924                         sendf(Client->Socket, "404 Invalid user\n");
925                         return ;
926                 }
927                 // TODO: Maybe disallow changes to disabled?
928         }
929
930         // Parse ammount
931         iAmmount = atoi(ammount);
932         if( iAmmount == 0 && ammount[0] != '0' ) {
933                 sendf(Client->Socket, "407 Invalid Argument\n");
934                 return ;
935         }
936
937         // Do give
938         switch( DispenseAdd(Client->UID, uid, iAmmount, reason) )
939         {
940         case 0:
941                 sendf(Client->Socket, "200 Add OK\n");
942                 return ;
943         case 2:
944                 sendf(Client->Socket, "402 Poor Guy\n");
945                 return ;
946         default:
947                 sendf(Client->Socket, "500 Unknown error\n");
948                 return ;
949         }
950 }
951
952 void Server_Cmd_SET(tClient *Client, char *Args)
953 {
954         char    *user, *ammount, *reason;
955          int    uid, iAmmount;
956         
957         // Parse arguments
958         if( Server_int_ParseArgs(1, Args, &user, &ammount, &reason, NULL) ) {
959                 sendf(Client->Socket, "407 SET takes 3 arguments\n");
960                 return ;
961         }
962         
963         if( !Client->bIsAuthed ) {
964                 sendf(Client->Socket, "401 Not Authenticated\n");
965                 return ;
966         }
967
968         // Check user permissions
969         if( !(Bank_GetFlags(Client->UID) & USER_FLAG_ADMIN)  ) {
970                 sendf(Client->Socket, "403 Not an admin\n");
971                 return ;
972         }
973
974         // Get recipient
975         uid = Bank_GetAcctByName(user, 0);
976         if( uid == -1 ) {
977                 sendf(Client->Socket, "404 Invalid user\n");
978                 return ;
979         }
980
981         // Parse ammount
982         iAmmount = atoi(ammount);
983         if( iAmmount == 0 && ammount[0] != '0' ) {
984                 sendf(Client->Socket, "407 Invalid Argument\n");
985                 return ;
986         }
987
988         // Do give
989         switch( DispenseSet(Client->UID, uid, iAmmount, reason) )
990         {
991         case 0:
992                 sendf(Client->Socket, "200 Add OK\n");
993                 return ;
994         case 2:
995                 sendf(Client->Socket, "402 Poor Guy\n");
996                 return ;
997         default:
998                 sendf(Client->Socket, "500 Unknown error\n");
999                 return ;
1000         }
1001 }
1002
1003 void Server_Cmd_ENUMUSERS(tClient *Client, char *Args)
1004 {
1005          int    i, numRet = 0;
1006         tAcctIterator   *it;
1007          int    maxBal = INT_MAX, minBal = INT_MIN;
1008          int    flagMask = 0, flagVal = 0;
1009          int    sort = BANK_ITFLAG_SORT_NAME;
1010         time_t  lastSeenAfter=0, lastSeenBefore=0;
1011         
1012          int    flags;  // Iterator flags
1013          int    balValue;       // Balance value for iterator
1014         time_t  timeValue;      // Time value for iterator
1015         
1016         // Parse arguments
1017         if( Args && strlen(Args) )
1018         {
1019                 char    *space = Args, *type, *val;
1020                 do
1021                 {
1022                         type = space;
1023                         while(*type == ' ')     type ++;
1024                         // Get next space
1025                         space = strchr(space, ' ');
1026                         if(space)       *space = '\0';
1027                         
1028                         // Get type
1029                         val = strchr(type, ':');
1030                         if( val ) {
1031                                 *val = '\0';
1032                                 val ++;
1033                                 
1034                                 // Types
1035                                 // - Minium Balance
1036                                 if( strcmp(type, "min_balance") == 0 ) {
1037                                         minBal = atoi(val);
1038                                 }
1039                                 // - Maximum Balance
1040                                 else if( strcmp(type, "max_balance") == 0 ) {
1041                                         maxBal = atoi(val);
1042                                 }
1043                                 // - Flags
1044                                 else if( strcmp(type, "flags") == 0 ) {
1045                                         if( Server_int_ParseFlags(Client, val, &flagMask, &flagVal) )
1046                                                 return ;
1047                                 }
1048                                 // - Last seen before timestamp
1049                                 else if( strcmp(type, "last_seen_before") == 0 ) {
1050                                         lastSeenAfter = atoll(val);
1051                                 }
1052                                 // - Last seen after timestamp
1053                                 else if( strcmp(type, "last_seen_after") == 0 ) {
1054                                         lastSeenAfter = atoll(val);
1055                                 }
1056                                 // - Sorting 
1057                                 else if( strcmp(type, "sort") == 0 ) {
1058                                         char    *dash = strchr(val, '-');
1059                                         if( dash ) {
1060                                                 *dash = '\0';
1061                                                 dash ++;
1062                                         }
1063                                         if( strcmp(val, "name") == 0 ) {
1064                                                 sort = BANK_ITFLAG_SORT_NAME;
1065                                         }
1066                                         else if( strcmp(val, "balance") == 0 ) {
1067                                                 sort = BANK_ITFLAG_SORT_BAL;
1068                                         }
1069                                         else if( strcmp(val, "lastseen") == 0 ) {
1070                                                 sort = BANK_ITFLAG_SORT_LASTSEEN;
1071                                         }
1072                                         else {
1073                                                 sendf(Client->Socket, "407 Unknown sort field ('%s')\n", val);
1074                                                 return ;
1075                                         }
1076                                         // Handle sort direction
1077                                         if( dash ) {
1078                                                 if( strcmp(dash, "desc") == 0 ) {
1079                                                         sort |= BANK_ITFLAG_REVSORT;
1080                                                 }
1081                                                 else {
1082                                                         sendf(Client->Socket, "407 Unknown sort direction '%s'\n", dash);
1083                                                         return ;
1084                                                 }
1085                                                 dash[-1] = '-';
1086                                         }
1087                                 }
1088                                 else {
1089                                         sendf(Client->Socket, "407 Unknown argument to ENUM_USERS '%s:%s'\n", type, val);
1090                                         return ;
1091                                 }
1092                                 
1093                                 val[-1] = ':';
1094                         }
1095                         else {
1096                                 sendf(Client->Socket, "407 Unknown argument to ENUM_USERS '%s'\n", type);
1097                                 return ;
1098                         }
1099                         
1100                         // Eat whitespace
1101                         if( space ) {
1102                                 *space = ' ';   // Repair (to be nice)
1103                                 space ++;
1104                                 while(*space == ' ')    space ++;
1105                         }
1106                 }       while(space);
1107         }
1108         
1109         // Create iterator
1110         if( maxBal != INT_MAX ) {
1111                 flags = sort|BANK_ITFLAG_MAXBALANCE;
1112                 balValue = maxBal;
1113         }
1114         else if( minBal != INT_MIN ) {
1115                 flags = sort|BANK_ITFLAG_MINBALANCE;
1116                 balValue = minBal;
1117         }
1118         else {
1119                 flags = sort;
1120                 balValue = 0;
1121         }
1122         if( lastSeenBefore ) {
1123                 timeValue = lastSeenBefore;
1124                 flags |= BANK_ITFLAG_SEENBEFORE;
1125         }
1126         else if( lastSeenAfter ) {
1127                 timeValue = lastSeenAfter;
1128                 flags |= BANK_ITFLAG_SEENAFTER;
1129         }
1130         else {
1131                 timeValue = 0;
1132         }
1133         it = Bank_Iterator(flagMask, flagVal, flags, balValue, timeValue);
1134         
1135         // Get return number
1136         while( (i = Bank_IteratorNext(it)) != -1 )
1137         {
1138                 int bal = Bank_GetBalance(i);
1139                 
1140                 if( bal == INT_MIN )    continue;
1141                 
1142                 if( bal < minBal )      continue;
1143                 if( bal > maxBal )      continue;
1144                 
1145                 numRet ++;
1146         }
1147         
1148         Bank_DelIterator(it);
1149         
1150         // Send count
1151         sendf(Client->Socket, "201 Users %i\n", numRet);
1152         
1153         
1154         // Create iterator
1155         it = Bank_Iterator(flagMask, flagVal, flags, balValue, timeValue);
1156         
1157         while( (i = Bank_IteratorNext(it)) != -1 )
1158         {
1159                 int bal = Bank_GetBalance(i);
1160                 
1161                 if( bal == INT_MIN )    continue;
1162                 
1163                 if( bal < minBal )      continue;
1164                 if( bal > maxBal )      continue;
1165                 
1166                 _SendUserInfo(Client, i);
1167         }
1168         
1169         Bank_DelIterator(it);
1170         
1171         sendf(Client->Socket, "200 List End\n");
1172 }
1173
1174 void Server_Cmd_USERINFO(tClient *Client, char *Args)
1175 {
1176          int    uid;
1177         char    *user;
1178         
1179         // Parse arguments
1180         if( Server_int_ParseArgs(0, Args, &user, NULL) ) {
1181                 sendf(Client->Socket, "407 USER_INFO takes 1 argument\n");
1182                 return ;
1183         }
1184         
1185         if( giDebugLevel )      Debug(Client, "User Info '%s'", user);
1186         
1187         // Get recipient
1188         uid = Bank_GetAcctByName(user, 0);
1189         
1190         if( giDebugLevel >= 2 ) Debug(Client, "uid = %i", uid);
1191         if( uid == -1 ) {
1192                 sendf(Client->Socket, "404 Invalid user\n");
1193                 return ;
1194         }
1195         
1196         _SendUserInfo(Client, uid);
1197 }
1198
1199 void _SendUserInfo(tClient *Client, int UserID)
1200 {
1201         char    *type, *disabled="", *door="";
1202          int    flags = Bank_GetFlags(UserID);
1203         
1204         if( flags & USER_FLAG_INTERNAL ) {
1205                 type = "internal";
1206         }
1207         else if( flags & USER_FLAG_COKE ) {
1208                 if( flags & USER_FLAG_ADMIN )
1209                         type = "coke,admin";
1210                 else
1211                         type = "coke";
1212         }
1213         else if( flags & USER_FLAG_ADMIN ) {
1214                 type = "admin";
1215         }
1216         else {
1217                 type = "user";
1218         }
1219         
1220         if( flags & USER_FLAG_DISABLED )
1221                 disabled = ",disabled";
1222         if( flags & USER_FLAG_DOORGROUP )
1223                 door = ",door";
1224         
1225         // TODO: User flags/type
1226         sendf(
1227                 Client->Socket, "202 User %s %i %s%s%s\n",
1228                 Bank_GetAcctName(UserID), Bank_GetBalance(UserID),
1229                 type, disabled, door
1230                 );
1231 }
1232
1233 void Server_Cmd_USERADD(tClient *Client, char *Args)
1234 {
1235         char    *username;
1236         
1237         // Parse arguments
1238         if( Server_int_ParseArgs(0, Args, &username, NULL) ) {
1239                 sendf(Client->Socket, "407 USER_ADD takes 1 argument\n");
1240                 return ;
1241         }
1242         
1243         // Check authentication
1244         if( !Client->bIsAuthed ) {
1245                 sendf(Client->Socket, "401 Not Authenticated\n");
1246                 return ;
1247         }
1248         
1249         // Check permissions
1250         if( !(Bank_GetFlags(Client->UID) & USER_FLAG_ADMIN) ) {
1251                 sendf(Client->Socket, "403 Not a coke admin\n");
1252                 return ;
1253         }
1254         
1255         // Try to create user
1256         if( Bank_CreateAcct(username) == -1 ) {
1257                 sendf(Client->Socket, "404 User exists\n");
1258                 return ;
1259         }
1260         
1261         {
1262                 char    *thisName = Bank_GetAcctName(Client->UID);
1263                 Log_Info("Account '%s' created by '%s'", username, thisName);
1264                 free(thisName);
1265         }
1266         
1267         sendf(Client->Socket, "200 User Added\n");
1268 }
1269
1270 void Server_Cmd_USERFLAGS(tClient *Client, char *Args)
1271 {
1272         char    *username, *flags, *reason=NULL;
1273          int    mask=0, value=0;
1274          int    uid;
1275         
1276         // Parse arguments
1277         if( Server_int_ParseArgs(1, Args, &username, &flags, &reason, NULL) ) {
1278                 if( !flags ) {
1279                         sendf(Client->Socket, "407 USER_FLAGS takes at least 2 arguments\n");
1280                         return ;
1281                 }
1282                 reason = "";
1283         }
1284         
1285         // Check authentication
1286         if( !Client->bIsAuthed ) {
1287                 sendf(Client->Socket, "401 Not Authenticated\n");
1288                 return ;
1289         }
1290         
1291         // Check permissions
1292         if( !(Bank_GetFlags(Client->UID) & USER_FLAG_ADMIN) ) {
1293                 sendf(Client->Socket, "403 Not a coke admin\n");
1294                 return ;
1295         }
1296         
1297         // Get UID
1298         uid = Bank_GetAcctByName(username, 0);
1299         if( uid == -1 ) {
1300                 sendf(Client->Socket, "404 User '%s' not found\n", username);
1301                 return ;
1302         }
1303         
1304         // Parse flags
1305         if( Server_int_ParseFlags(Client, flags, &mask, &value) )
1306                 return ;
1307         
1308         if( giDebugLevel )
1309                 Debug(Client, "Set %i(%s) flags to %x (masked %x)\n",
1310                         uid, username, mask, value);
1311         
1312         // Apply flags
1313         Bank_SetFlags(uid, mask, value);
1314
1315         // Log the change
1316         Log_Info("Updated '%s' with flag set '%s' - Reason: %s",
1317                 username, flags, reason);
1318         
1319         // Return OK
1320         sendf(Client->Socket, "200 User Updated\n");
1321 }
1322
1323 void Server_Cmd_UPDATEITEM(tClient *Client, char *Args)
1324 {
1325         char    *itemname, *price_str, *description;
1326          int    price;
1327         tItem   *item;
1328         
1329         if( Server_int_ParseArgs(1, Args, &itemname, &price_str, &description, NULL) ) {
1330                 sendf(Client->Socket, "407 UPDATE_ITEM takes 3 arguments\n");
1331                 return ;
1332         }
1333         
1334         if( !Client->bIsAuthed ) {
1335                 sendf(Client->Socket, "401 Not Authenticated\n");
1336                 return ;
1337         }
1338
1339         // Check user permissions
1340         if( !(Bank_GetFlags(Client->UID) & (USER_FLAG_COKE|USER_FLAG_ADMIN))  ) {
1341                 sendf(Client->Socket, "403 Not in coke\n");
1342                 return ;
1343         }
1344         
1345         item = _GetItemFromString(itemname);
1346         if( !item ) {
1347                 // TODO: Create item?
1348                 sendf(Client->Socket, "406 Bad Item ID\n");
1349                 return ;
1350         }
1351         
1352         price = atoi(price_str);
1353         if( price <= 0 && price_str[0] != '0' ) {
1354                 sendf(Client->Socket, "407 Invalid price set\n");
1355         }
1356         
1357         switch( DispenseUpdateItem( Client->UID, item, description, price ) )
1358         {
1359         case 0:
1360                 // Return OK
1361                 sendf(Client->Socket, "200 Item updated\n");
1362                 break;
1363         default:
1364                 break;
1365         }
1366 }
1367
1368 // --- INTERNAL HELPERS ---
1369 void Debug(tClient *Client, const char *Format, ...)
1370 {
1371         va_list args;
1372         //printf("%010i [%i] ", (int)time(NULL), Client->ID);
1373         printf("[%i] ", Client->ID);
1374         va_start(args, Format);
1375         vprintf(Format, args);
1376         va_end(args);
1377         printf("\n");
1378 }
1379
1380 int sendf(int Socket, const char *Format, ...)
1381 {
1382         va_list args;
1383          int    len;
1384         
1385         va_start(args, Format);
1386         len = vsnprintf(NULL, 0, Format, args);
1387         va_end(args);
1388         
1389         {
1390                 char    buf[len+1];
1391                 va_start(args, Format);
1392                 vsnprintf(buf, len+1, Format, args);
1393                 va_end(args);
1394                 
1395                 #if DEBUG_TRACE_CLIENT
1396                 printf("sendf: %s", buf);
1397                 #endif
1398                 
1399                 return send(Socket, buf, len, 0);
1400         }
1401 }
1402
1403 // Takes a series of char *'s in
1404 /**
1405  * \brief Parse space-separated entries into 
1406  */
1407 int Server_int_ParseArgs(int bUseLongLast, char *ArgStr, ...)
1408 {
1409         va_list args;
1410         char    savedChar;
1411         char    **dest;
1412         va_start(args, ArgStr);
1413
1414         // Check for null
1415         if( !ArgStr )
1416         {
1417                 while( (dest = va_arg(args, char **)) )
1418                         *dest = NULL;
1419                 va_end(args);
1420                 return 1;
1421         }
1422
1423         savedChar = *ArgStr;
1424         
1425         while( (dest = va_arg(args, char **)) )
1426         {
1427                 // Trim leading spaces
1428                 while( *ArgStr == ' ' || *ArgStr == '\t' )
1429                         ArgStr ++;
1430                 
1431                 // ... oops, not enough arguments
1432                 if( *ArgStr == '\0' )
1433                 {
1434                         // NULL unset arguments
1435                         do {
1436                                 *dest = NULL;
1437                         }       while( (dest = va_arg(args, char **)) );
1438                 va_end(args);
1439                         return -1;
1440                 }
1441                 
1442                 if( *ArgStr == '"' )
1443                 {
1444                         ArgStr ++;
1445                         *dest = ArgStr;
1446                         // Read until quote
1447                         while( *ArgStr && *ArgStr != '"' )
1448                                 ArgStr ++;
1449                 }
1450                 else
1451                 {
1452                         // Set destination
1453                         *dest = ArgStr;
1454                         // Read until a space
1455                         while( *ArgStr && *ArgStr != ' ' && *ArgStr != '\t' )
1456                                 ArgStr ++;
1457                 }
1458                 savedChar = *ArgStr;    // savedChar is used to un-mangle the last string
1459                 *ArgStr = '\0';
1460                 ArgStr ++;
1461         }
1462         va_end(args);
1463         
1464         // Oops, extra arguments, and greedy not set
1465         if( (savedChar == ' ' || savedChar == '\t') && !bUseLongLast ) {
1466                 return -1;
1467         }
1468         
1469         // Un-mangle last
1470         if(bUseLongLast) {
1471                 ArgStr --;
1472                 *ArgStr = savedChar;
1473         }
1474         
1475         return 0;       // Success!
1476 }
1477
1478 int Server_int_ParseFlags(tClient *Client, const char *Str, int *Mask, int *Value)
1479 {
1480         struct {
1481                 const char      *Name;
1482                  int    Mask;
1483                  int    Value;
1484         }       cFLAGS[] = {
1485                  {"disabled", USER_FLAG_DISABLED, USER_FLAG_DISABLED}
1486                 ,{"door", USER_FLAG_DOORGROUP, USER_FLAG_DOORGROUP}
1487                 ,{"coke", USER_FLAG_COKE, USER_FLAG_COKE}
1488                 ,{"admin", USER_FLAG_ADMIN, USER_FLAG_ADMIN}
1489                 ,{"internal", USER_FLAG_INTERNAL, USER_FLAG_INTERNAL}
1490         };
1491         const int       ciNumFlags = sizeof(cFLAGS)/sizeof(cFLAGS[0]);
1492         
1493         char    *space;
1494         
1495         *Mask = 0;
1496         *Value = 0;
1497         
1498         do {
1499                  int    bRemove = 0;
1500                  int    i;
1501                  int    len;
1502                 
1503                 while( *Str == ' ' )    Str ++; // Eat whitespace
1504                 space = strchr(Str, ',');       // Find the end of the flag
1505                 if(space)
1506                         len = space - Str;
1507                 else
1508                         len = strlen(Str);
1509                 
1510                 // Check for inversion/removal
1511                 if( *Str == '!' || *Str == '-' ) {
1512                         bRemove = 1;
1513                         Str ++;
1514                 }
1515                 else if( *Str == '+' ) {
1516                         Str ++;
1517                 }
1518                 
1519                 // Check flag values
1520                 for( i = 0; i < ciNumFlags; i ++ )
1521                 {
1522                         if( strncmp(Str, cFLAGS[i].Name, len) == 0 ) {
1523                                 *Mask |= cFLAGS[i].Mask;
1524                                 *Value &= ~cFLAGS[i].Mask;
1525                                 if( !bRemove )
1526                                         *Value |= cFLAGS[i].Value;
1527                                 break;
1528                         }
1529                 }
1530                 
1531                 // Error check
1532                 if( i == ciNumFlags ) {
1533                         char    val[len+1];
1534                         strncpy(val, Str, len+1);
1535                         sendf(Client->Socket, "407 Unknown flag value '%s'\n", val);
1536                         return -1;
1537                 }
1538                 
1539                 Str = space + 1;
1540         } while(space);
1541         
1542         return 0;
1543 }

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