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

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