Door config settings (and some fixes to doorgroup reporting)
[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 <string.h>
18 #include <limits.h>
19 #include <stdarg.h>
20
21 #define DEBUG_TRACE_CLIENT      0
22
23 // Statistics
24 #define MAX_CONNECTION_QUEUE    5
25 #define INPUT_BUFFER_SIZE       256
26 #define CLIENT_TIMEOUT  10      // Seconds
27
28 #define HASH_TYPE       SHA1
29 #define HASH_LENGTH     20
30
31 #define MSG_STR_TOO_LONG        "499 Command too long (limit "EXPSTR(INPUT_BUFFER_SIZE)")\n"
32
33 // === TYPES ===
34 typedef struct sClient
35 {
36          int    Socket; // Client socket ID
37          int    ID;     // Client ID
38          
39          int    bIsTrusted;     // Is the connection from a trusted host/port
40         
41         char    *Username;
42         char    Salt[9];
43         
44          int    UID;
45          int    EffectiveUID;
46          int    bIsAuthed;
47 }       tClient;
48
49 // === PROTOTYPES ===
50 void    Server_Start(void);
51 void    Server_Cleanup(void);
52 void    Server_HandleClient(int Socket, int bTrusted);
53 void    Server_ParseClientCommand(tClient *Client, char *CommandString);
54 // --- Commands ---
55 void    Server_Cmd_USER(tClient *Client, char *Args);
56 void    Server_Cmd_PASS(tClient *Client, char *Args);
57 void    Server_Cmd_AUTOAUTH(tClient *Client, char *Args);
58 void    Server_Cmd_SETEUSER(tClient *Client, char *Args);
59 void    Server_Cmd_ENUMITEMS(tClient *Client, char *Args);
60 void    Server_Cmd_ITEMINFO(tClient *Client, char *Args);
61 void    Server_Cmd_DISPENSE(tClient *Client, char *Args);
62 void    Server_Cmd_GIVE(tClient *Client, char *Args);
63 void    Server_Cmd_DONATE(tClient *Client, char *Args);
64 void    Server_Cmd_ADD(tClient *Client, char *Args);
65 void    Server_Cmd_SET(tClient *Client, char *Args);
66 void    Server_Cmd_ENUMUSERS(tClient *Client, char *Args);
67 void    Server_Cmd_USERINFO(tClient *Client, char *Args);
68 void    _SendUserInfo(tClient *Client, int UserID);
69 void    Server_Cmd_USERADD(tClient *Client, char *Args);
70 void    Server_Cmd_USERFLAGS(tClient *Client, char *Args);
71 // --- Helpers ---
72 void    Debug(tClient *Client, const char *Format, ...);
73  int    Server_int_ParseFlags(tClient *Client, const char *Str, int *Mask, int *Value);
74  int    sendf(int Socket, const char *Format, ...);
75
76 // === CONSTANTS ===
77 // - Commands
78 const struct sClientCommand {
79         const char      *Name;
80         void    (*Function)(tClient *Client, char *Arguments);
81 }       gaServer_Commands[] = {
82         {"USER", Server_Cmd_USER},
83         {"PASS", Server_Cmd_PASS},
84         {"AUTOAUTH", Server_Cmd_AUTOAUTH},
85         {"SETEUSER", Server_Cmd_SETEUSER},
86         {"ENUM_ITEMS", Server_Cmd_ENUMITEMS},
87         {"ITEM_INFO", Server_Cmd_ITEMINFO},
88         {"DISPENSE", Server_Cmd_DISPENSE},
89         {"GIVE", Server_Cmd_GIVE},
90         {"DONATE", Server_Cmd_DONATE},
91         {"ADD", Server_Cmd_ADD},
92         {"SET", Server_Cmd_SET},
93         {"ENUM_USERS", Server_Cmd_ENUMUSERS},
94         {"USER_INFO", Server_Cmd_USERINFO},
95         {"USER_ADD", Server_Cmd_USERADD},
96         {"USER_FLAGS", Server_Cmd_USERFLAGS}
97 };
98 #define NUM_COMMANDS    ((int)(sizeof(gaServer_Commands)/sizeof(gaServer_Commands[0])))
99
100 // === GLOBALS ===
101  int    giServer_Port = 1020;
102  int    giServer_NextClientID = 1;
103  int    giServer_Socket;
104
105 // === CODE ===
106 /**
107  * \brief Open listenting socket and serve connections
108  */
109 void Server_Start(void)
110 {
111          int    client_socket;
112         struct sockaddr_in      server_addr, client_addr;
113
114         atexit(Server_Cleanup);
115
116         // Create Server
117         giServer_Socket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
118         if( giServer_Socket < 0 ) {
119                 fprintf(stderr, "ERROR: Unable to create server socket\n");
120                 return ;
121         }
122         
123         // Make listen address
124         memset(&server_addr, 0, sizeof(server_addr));
125         server_addr.sin_family = AF_INET;       // Internet Socket
126         server_addr.sin_addr.s_addr = htonl(INADDR_ANY);        // Listen on all interfaces
127         server_addr.sin_port = htons(giServer_Port);    // Port
128
129         // Bind
130         if( bind(giServer_Socket, (struct sockaddr *) &server_addr, sizeof(server_addr)) < 0 ) {
131                 fprintf(stderr, "ERROR: Unable to bind to 0.0.0.0:%i\n", giServer_Port);
132                 perror("Binding");
133                 return ;
134         }
135         
136         // Listen
137         if( listen(giServer_Socket, MAX_CONNECTION_QUEUE) < 0 ) {
138                 fprintf(stderr, "ERROR: Unable to listen to socket\n");
139                 perror("Listen");
140                 return ;
141         }
142         
143         printf("Listening on 0.0.0.0:%i\n", giServer_Port);
144         
145         for(;;)
146         {
147                 uint    len = sizeof(client_addr);
148                  int    bTrusted = 0;
149                 
150                 // Accept a connection
151                 client_socket = accept(giServer_Socket, (struct sockaddr *) &client_addr, &len);
152                 if(client_socket < 0) {
153                         fprintf(stderr, "ERROR: Unable to accept client connection\n");
154                         return ;
155                 }
156                 
157                 // Set a timeout on the user conneciton
158                 {
159                         struct timeval tv;
160                         tv.tv_sec = CLIENT_TIMEOUT;
161                         tv.tv_usec = 0;
162                         if( setsockopt(client_socket, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) )
163                         {
164                                 perror("setsockopt");
165                                 return ;
166                         }
167                 }
168                 
169                 // Debug: Print the connection string
170                 if(giDebugLevel >= 2) {
171                         char    ipstr[INET_ADDRSTRLEN];
172                         inet_ntop(AF_INET, &client_addr.sin_addr, ipstr, INET_ADDRSTRLEN);
173                         printf("Client connection from %s:%i\n",
174                                 ipstr, ntohs(client_addr.sin_port));
175                 }
176                 
177                 // Doesn't matter what, localhost is trusted
178                 if( ntohl( client_addr.sin_addr.s_addr ) == 0x7F000001 )
179                         bTrusted = 1;
180                 
181                 // Trusted Connections
182                 if( ntohs(client_addr.sin_port) < 1024 )
183                 {
184                         // TODO: Make this runtime configurable
185                         switch( ntohl( client_addr.sin_addr.s_addr ) )
186                         {
187                         case 0x7F000001:        // 127.0.0.1    localhost
188                         //case 0x825E0D00:      // 130.95.13.0
189                         case 0x825E0D07:        // 130.95.13.7  motsugo
190                         case 0x825E0D12:        // 130.95.13.18 mussel
191                         case 0x825E0D17:        // 130.95.13.23 martello
192                                 bTrusted = 1;
193                                 break;
194                         default:
195                                 break;
196                         }
197                 }
198                 
199                 // TODO: Multithread this?
200                 Server_HandleClient(client_socket, bTrusted);
201                 
202                 close(client_socket);
203         }
204 }
205
206 void Server_Cleanup(void)
207 {
208         printf("Close(%i)\n", giServer_Socket);
209         close(giServer_Socket);
210 }
211
212 /**
213  * \brief Reads from a client socket and parses the command strings
214  * \param Socket        Client socket number/handle
215  * \param bTrusted      Is the client trusted?
216  */
217 void Server_HandleClient(int Socket, int bTrusted)
218 {
219         char    inbuf[INPUT_BUFFER_SIZE];
220         char    *buf = inbuf;
221          int    remspace = INPUT_BUFFER_SIZE-1;
222          int    bytes = -1;
223         tClient clientInfo;
224         
225         memset(&clientInfo, 0, sizeof(clientInfo));
226         
227         // Initialise Client info
228         clientInfo.Socket = Socket;
229         clientInfo.ID = giServer_NextClientID ++;
230         clientInfo.bIsTrusted = bTrusted;
231         clientInfo.EffectiveUID = -1;
232         
233         // Read from client
234         /*
235          * Notes:
236          * - The `buf` and `remspace` variables allow a line to span several
237          *   calls to recv(), if a line is not completed in one recv() call
238          *   it is saved to the beginning of `inbuf` and `buf` is updated to
239          *   the end of it.
240          */
241         // TODO: Use select() instead (to give a timeout)
242         while( (bytes = recv(Socket, buf, remspace, 0)) > 0 )
243         {
244                 char    *eol, *start;
245                 buf[bytes] = '\0';      // Allow us to use stdlib string functions on it
246                 
247                 // Split by lines
248                 start = inbuf;
249                 while( (eol = strchr(start, '\n')) )
250                 {
251                         *eol = '\0';
252                         
253                         Server_ParseClientCommand(&clientInfo, start);
254                         
255                         start = eol + 1;
256                 }
257                 
258                 // Check if there was an incomplete line
259                 if( *start != '\0' ) {
260                          int    tailBytes = bytes - (start-buf);
261                         // Roll back in buffer
262                         memcpy(inbuf, start, tailBytes);
263                         remspace -= tailBytes;
264                         if(remspace == 0) {
265                                 send(Socket, MSG_STR_TOO_LONG, sizeof(MSG_STR_TOO_LONG), 0);
266                                 buf = inbuf;
267                                 remspace = INPUT_BUFFER_SIZE - 1;
268                         }
269                 }
270                 else {
271                         buf = inbuf;
272                         remspace = INPUT_BUFFER_SIZE - 1;
273                 }
274         }
275         
276         // Check for errors
277         if( bytes < 0 ) {
278                 fprintf(stderr, "ERROR: Unable to recieve from client on socket %i\n", Socket);
279                 return ;
280         }
281         
282         if(giDebugLevel >= 2) {
283                 printf("Client %i: Disconnected\n", clientInfo.ID);
284         }
285 }
286
287 /**
288  * \brief Parses a client command and calls the required helper function
289  * \param Client        Pointer to client state structure
290  * \param CommandString Command from client (single line of the command)
291  * \return Heap String to return to the client
292  */
293 void Server_ParseClientCommand(tClient *Client, char *CommandString)
294 {
295         char    *space, *args;
296          int    i;
297         #if 0
298         char    **argList;
299          int    numArgs = 0;
300         #endif
301         
302         // Split at first space
303         space = strchr(CommandString, ' ');
304         if(space == NULL) {
305                 args = NULL;
306         }
307         else {
308                 *space = '\0';
309                 args = space + 1;
310                 while( *space == ' ' )  space ++;
311                 
312                 #if 0
313                 // Count arguments
314                 numArgs = 1;
315                 for( i = 0; args[i]; )
316                 {
317                         while( CommandString[i] != ' ' ) {
318                                 if( CommandString[i] == '"' ) {
319                                         while( !(CommandString[i] != '\\' CommandString[i+1] == '"' ) )
320                                                 i ++;
321                                         i ++;
322                                 }
323                                 i ++;
324                         }
325                         numArgs ++;
326                         while( CommandString[i] == ' ' )        i ++;
327                 }
328                 #endif
329         }
330         
331         
332         // Find command
333         for( i = 0; i < NUM_COMMANDS; i++ )
334         {
335                 if(strcmp(CommandString, gaServer_Commands[i].Name) == 0) {
336                         gaServer_Commands[i].Function(Client, args);
337                         return ;
338                 }
339         }
340         
341         sendf(Client->Socket, "400 Unknown Command\n");
342 }
343
344 // ---
345 // Commands
346 // ---
347 /**
348  * \brief Set client username
349  * 
350  * Usage: USER <username>
351  */
352 void Server_Cmd_USER(tClient *Client, char *Args)
353 {
354         char    *space = strchr(Args, ' ');
355         if(space)       *space = '\0';  // Remove characters after the ' '
356         
357         // Debug!
358         if( giDebugLevel )
359                 Debug(Client, "Authenticating as '%s'", Args);
360         
361         // Save username
362         if(Client->Username)
363                 free(Client->Username);
364         Client->Username = strdup(Args);
365         
366         #if USE_SALT
367         // Create a salt (that changes if the username is changed)
368         // Yes, I know, I'm a little paranoid, but who isn't?
369         Client->Salt[0] = 0x21 + (rand()&0x3F);
370         Client->Salt[1] = 0x21 + (rand()&0x3F);
371         Client->Salt[2] = 0x21 + (rand()&0x3F);
372         Client->Salt[3] = 0x21 + (rand()&0x3F);
373         Client->Salt[4] = 0x21 + (rand()&0x3F);
374         Client->Salt[5] = 0x21 + (rand()&0x3F);
375         Client->Salt[6] = 0x21 + (rand()&0x3F);
376         Client->Salt[7] = 0x21 + (rand()&0x3F);
377         
378         // TODO: Also send hash type to use, (SHA1 or crypt according to [DAA])
379         sendf(Client->Socket, "100 SALT %s\n", Client->Salt);
380         #else
381         sendf(Client->Socket, "100 User Set\n");
382         #endif
383 }
384
385 /**
386  * \brief Authenticate as a user
387  * 
388  * Usage: PASS <hash>
389  */
390 void Server_Cmd_PASS(tClient *Client, char *Args)
391 {
392         char    *space = strchr(Args, ' ');
393         if(space)       *space = '\0';  // Remove characters after the ' '
394         
395         // Pass on to cokebank
396         Client->UID = Bank_GetUserAuth(Client->Salt, Client->Username, Args);
397
398         if( Client->UID != -1 ) {
399                 Client->bIsAuthed = 1;
400                 sendf(Client->Socket, "200 Auth OK\n");
401                 return ;
402         }
403         
404         sendf(Client->Socket, "401 Auth Failure\n");
405 }
406
407 /**
408  * \brief Authenticate as a user without a password
409  * 
410  * Usage: AUTOAUTH <user>
411  */
412 void Server_Cmd_AUTOAUTH(tClient *Client, char *Args)
413 {
414         char    *space = strchr(Args, ' ');
415         if(space)       *space = '\0';  // Remove characters after the ' '
416         
417         // Check if trusted
418         if( !Client->bIsTrusted ) {
419                 if(giDebugLevel)
420                         Debug(Client, "Untrusted client attempting to AUTOAUTH");
421                 sendf(Client->Socket, "401 Untrusted\n");
422                 return ;
423         }
424         
425         // Get UID
426         Client->UID = Bank_GetAcctByName( Args );       
427         if( Client->UID < 0 ) {
428                 if(giDebugLevel)
429                         Debug(Client, "Unknown user '%s'", Args);
430                 sendf(Client->Socket, "401 Auth Failure\n");
431                 return ;
432         }
433         
434         // You can't be an internal account
435         if( Bank_GetFlags(Client->UID) & USER_FLAG_INTERNAL ) {
436                 Client->UID = -1;
437                 sendf(Client->Socket, "401 Auth Failure\n");
438                 return ;
439         }
440
441         Client->bIsAuthed = 1;
442         
443         if(giDebugLevel)
444                 Debug(Client, "Auto authenticated as '%s' (%i)", Args, Client->UID);
445         
446         sendf(Client->Socket, "200 Auth OK\n");
447 }
448
449 /**
450  * \brief Set effective user
451  */
452 void Server_Cmd_SETEUSER(tClient *Client, char *Args)
453 {
454         char    *space;
455         
456         space = strchr(Args, ' ');
457         
458         if(space)       *space = '\0';
459         
460         if( !strlen(Args) ) {
461                 sendf(Client->Socket, "407 SETEUSER expects an argument\n");
462                 return ;
463         }
464
465         // Check user permissions
466         if( !(Bank_GetFlags(Client->UID) & (USER_FLAG_COKE|USER_FLAG_ADMIN)) ) {
467                 sendf(Client->Socket, "403 Not in coke\n");
468                 return ;
469         }
470         
471         // Set id
472         Client->EffectiveUID = Bank_GetAcctByName(Args);
473         if( Client->EffectiveUID == -1 ) {
474                 sendf(Client->Socket, "404 User not found\n");
475                 return ;
476         }
477         
478         // You can't be an internal account
479         if( Bank_GetFlags(Client->EffectiveUID) & USER_FLAG_INTERNAL ) {
480                 Client->EffectiveUID = -1;
481                 sendf(Client->Socket, "404 User not found\n");
482                 return ;
483         }
484         
485         sendf(Client->Socket, "200 User set\n");
486 }
487
488 /**
489  * \brief Enumerate the items that the server knows about
490  */
491 void Server_Cmd_ENUMITEMS(tClient *Client, char *Args)
492 {
493          int    i, count;
494
495         if( Args != NULL && strlen(Args) ) {
496                 sendf(Client->Socket, "407 ENUM_ITEMS takes no arguments\n");
497                 return ;
498         }
499         
500         // Count shown items
501         count = 0;
502         for( i = 0; i < giNumItems; i ++ ) {
503                 if( gaItems[i].bHidden )        continue;
504                 count ++;
505         }
506
507         sendf(Client->Socket, "201 Items %i\n", count);
508
509         for( i = 0; i < giNumItems; i ++ ) {
510                 if( gaItems[i].bHidden )        continue;
511                 sendf(Client->Socket,
512                         "202 Item %s:%i %i %s\n",
513                          gaItems[i].Handler->Name, gaItems[i].ID, gaItems[i].Price, gaItems[i].Name
514                          );
515         }
516
517         sendf(Client->Socket, "200 List end\n");
518 }
519
520 tItem *_GetItemFromString(char *String)
521 {
522         tHandler        *handler;
523         char    *type = String;
524         char    *colon = strchr(String, ':');
525          int    num, i;
526         
527         if( !colon ) {
528                 return NULL;
529         }
530
531         num = atoi(colon+1);
532         *colon = '\0';
533
534         // Find handler
535         handler = NULL;
536         for( i = 0; i < giNumHandlers; i ++ )
537         {
538                 if( strcmp(gaHandlers[i]->Name, type) == 0) {
539                         handler = gaHandlers[i];
540                         break;
541                 }
542         }
543         if( !handler ) {
544                 return NULL;
545         }
546
547         // Find item
548         for( i = 0; i < giNumItems; i ++ )
549         {
550                 if( gaItems[i].Handler != handler )     continue;
551                 if( gaItems[i].ID != num )      continue;
552                 return &gaItems[i];
553         }
554         return NULL;
555 }
556
557 /**
558  * \brief Fetch information on a specific item
559  */
560 void Server_Cmd_ITEMINFO(tClient *Client, char *Args)
561 {
562         tItem   *item = _GetItemFromString(Args);
563         
564         if( !item ) {
565                 sendf(Client->Socket, "406 Bad Item ID\n");
566                 return ;
567         }
568         
569         sendf(Client->Socket,
570                 "202 Item %s:%i %i %s\n",
571                  item->Handler->Name, item->ID, item->Price, item->Name
572                  );
573 }
574
575 void Server_Cmd_DISPENSE(tClient *Client, char *Args)
576 {
577         tItem   *item;
578          int    ret;
579          int    uid;
580          
581         if( !Client->bIsAuthed ) {
582                 sendf(Client->Socket, "401 Not Authenticated\n");
583                 return ;
584         }
585
586         item = _GetItemFromString(Args);
587         if( !item ) {
588                 sendf(Client->Socket, "406 Bad Item ID\n");
589                 return ;
590         }
591         
592         if( Client->EffectiveUID != -1 ) {
593                 uid = Client->EffectiveUID;
594         }
595         else {
596                 uid = Client->UID;
597         }
598
599         switch( ret = DispenseItem( Client->UID, uid, item ) )
600         {
601         case 0: sendf(Client->Socket, "200 Dispense OK\n");     return ;
602         case 1: sendf(Client->Socket, "501 Unable to dispense\n");      return ;
603         case 2: sendf(Client->Socket, "402 Poor You\n");        return ;
604         default:
605                 sendf(Client->Socket, "500 Dispense Error\n");
606                 return ;
607         }
608 }
609
610 void Server_Cmd_GIVE(tClient *Client, char *Args)
611 {
612         char    *recipient, *ammount, *reason;
613          int    uid, iAmmount;
614          int    thisUid;
615         
616         if( !Client->bIsAuthed ) {
617                 sendf(Client->Socket, "401 Not Authenticated\n");
618                 return ;
619         }
620
621         recipient = Args;
622
623         ammount = strchr(Args, ' ');
624         if( !ammount ) {
625                 sendf(Client->Socket, "407 Invalid Argument, expected 3 parameters, 1 encountered\n");
626                 return ;
627         }
628         *ammount = '\0';
629         ammount ++;
630
631         reason = strchr(ammount, ' ');
632         if( !reason ) {
633                 sendf(Client->Socket, "407 Invalid Argument, expected 3 parameters, 2 encountered\n");
634                 return ;
635         }
636         *reason = '\0';
637         reason ++;
638
639         // Get recipient
640         uid = Bank_GetAcctByName(recipient);
641         if( uid == -1 ) {
642                 sendf(Client->Socket, "404 Invalid target user\n");
643                 return ;
644         }
645         
646         // You can't alter an internal account
647         if( Bank_GetFlags(uid) & USER_FLAG_INTERNAL ) {
648                 sendf(Client->Socket, "404 Invalid target user\n");
649                 return ;
650         }
651
652         // Parse ammount
653         iAmmount = atoi(ammount);
654         if( iAmmount <= 0 ) {
655                 sendf(Client->Socket, "407 Invalid Argument, ammount must be > zero\n");
656                 return ;
657         }
658         
659         if( Client->EffectiveUID != -1 ) {
660                 thisUid = Client->EffectiveUID;
661         }
662         else {
663                 thisUid = Client->UID;
664         }
665
666         // Do give
667         switch( DispenseGive(Client->UID, thisUid, uid, iAmmount, reason) )
668         {
669         case 0:
670                 sendf(Client->Socket, "200 Give OK\n");
671                 return ;
672         case 2:
673                 sendf(Client->Socket, "402 Poor You\n");
674                 return ;
675         default:
676                 sendf(Client->Socket, "500 Unknown error\n");
677                 return ;
678         }
679 }
680
681 void Server_Cmd_DONATE(tClient *Client, char *Args)
682 {
683         char    *ammount, *reason;
684          int    iAmmount;
685          int    thisUid;
686         
687         if( !Client->bIsAuthed ) {
688                 sendf(Client->Socket, "401 Not Authenticated\n");
689                 return ;
690         }
691
692         ammount = Args;
693
694         // Get the start of the reason
695         reason = strchr(Args, ' ');
696         if( !ammount ) {
697                 sendf(Client->Socket, "407 Invalid Argument, expected 2 parameters, 1 encountered\n");
698                 return ;
699         }
700         *reason = '\0';
701         reason ++;
702         
703         // Check the end of the reason
704         if( strchr(reason, ' ') ) {
705                 sendf(Client->Socket, "407 Invalid Argument, expected 2 parameters, more encountered\n");
706                 return ;
707         }
708
709         // Parse ammount
710         iAmmount = atoi(ammount);
711         if( iAmmount <= 0 ) {
712                 sendf(Client->Socket, "407 Invalid Argument, ammount must be > zero\n");
713                 return ;
714         }
715         
716         // Handle effective users
717         if( Client->EffectiveUID != -1 ) {
718                 thisUid = Client->EffectiveUID;
719         }
720         else {
721                 thisUid = Client->UID;
722         }
723
724         // Do give
725         switch( DispenseDonate(Client->UID, thisUid, iAmmount, reason) )
726         {
727         case 0:
728                 sendf(Client->Socket, "200 Give OK\n");
729                 return ;
730         case 2:
731                 sendf(Client->Socket, "402 Poor You\n");
732                 return ;
733         default:
734                 sendf(Client->Socket, "500 Unknown error\n");
735                 return ;
736         }
737 }
738
739 void Server_Cmd_ADD(tClient *Client, char *Args)
740 {
741         char    *user, *ammount, *reason;
742          int    uid, iAmmount;
743         
744         if( !Client->bIsAuthed ) {
745                 sendf(Client->Socket, "401 Not Authenticated\n");
746                 return ;
747         }
748
749         user = Args;
750
751         ammount = strchr(Args, ' ');
752         if( !ammount ) {
753                 sendf(Client->Socket, "407 Invalid Argument, expected 3 parameters, 1 encountered\n");
754                 return ;
755         }
756         *ammount = '\0';
757         ammount ++;
758
759         reason = strchr(ammount, ' ');
760         if( !reason ) {
761                 sendf(Client->Socket, "407 Invalid Argument, expected 3 parameters, 2 encountered\n");
762                 return ;
763         }
764         *reason = '\0';
765         reason ++;
766
767         // Check user permissions
768         if( !(Bank_GetFlags(Client->UID) & (USER_FLAG_COKE|USER_FLAG_ADMIN))  ) {
769                 sendf(Client->Socket, "403 Not in coke\n");
770                 return ;
771         }
772
773         // Get recipient
774         uid = Bank_GetAcctByName(user);
775         if( uid == -1 ) {
776                 sendf(Client->Socket, "404 Invalid user\n");
777                 return ;
778         }
779         
780         // You can't alter an internal account
781         if( Bank_GetFlags(uid) & USER_FLAG_INTERNAL ) {
782                 sendf(Client->Socket, "404 Invalid user\n");
783                 return ;
784         }
785
786         // Parse ammount
787         iAmmount = atoi(ammount);
788         if( iAmmount == 0 && ammount[0] != '0' ) {
789                 sendf(Client->Socket, "407 Invalid Argument\n");
790                 return ;
791         }
792
793         // Do give
794         switch( DispenseAdd(Client->UID, uid, iAmmount, reason) )
795         {
796         case 0:
797                 sendf(Client->Socket, "200 Add OK\n");
798                 return ;
799         case 2:
800                 sendf(Client->Socket, "402 Poor Guy\n");
801                 return ;
802         default:
803                 sendf(Client->Socket, "500 Unknown error\n");
804                 return ;
805         }
806 }
807
808 void Server_Cmd_SET(tClient *Client, char *Args)
809 {
810         char    *user, *ammount, *reason;
811          int    uid, iAmmount;
812         
813         if( !Client->bIsAuthed ) {
814                 sendf(Client->Socket, "401 Not Authenticated\n");
815                 return ;
816         }
817
818         user = Args;
819
820         ammount = strchr(Args, ' ');
821         if( !ammount ) {
822                 sendf(Client->Socket, "407 Invalid Argument, expected 3 parameters, 1 encountered\n");
823                 return ;
824         }
825         *ammount = '\0';
826         ammount ++;
827
828         reason = strchr(ammount, ' ');
829         if( !reason ) {
830                 sendf(Client->Socket, "407 Invalid Argument, expected 3 parameters, 2 encountered\n");
831                 return ;
832         }
833         *reason = '\0';
834         reason ++;
835
836         // Check user permissions
837         if( !(Bank_GetFlags(Client->UID) & USER_FLAG_ADMIN)  ) {
838                 sendf(Client->Socket, "403 Not an admin\n");
839                 return ;
840         }
841
842         // Get recipient
843         uid = Bank_GetAcctByName(user);
844         if( uid == -1 ) {
845                 sendf(Client->Socket, "404 Invalid user\n");
846                 return ;
847         }
848         
849         // You can't alter an internal account
850         if( Bank_GetFlags(uid) & USER_FLAG_INTERNAL ) {
851                 sendf(Client->Socket, "404 Invalid user\n");
852                 return ;
853         }
854
855         // Parse ammount
856         iAmmount = atoi(ammount);
857         if( iAmmount == 0 && ammount[0] != '0' ) {
858                 sendf(Client->Socket, "407 Invalid Argument\n");
859                 return ;
860         }
861
862         // Do give
863         switch( DispenseSet(Client->UID, uid, iAmmount, reason) )
864         {
865         case 0:
866                 sendf(Client->Socket, "200 Add OK\n");
867                 return ;
868         case 2:
869                 sendf(Client->Socket, "402 Poor Guy\n");
870                 return ;
871         default:
872                 sendf(Client->Socket, "500 Unknown error\n");
873                 return ;
874         }
875 }
876
877 void Server_Cmd_ENUMUSERS(tClient *Client, char *Args)
878 {
879          int    i, numRet = 0;
880         tAcctIterator   *it;
881          int    maxBal = INT_MAX, minBal = INT_MIN;
882          int    flagMask = 0, flagVal = 0;
883          int    sort = BANK_ITFLAG_SORT_NAME;
884         time_t  lastSeenAfter=0, lastSeenBefore=0;
885         
886          int    flags;  // Iterator flags
887          int    balValue;       // Balance value for iterator
888         time_t  timeValue;      // Time value for iterator
889         
890         // Parse arguments
891         if( Args && strlen(Args) )
892         {
893                 char    *space = Args, *type, *val;
894                 do
895                 {
896                         type = space;
897                         // Get next space
898                         space = strchr(space, ' ');
899                         if(space)       *space = '\0';
900                         
901                         // Get type
902                         val = strchr(type, ':');
903                         if( val ) {
904                                 *val = '\0';
905                                 val ++;
906                                 
907                                 // Types
908                                 // - Minium Balance
909                                 if( strcmp(type, "min_balance") == 0 ) {
910                                         minBal = atoi(val);
911                                 }
912                                 // - Maximum Balance
913                                 else if( strcmp(type, "max_balance") == 0 ) {
914                                         maxBal = atoi(val);
915                                 }
916                                 // - Flags
917                                 else if( strcmp(type, "flags") == 0 ) {
918                                         if( Server_int_ParseFlags(Client, val, &flagMask, &flagVal) )
919                                                 return ;
920                                 }
921                                 // - Last seen before timestamp
922                                 else if( strcmp(type, "last_seen_before") == 0 ) {
923                                         lastSeenAfter = atoll(val);
924                                 }
925                                 // - Last seen after timestamp
926                                 else if( strcmp(type, "last_seen_after") == 0 ) {
927                                         lastSeenAfter = atoll(val);
928                                 }
929                                 // - Sorting 
930                                 else if( strcmp(type, "sort") == 0 ) {
931                                         char    *dash = strchr(val, '-');
932                                         if( dash ) {
933                                                 *dash = '\0';
934                                                 dash ++;
935                                         }
936                                         if( strcmp(val, "name") == 0 ) {
937                                                 sort = BANK_ITFLAG_SORT_NAME;
938                                         }
939                                         else if( strcmp(val, "balance") == 0 ) {
940                                                 sort = BANK_ITFLAG_SORT_BAL;
941                                         }
942                                         else if( strcmp(val, "lastseen") == 0 ) {
943                                                 sort = BANK_ITFLAG_SORT_LASTSEEN;
944                                         }
945                                         else {
946                                                 sendf(Client->Socket, "407 Unknown sort field ('%s')\n", val);
947                                                 return ;
948                                         }
949                                         // Handle sort direction
950                                         if( dash ) {
951                                                 if( strcmp(dash, "desc") == 0 ) {
952                                                         sort |= BANK_ITFLAG_REVSORT;
953                                                 }
954                                                 else {
955                                                         sendf(Client->Socket, "407 Unknown sort direction '%s'\n", dash);
956                                                         return ;
957                                                 }
958                                                 dash[-1] = '-';
959                                         }
960                                 }
961                                 else {
962                                         sendf(Client->Socket, "407 Unknown argument to ENUM_USERS '%s:%s'\n", type, val);
963                                         return ;
964                                 }
965                                 
966                                 val[-1] = ':';
967                         }
968                         else {
969                                 sendf(Client->Socket, "407 Unknown argument to ENUM_USERS '%s'\n", type);
970                                 return ;
971                         }
972                         
973                         // Eat whitespace
974                         if( space ) {
975                                 *space = ' ';   // Repair (to be nice)
976                                 space ++;
977                                 while(*space == ' ')    space ++;
978                         }
979                 }       while(space);
980         }
981         
982         // Create iterator
983         if( maxBal != INT_MAX ) {
984                 flags = sort|BANK_ITFLAG_MAXBALANCE;
985                 balValue = maxBal;
986         }
987         else if( minBal != INT_MIN ) {
988                 flags = sort|BANK_ITFLAG_MINBALANCE;
989                 balValue = minBal;
990         }
991         else {
992                 flags = sort;
993                 balValue = 0;
994         }
995         if( lastSeenBefore ) {
996                 timeValue = lastSeenBefore;
997                 flags |= BANK_ITFLAG_SEENBEFORE;
998         }
999         else if( lastSeenAfter ) {
1000                 timeValue = lastSeenAfter;
1001                 flags |= BANK_ITFLAG_SEENAFTER;
1002         }
1003         else {
1004                 timeValue = 0;
1005         }
1006         it = Bank_Iterator(flagMask, flagVal, flags, balValue, timeValue);
1007         
1008         // Get return number
1009         while( (i = Bank_IteratorNext(it)) != -1 )
1010         {
1011                 int bal = Bank_GetBalance(i);
1012                 
1013                 if( bal == INT_MIN )    continue;
1014                 
1015                 if( bal < minBal )      continue;
1016                 if( bal > maxBal )      continue;
1017                 
1018                 numRet ++;
1019         }
1020         
1021         Bank_DelIterator(it);
1022         
1023         // Send count
1024         sendf(Client->Socket, "201 Users %i\n", numRet);
1025         
1026         
1027         // Create iterator
1028         it = Bank_Iterator(flagMask, flagVal, flags, balValue, timeValue);
1029         
1030         while( (i = Bank_IteratorNext(it)) != -1 )
1031         {
1032                 int bal = Bank_GetBalance(i);
1033                 
1034                 if( bal == INT_MIN )    continue;
1035                 
1036                 if( bal < minBal )      continue;
1037                 if( bal > maxBal )      continue;
1038                 
1039                 _SendUserInfo(Client, i);
1040         }
1041         
1042         Bank_DelIterator(it);
1043         
1044         sendf(Client->Socket, "200 List End\n");
1045 }
1046
1047 void Server_Cmd_USERINFO(tClient *Client, char *Args)
1048 {
1049          int    uid;
1050         char    *user = Args;
1051         char    *space;
1052         
1053         space = strchr(user, ' ');
1054         if(space)       *space = '\0';
1055         
1056         if( giDebugLevel )      Debug(Client, "User Info '%s'", user);
1057         
1058         // Get recipient
1059         uid = Bank_GetAcctByName(user);
1060         
1061         if( giDebugLevel >= 2 ) Debug(Client, "uid = %i", uid);
1062         if( uid == -1 ) {
1063                 sendf(Client->Socket, "404 Invalid user\n");
1064                 return ;
1065         }
1066         
1067         _SendUserInfo(Client, uid);
1068 }
1069
1070 void _SendUserInfo(tClient *Client, int UserID)
1071 {
1072         char    *type, *disabled="", *door="";
1073          int    flags = Bank_GetFlags(UserID);
1074         
1075         if( flags & USER_FLAG_INTERNAL ) {
1076                 type = "internal";
1077         }
1078         else if( flags & USER_FLAG_COKE ) {
1079                 if( flags & USER_FLAG_ADMIN )
1080                         type = "coke,admin";
1081                 else
1082                         type = "coke";
1083         }
1084         else if( flags & USER_FLAG_ADMIN ) {
1085                 type = "admin";
1086         }
1087         else {
1088                 type = "user";
1089         }
1090         
1091         if( flags & USER_FLAG_DISABLED )
1092                 disabled = ",disabled";
1093         if( flags & USER_FLAG_DOORGROUP )
1094                 door = ",door";
1095         
1096         // TODO: User flags/type
1097         sendf(
1098                 Client->Socket, "202 User %s %i %s%s%s\n",
1099                 Bank_GetAcctName(UserID), Bank_GetBalance(UserID),
1100                 type, disabled, door
1101                 );
1102 }
1103
1104 void Server_Cmd_USERADD(tClient *Client, char *Args)
1105 {
1106         char    *username, *space;
1107         
1108         // Check permissions
1109         if( !(Bank_GetFlags(Client->UID) & USER_FLAG_ADMIN) ) {
1110                 sendf(Client->Socket, "403 Not a coke admin\n");
1111                 return ;
1112         }
1113         
1114         // Read arguments
1115         username = Args;
1116         while( *username == ' ' )       username ++;
1117         space = strchr(username, ' ');
1118         if(space)       *space = '\0';
1119         
1120         // Try to create user
1121         if( Bank_CreateAcct(username) == -1 ) {
1122                 sendf(Client->Socket, "404 User exists\n");
1123                 return ;
1124         }
1125         
1126         {
1127                 char    *thisName = Bank_GetAcctName(Client->UID);
1128                 Log_Info("Account '%s' created by '%s'", username, thisName);
1129                 free(thisName);
1130         }
1131         
1132         sendf(Client->Socket, "200 User Added\n");
1133 }
1134
1135 void Server_Cmd_USERFLAGS(tClient *Client, char *Args)
1136 {
1137         char    *username, *flags;
1138         char    *space;
1139          int    mask=0, value=0;
1140          int    uid;
1141         
1142         // Check permissions
1143         if( !(Bank_GetFlags(Client->UID) & USER_FLAG_ADMIN) ) {
1144                 sendf(Client->Socket, "403 Not a coke admin\n");
1145                 return ;
1146         }
1147         
1148         // Read arguments
1149         // - Username
1150         username = Args;
1151         while( *username == ' ' )       username ++;
1152         space = strchr(username, ' ');
1153         if(!space) {
1154                 sendf(Client->Socket, "407 USER_FLAGS requires 2 arguments, 1 given\n");
1155                 return ;
1156         }
1157         *space = '\0';
1158         // - Flags
1159         flags = space + 1;
1160         while( *flags == ' ' )  flags ++;
1161         space = strchr(flags, ' ');
1162         if(space)       *space = '\0';
1163         
1164         // Get UID
1165         uid = Bank_GetAcctByName(username);
1166         if( uid == -1 ) {
1167                 sendf(Client->Socket, "404 User '%s' not found\n", username);
1168                 return ;
1169         }
1170         
1171         // Parse flags
1172         if( Server_int_ParseFlags(Client, flags, &mask, &value) )
1173                 return ;
1174         
1175         if( giDebugLevel )
1176                 Debug(Client, "Set %i(%s) flags to %x (masked %x)\n",
1177                         uid, username, mask, value);
1178         
1179         // Apply flags
1180         Bank_SetFlags(uid, mask, value);
1181         
1182         // Return OK
1183         sendf(Client->Socket, "200 User Updated\n");
1184 }
1185
1186 // --- INTERNAL HELPERS ---
1187 void Debug(tClient *Client, const char *Format, ...)
1188 {
1189         va_list args;
1190         //printf("%010i [%i] ", (int)time(NULL), Client->ID);
1191         printf("[%i] ", Client->ID);
1192         va_start(args, Format);
1193         vprintf(Format, args);
1194         va_end(args);
1195         printf("\n");
1196 }
1197
1198 int sendf(int Socket, const char *Format, ...)
1199 {
1200         va_list args;
1201          int    len;
1202         
1203         va_start(args, Format);
1204         len = vsnprintf(NULL, 0, Format, args);
1205         va_end(args);
1206         
1207         {
1208                 char    buf[len+1];
1209                 va_start(args, Format);
1210                 vsnprintf(buf, len+1, Format, args);
1211                 va_end(args);
1212                 
1213                 #if DEBUG_TRACE_CLIENT
1214                 printf("sendf: %s", buf);
1215                 #endif
1216                 
1217                 return send(Socket, buf, len, 0);
1218         }
1219 }
1220
1221 int Server_int_ParseFlags(tClient *Client, const char *Str, int *Mask, int *Value)
1222 {
1223         struct {
1224                 const char      *Name;
1225                  int    Mask;
1226                  int    Value;
1227         }       cFLAGS[] = {
1228                  {"disabled", USER_FLAG_DISABLED, USER_FLAG_DISABLED}
1229                 ,{"door", USER_FLAG_DOORGROUP, USER_FLAG_DOORGROUP}
1230                 ,{"coke", USER_FLAG_COKE, USER_FLAG_COKE}
1231                 ,{"admin", USER_FLAG_ADMIN, USER_FLAG_ADMIN}
1232                 ,{"internal", USER_FLAG_INTERNAL, USER_FLAG_INTERNAL}
1233         };
1234         const int       ciNumFlags = sizeof(cFLAGS)/sizeof(cFLAGS[0]);
1235         
1236         char    *space;
1237         
1238         *Mask = 0;
1239         *Value = 0;
1240         
1241         do {
1242                  int    bRemove = 0;
1243                  int    i;
1244                  int    len;
1245                 
1246                 while( *Str == ' ' )    Str ++; // Eat whitespace
1247                 space = strchr(Str, ',');       // Find the end of the flag
1248                 if(space)
1249                         len = space - Str;
1250                 else
1251                         len = strlen(Str);
1252                 
1253                 // Check for inversion/removal
1254                 if( *Str == '!' || *Str == '-' ) {
1255                         bRemove = 1;
1256                         Str ++;
1257                 }
1258                 else if( *Str == '+' ) {
1259                         Str ++;
1260                 }
1261                 
1262                 // Check flag values
1263                 for( i = 0; i < ciNumFlags; i ++ )
1264                 {
1265                         if( strncmp(Str, cFLAGS[i].Name, len) == 0 ) {
1266                                 *Mask |= cFLAGS[i].Mask;
1267                                 *Value &= ~cFLAGS[i].Mask;
1268                                 if( !bRemove )
1269                                         *Value |= cFLAGS[i].Value;
1270                                 break;
1271                         }
1272                 }
1273                 
1274                 // Error check
1275                 if( i == ciNumFlags ) {
1276                         char    val[len+1];
1277                         strncpy(val, Str, len+1);
1278                         sendf(Client->Socket, "407 Unknown flag value '%s'\n", val);
1279                         return -1;
1280                 }
1281                 
1282                 Str = space + 1;
1283         } while(space);
1284         
1285         return 0;
1286 }

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