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

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