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

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