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

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