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

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