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

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