Big cleanups, reworked CokeBank API
[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
27 #define HASH_TYPE       SHA1
28 #define HASH_LENGTH     20
29
30 #define MSG_STR_TOO_LONG        "499 Command too long (limit "EXPSTR(INPUT_BUFFER_SIZE)")\n"
31
32 // === TYPES ===
33 typedef struct sClient
34 {
35          int    Socket; // Client socket ID
36          int    ID;     // Client ID
37          
38          int    bIsTrusted;     // Is the connection from a trusted host/port
39         
40         char    *Username;
41         char    Salt[9];
42         
43          int    UID;
44          int    EffectiveUID;
45          int    bIsAuthed;
46 }       tClient;
47
48 // === PROTOTYPES ===
49 void    Server_Start(void);
50 void    Server_Cleanup(void);
51 void    Server_HandleClient(int Socket, int bTrusted);
52 void    Server_ParseClientCommand(tClient *Client, char *CommandString);
53 // --- Commands ---
54 void    Server_Cmd_USER(tClient *Client, char *Args);
55 void    Server_Cmd_PASS(tClient *Client, char *Args);
56 void    Server_Cmd_AUTOAUTH(tClient *Client, char *Args);
57 void    Server_Cmd_SETEUSER(tClient *Client, char *Args);
58 void    Server_Cmd_ENUMITEMS(tClient *Client, char *Args);
59 void    Server_Cmd_ITEMINFO(tClient *Client, char *Args);
60 void    Server_Cmd_DISPENSE(tClient *Client, char *Args);
61 void    Server_Cmd_GIVE(tClient *Client, char *Args);
62 void    Server_Cmd_ADD(tClient *Client, char *Args);
63 void    Server_Cmd_ENUMUSERS(tClient *Client, char *Args);
64 void    Server_Cmd_USERINFO(tClient *Client, char *Args);
65 void    _SendUserInfo(tClient *Client, int UserID);
66 void    Server_Cmd_USERADD(tClient *Client, char *Args);
67 void    Server_Cmd_USERFLAGS(tClient *Client, char *Args);
68 // --- Helpers ---
69  int    sendf(int Socket, const char *Format, ...);
70
71 // === CONSTANTS ===
72 // - Commands
73 const struct sClientCommand {
74         const char      *Name;
75         void    (*Function)(tClient *Client, char *Arguments);
76 }       gaServer_Commands[] = {
77         {"USER", Server_Cmd_USER},
78         {"PASS", Server_Cmd_PASS},
79         {"AUTOAUTH", Server_Cmd_AUTOAUTH},
80         {"SETEUSER", Server_Cmd_SETEUSER},
81         {"ENUM_ITEMS", Server_Cmd_ENUMITEMS},
82         {"ITEM_INFO", Server_Cmd_ITEMINFO},
83         {"DISPENSE", Server_Cmd_DISPENSE},
84         {"GIVE", Server_Cmd_GIVE},
85         {"ADD", Server_Cmd_ADD},
86         {"ENUM_USERS", Server_Cmd_ENUMUSERS},
87         {"USER_INFO", Server_Cmd_USERINFO},
88         {"USER_ADD", Server_Cmd_USERADD},
89         {"USER_FLAGS", Server_Cmd_USERFLAGS}
90 };
91 #define NUM_COMMANDS    ((int)(sizeof(gaServer_Commands)/sizeof(gaServer_Commands[0])))
92
93 // === GLOBALS ===
94  int    giServer_Port = 1020;
95  int    giServer_NextClientID = 1;
96  int    giServer_Socket;
97
98 // === CODE ===
99 /**
100  * \brief Open listenting socket and serve connections
101  */
102 void Server_Start(void)
103 {
104          int    client_socket;
105         struct sockaddr_in      server_addr, client_addr;
106
107         atexit(Server_Cleanup);
108
109         // Create Server
110         giServer_Socket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
111         if( giServer_Socket < 0 ) {
112                 fprintf(stderr, "ERROR: Unable to create server socket\n");
113                 return ;
114         }
115         
116         // Make listen address
117         memset(&server_addr, 0, sizeof(server_addr));
118         server_addr.sin_family = AF_INET;       // Internet Socket
119         server_addr.sin_addr.s_addr = htonl(INADDR_ANY);        // Listen on all interfaces
120         server_addr.sin_port = htons(giServer_Port);    // Port
121
122         // Bind
123         if( bind(giServer_Socket, (struct sockaddr *) &server_addr, sizeof(server_addr)) < 0 ) {
124                 fprintf(stderr, "ERROR: Unable to bind to 0.0.0.0:%i\n", giServer_Port);
125                 perror("Binding");
126                 return ;
127         }
128         
129         // Listen
130         if( listen(giServer_Socket, MAX_CONNECTION_QUEUE) < 0 ) {
131                 fprintf(stderr, "ERROR: Unable to listen to socket\n");
132                 perror("Listen");
133                 return ;
134         }
135         
136         printf("Listening on 0.0.0.0:%i\n", giServer_Port);
137         
138         for(;;)
139         {
140                 uint    len = sizeof(client_addr);
141                  int    bTrusted = 0;
142                 
143                 client_socket = accept(giServer_Socket, (struct sockaddr *) &client_addr, &len);
144                 if(client_socket < 0) {
145                         fprintf(stderr, "ERROR: Unable to accept client connection\n");
146                         return ;
147                 }
148                 
149                 if(giDebugLevel >= 2) {
150                         char    ipstr[INET_ADDRSTRLEN];
151                         inet_ntop(AF_INET, &client_addr.sin_addr, ipstr, INET_ADDRSTRLEN);
152                         printf("Client connection from %s:%i\n",
153                                 ipstr, ntohs(client_addr.sin_port));
154                 }
155                 
156                 // Trusted Connections
157                 if( ntohs(client_addr.sin_port) < 1024 )
158                 {
159                         // TODO: Make this runtime configurable
160                         switch( ntohl( client_addr.sin_addr.s_addr ) )
161                         {
162                         case 0x7F000001:        // 127.0.0.1    localhost
163                         //case 0x825E0D00:      // 130.95.13.0
164                         case 0x825E0D12:        // 130.95.13.18 mussel
165                         case 0x825E0D17:        // 130.95.13.23 martello
166                                 bTrusted = 1;
167                                 break;
168                         default:
169                                 break;
170                         }
171                 }
172                 
173                 // TODO: Multithread this?
174                 Server_HandleClient(client_socket, bTrusted);
175                 
176                 close(client_socket);
177         }
178 }
179
180 void Server_Cleanup(void)
181 {
182         printf("Close(%i)\n", giServer_Socket);
183         close(giServer_Socket);
184 }
185
186 /**
187  * \brief Reads from a client socket and parses the command strings
188  * \param Socket        Client socket number/handle
189  * \param bTrusted      Is the client trusted?
190  */
191 void Server_HandleClient(int Socket, int bTrusted)
192 {
193         char    inbuf[INPUT_BUFFER_SIZE];
194         char    *buf = inbuf;
195          int    remspace = INPUT_BUFFER_SIZE-1;
196          int    bytes = -1;
197         tClient clientInfo;
198         
199         memset(&clientInfo, 0, sizeof(clientInfo));
200         
201         // Initialise Client info
202         clientInfo.Socket = Socket;
203         clientInfo.ID = giServer_NextClientID ++;
204         clientInfo.bIsTrusted = bTrusted;
205         
206         // Read from client
207         /*
208          * Notes:
209          * - The `buf` and `remspace` variables allow a line to span several
210          *   calls to recv(), if a line is not completed in one recv() call
211          *   it is saved to the beginning of `inbuf` and `buf` is updated to
212          *   the end of it.
213          */
214         while( (bytes = recv(Socket, buf, remspace, 0)) > 0 )
215         {
216                 char    *eol, *start;
217                 buf[bytes] = '\0';      // Allow us to use stdlib string functions on it
218                 
219                 // Split by lines
220                 start = inbuf;
221                 while( (eol = strchr(start, '\n')) )
222                 {
223                         *eol = '\0';
224                         
225                         Server_ParseClientCommand(&clientInfo, start);
226                         
227                         start = eol + 1;
228                 }
229                 
230                 // Check if there was an incomplete line
231                 if( *start != '\0' ) {
232                          int    tailBytes = bytes - (start-buf);
233                         // Roll back in buffer
234                         memcpy(inbuf, start, tailBytes);
235                         remspace -= tailBytes;
236                         if(remspace == 0) {
237                                 send(Socket, MSG_STR_TOO_LONG, sizeof(MSG_STR_TOO_LONG), 0);
238                                 buf = inbuf;
239                                 remspace = INPUT_BUFFER_SIZE - 1;
240                         }
241                 }
242                 else {
243                         buf = inbuf;
244                         remspace = INPUT_BUFFER_SIZE - 1;
245                 }
246         }
247         
248         // Check for errors
249         if( bytes < 0 ) {
250                 fprintf(stderr, "ERROR: Unable to recieve from client on socket %i\n", Socket);
251                 return ;
252         }
253         
254         if(giDebugLevel >= 2) {
255                 printf("Client %i: Disconnected\n", clientInfo.ID);
256         }
257 }
258
259 /**
260  * \brief Parses a client command and calls the required helper function
261  * \param Client        Pointer to client state structure
262  * \param CommandString Command from client (single line of the command)
263  * \return Heap String to return to the client
264  */
265 void Server_ParseClientCommand(tClient *Client, char *CommandString)
266 {
267         char    *space, *args;
268          int    i;
269         #if 0
270         char    **argList;
271          int    numArgs = 0;
272         #endif
273         
274         // Split at first space
275         space = strchr(CommandString, ' ');
276         if(space == NULL) {
277                 args = NULL;
278         }
279         else {
280                 *space = '\0';
281                 args = space + 1;
282                 while( *space == ' ' )  space ++;
283                 
284                 #if 0
285                 // Count arguments
286                 numArgs = 1;
287                 for( i = 0; args[i]; )
288                 {
289                         while( CommandString[i] != ' ' ) {
290                                 if( CommandString[i] == '"' ) {
291                                         while( !(CommandString[i] != '\\' CommandString[i+1] == '"' ) )
292                                                 i ++;
293                                         i ++;
294                                 }
295                                 i ++;
296                         }
297                         numArgs ++;
298                         while( CommandString[i] == ' ' )        i ++;
299                 }
300                 #endif
301         }
302         
303         
304         // Find command
305         for( i = 0; i < NUM_COMMANDS; i++ )
306         {
307                 if(strcmp(CommandString, gaServer_Commands[i].Name) == 0) {
308                         gaServer_Commands[i].Function(Client, args);
309                         return ;
310                 }
311         }
312         
313         sendf(Client->Socket, "400 Unknown Command\n");
314 }
315
316 // ---
317 // Commands
318 // ---
319 /**
320  * \brief Set client username
321  * 
322  * Usage: USER <username>
323  */
324 void Server_Cmd_USER(tClient *Client, char *Args)
325 {
326         char    *space = strchr(Args, ' ');
327         if(space)       *space = '\0';  // Remove characters after the ' '
328         
329         // Debug!
330         if( giDebugLevel )
331                 printf("Client %i authenticating as '%s'\n", Client->ID, Args);
332         
333         // Save username
334         if(Client->Username)
335                 free(Client->Username);
336         Client->Username = strdup(Args);
337         
338         #if USE_SALT
339         // Create a salt (that changes if the username is changed)
340         // Yes, I know, I'm a little paranoid, but who isn't?
341         Client->Salt[0] = 0x21 + (rand()&0x3F);
342         Client->Salt[1] = 0x21 + (rand()&0x3F);
343         Client->Salt[2] = 0x21 + (rand()&0x3F);
344         Client->Salt[3] = 0x21 + (rand()&0x3F);
345         Client->Salt[4] = 0x21 + (rand()&0x3F);
346         Client->Salt[5] = 0x21 + (rand()&0x3F);
347         Client->Salt[6] = 0x21 + (rand()&0x3F);
348         Client->Salt[7] = 0x21 + (rand()&0x3F);
349         
350         // TODO: Also send hash type to use, (SHA1 or crypt according to [DAA])
351         sendf(Client->Socket, "100 SALT %s\n", Client->Salt);
352         #else
353         sendf(Client->Socket, "100 User Set\n");
354         #endif
355 }
356
357 /**
358  * \brief Authenticate as a user
359  * 
360  * Usage: PASS <hash>
361  */
362 void Server_Cmd_PASS(tClient *Client, char *Args)
363 {
364         char    *space = strchr(Args, ' ');
365         if(space)       *space = '\0';  // Remove characters after the ' '
366         
367         // Pass on to cokebank
368         Client->UID = Bank_GetUserAuth(Client->Salt, Client->Username, Args);
369
370         if( Client->UID != -1 ) {
371                 Client->bIsAuthed = 1;
372                 sendf(Client->Socket, "200 Auth OK\n");
373                 return ;
374         }
375         
376         sendf(Client->Socket, "401 Auth Failure\n");
377 }
378
379 /**
380  * \brief Authenticate as a user without a password
381  * 
382  * Usage: AUTOAUTH <user>
383  */
384 void Server_Cmd_AUTOAUTH(tClient *Client, char *Args)
385 {
386         char    *space = strchr(Args, ' ');
387         if(space)       *space = '\0';  // Remove characters after the ' '
388         
389         // Check if trusted
390         if( !Client->bIsTrusted ) {
391                 if(giDebugLevel)
392                         printf("Client %i: Untrusted client attempting to AUTOAUTH\n", Client->ID);
393                 sendf(Client->Socket, "401 Untrusted\n");
394                 return ;
395         }
396         
397         // Get UID
398         Client->UID = Bank_GetUserID( Args );   
399         if( Client->UID < 0 ) {
400                 if(giDebugLevel)
401                         printf("Client %i: Unknown user '%s'\n", Client->ID, Args);
402                 sendf(Client->Socket, "401 Auth Failure\n");
403                 return ;
404         }
405         
406         // You can't be an internal account
407         if( Bank_GetFlags(Client->UID) & USER_FLAG_INTERNAL ) {
408                 Client->UID = -1;
409                 sendf(Client->Socket, "401 Auth Failure\n");
410                 return ;
411         }
412         
413         if(giDebugLevel)
414                 printf("Client %i: Authenticated as '%s' (%i)\n", Client->ID, Args, Client->UID);
415         
416         sendf(Client->Socket, "200 Auth OK\n");
417 }
418
419 /**
420  * \brief Set effective user
421  */
422 void Server_Cmd_SETEUSER(tClient *Client, char *Args)
423 {
424         char    *space;
425         
426         space = strchr(Args, ' ');
427         
428         if(space)       *space = '\0';
429         
430         if( !strlen(Args) ) {
431                 sendf(Client->Socket, "407 SETEUSER expects an argument\n");
432                 return ;
433         }
434
435         // Check user permissions
436         if( !(Bank_GetFlags(Client->UID) & USER_FLAG_COKE) ) {
437                 sendf(Client->Socket, "403 Not in coke\n");
438                 return ;
439         }
440         
441         // Set id
442         Client->EffectiveUID = Bank_GetUserID(Args);
443         if( Client->EffectiveUID == -1 ) {
444                 sendf(Client->Socket, "404 User not found\n");
445                 return ;
446         }
447         
448         // You can't be an internal account
449         if( Bank_GetFlags(Client->EffectiveUID) & USER_FLAG_INTERNAL ) {
450                 Client->EffectiveUID = -1;
451                 sendf(Client->Socket, "404 User not found\n");
452                 return ;
453         }
454         
455         sendf(Client->Socket, "200 User set\n");
456 }
457
458 /**
459  * \brief Enumerate the items that the server knows about
460  */
461 void Server_Cmd_ENUMITEMS(tClient *Client, char *Args)
462 {
463          int    i;
464
465         if( Args != NULL && strlen(Args) ) {
466                 sendf(Client->Socket, "407 ENUM_ITEMS takes no arguments\n");
467                 return ;
468         }
469
470         sendf(Client->Socket, "201 Items %i\n", giNumItems);
471
472         for( i = 0; i < giNumItems; i ++ ) {
473                 sendf(Client->Socket,
474                         "202 Item %s:%i %i %s\n",
475                          gaItems[i].Handler->Name, gaItems[i].ID, gaItems[i].Price, gaItems[i].Name
476                          );
477         }
478
479         sendf(Client->Socket, "200 List end\n");
480 }
481
482 tItem *_GetItemFromString(char *String)
483 {
484         tHandler        *handler;
485         char    *type = String;
486         char    *colon = strchr(String, ':');
487          int    num, i;
488         
489         if( !colon ) {
490                 return NULL;
491         }
492
493         num = atoi(colon+1);
494         *colon = '\0';
495
496         // Find handler
497         handler = NULL;
498         for( i = 0; i < giNumHandlers; i ++ )
499         {
500                 if( strcmp(gaHandlers[i]->Name, type) == 0) {
501                         handler = gaHandlers[i];
502                         break;
503                 }
504         }
505         if( !handler ) {
506                 return NULL;
507         }
508
509         // Find item
510         for( i = 0; i < giNumItems; i ++ )
511         {
512                 if( gaItems[i].Handler != handler )     continue;
513                 if( gaItems[i].ID != num )      continue;
514                 return &gaItems[i];
515         }
516         return NULL;
517 }
518
519 /**
520  * \brief Fetch information on a specific item
521  */
522 void Server_Cmd_ITEMINFO(tClient *Client, char *Args)
523 {
524         tItem   *item = _GetItemFromString(Args);
525         
526         if( !item ) {
527                 sendf(Client->Socket, "406 Bad Item ID\n");
528                 return ;
529         }
530         
531         sendf(Client->Socket,
532                 "202 Item %s:%i %i %s\n",
533                  item->Handler->Name, item->ID, item->Price, item->Name
534                  );
535 }
536
537 void Server_Cmd_DISPENSE(tClient *Client, char *Args)
538 {
539         tItem   *item;
540          int    ret;
541          int    uid;
542          
543         if( !Client->bIsAuthed ) {
544                 sendf(Client->Socket, "401 Not Authenticated\n");
545                 return ;
546         }
547
548         item = _GetItemFromString(Args);
549         if( !item ) {
550                 sendf(Client->Socket, "406 Bad Item ID\n");
551                 return ;
552         }
553         
554         if( Client->EffectiveUID != -1 ) {
555                 uid = Client->EffectiveUID;
556         }
557         else {
558                 uid = Client->UID;
559         }
560
561         switch( ret = DispenseItem( Client->UID, uid, item ) )
562         {
563         case 0: sendf(Client->Socket, "200 Dispense OK\n");     return ;
564         case 1: sendf(Client->Socket, "501 Unable to dispense\n");      return ;
565         case 2: sendf(Client->Socket, "402 Poor You\n");        return ;
566         default:
567                 sendf(Client->Socket, "500 Dispense Error\n");
568                 return ;
569         }
570 }
571
572 void Server_Cmd_GIVE(tClient *Client, char *Args)
573 {
574         char    *recipient, *ammount, *reason;
575          int    uid, iAmmount;
576          int    thisUid;
577         
578         if( !Client->bIsAuthed ) {
579                 sendf(Client->Socket, "401 Not Authenticated\n");
580                 return ;
581         }
582
583         recipient = Args;
584
585         ammount = strchr(Args, ' ');
586         if( !ammount ) {
587                 sendf(Client->Socket, "407 Invalid Argument, expected 3 parameters, 1 encountered\n");
588                 return ;
589         }
590         *ammount = '\0';
591         ammount ++;
592
593         reason = strchr(ammount, ' ');
594         if( !reason ) {
595                 sendf(Client->Socket, "407 Invalid Argument, expected 3 parameters, 2 encountered\n");
596                 return ;
597         }
598         *reason = '\0';
599         reason ++;
600
601         // Get recipient
602         uid = Bank_GetUserID(recipient);
603         if( uid == -1 ) {
604                 sendf(Client->Socket, "404 Invalid target user\n");
605                 return ;
606         }
607         
608         // You can't alter an internal account
609         if( Bank_GetFlags(uid) & USER_FLAG_INTERNAL ) {
610                 sendf(Client->Socket, "404 Invalid target user\n");
611                 return ;
612         }
613
614         // Parse ammount
615         iAmmount = atoi(ammount);
616         if( iAmmount <= 0 ) {
617                 sendf(Client->Socket, "407 Invalid Argument, ammount must be > zero\n");
618                 return ;
619         }
620         
621         if( Client->EffectiveUID != -1 ) {
622                 thisUid = Client->EffectiveUID;
623         }
624         else {
625                 thisUid = Client->UID;
626         }
627
628         // Do give
629         switch( DispenseGive(Client->UID, thisUid, uid, iAmmount, reason) )
630         {
631         case 0:
632                 sendf(Client->Socket, "200 Give OK\n");
633                 return ;
634         case 2:
635                 sendf(Client->Socket, "402 Poor You\n");
636                 return ;
637         default:
638                 sendf(Client->Socket, "500 Unknown error\n");
639                 return ;
640         }
641 }
642
643 void Server_Cmd_ADD(tClient *Client, char *Args)
644 {
645         char    *user, *ammount, *reason;
646          int    uid, iAmmount;
647         
648         if( !Client->bIsAuthed ) {
649                 sendf(Client->Socket, "401 Not Authenticated\n");
650                 return ;
651         }
652
653         user = Args;
654
655         ammount = strchr(Args, ' ');
656         if( !ammount ) {
657                 sendf(Client->Socket, "407 Invalid Argument, expected 3 parameters, 1 encountered\n");
658                 return ;
659         }
660         *ammount = '\0';
661         ammount ++;
662
663         reason = strchr(ammount, ' ');
664         if( !reason ) {
665                 sendf(Client->Socket, "407 Invalid Argument, expected 3 parameters, 2 encountered\n");
666                 return ;
667         }
668         *reason = '\0';
669         reason ++;
670
671         // Check user permissions
672         if( !(Bank_GetFlags(Client->UID) & USER_FLAG_COKE)  ) {
673                 sendf(Client->Socket, "403 Not in coke\n");
674                 return ;
675         }
676
677         // Get recipient
678         uid = Bank_GetUserID(user);
679         if( uid == -1 ) {
680                 sendf(Client->Socket, "404 Invalid user\n");
681                 return ;
682         }
683         
684         // You can't alter an internal account
685         if( Bank_GetFlags(uid) & USER_FLAG_INTERNAL ) {
686                 sendf(Client->Socket, "404 Invalid user\n");
687                 return ;
688         }
689
690         // Parse ammount
691         iAmmount = atoi(ammount);
692         if( iAmmount == 0 && ammount[0] != '0' ) {
693                 sendf(Client->Socket, "407 Invalid Argument\n");
694                 return ;
695         }
696
697         // Do give
698         switch( DispenseAdd(uid, Client->UID, iAmmount, reason) )
699         {
700         case 0:
701                 sendf(Client->Socket, "200 Add OK\n");
702                 return ;
703         case 2:
704                 sendf(Client->Socket, "402 Poor Guy\n");
705                 return ;
706         default:
707                 sendf(Client->Socket, "500 Unknown error\n");
708                 return ;
709         }
710 }
711
712 void Server_Cmd_ENUMUSERS(tClient *Client, char *Args)
713 {
714          int    i, numRet = 0;
715          int    maxBal = INT_MAX, minBal = INT_MIN;
716         tAcctIterator   *it;
717          int    sort = BANK_ITFLAG_SORT_NAME;
718         
719         // Parse arguments
720         if( Args && strlen(Args) )
721         {
722                 char    *min = Args, *max;
723                 
724                 max = strchr(Args, ' ');
725                 if( max ) {
726                         *max = '\0';
727                         max ++;
728                 }
729                 
730                 // If <minBal> != "-"
731                 if( strcmp(min, "-") != 0 )
732                         minBal = atoi(min);
733                 // If <maxBal> != "-"
734                 if( max && strcmp(max, "-") != 0 )
735                         maxBal = atoi(max);
736         }
737         
738         // Create iterator
739         if( maxBal != INT_MAX )
740                 it = Bank_Iterator(0, 0, sort|BANK_ITFLAG_MAXBALANCE, maxBal, 0);
741         else if( minBal != INT_MIN )
742                 it = Bank_Iterator(0, 0, sort|BANK_ITFLAG_MINBALANCE, minBal, 0);
743         else
744                 it = Bank_Iterator(0, 0, sort, 0, 0);
745         
746         // Get return number
747         while( (i = Bank_IteratorNext(it)) != -1 )
748         {
749                 int bal = Bank_GetBalance(i);
750                 
751                 if( bal == INT_MIN )    continue;
752                 
753                 if( bal < minBal )      continue;
754                 if( bal > maxBal )      continue;
755                 
756                 numRet ++;
757         }
758         
759         Bank_DelIterator(it);
760         
761         // Send count
762         sendf(Client->Socket, "201 Users %i\n", numRet);
763         
764         
765         // Create iterator
766         if( maxBal != INT_MAX )
767                 it = Bank_Iterator(0, 0, sort|BANK_ITFLAG_MAXBALANCE, maxBal, 0);
768         else if( minBal != INT_MIN )
769                 it = Bank_Iterator(0, 0, sort|BANK_ITFLAG_MINBALANCE, minBal, 0);
770         else
771                 it = Bank_Iterator(0, 0, sort, 0, 0);
772         
773         while( (i = Bank_IteratorNext(it)) != -1 )
774         {
775                 int bal = Bank_GetBalance(i);
776                 
777                 if( bal == INT_MIN )    continue;
778                 
779                 if( bal < minBal )      continue;
780                 if( bal > maxBal )      continue;
781                 
782                 _SendUserInfo(Client, i);
783         }
784         
785         Bank_DelIterator(it);
786         
787         sendf(Client->Socket, "200 List End\n");
788 }
789
790 void Server_Cmd_USERINFO(tClient *Client, char *Args)
791 {
792          int    uid;
793         char    *user = Args;
794         char    *space;
795         
796         space = strchr(user, ' ');
797         if(space)       *space = '\0';
798         
799         // Get recipient
800         uid = Bank_GetUserID(user);
801         if( uid == -1 ) {
802                 sendf(Client->Socket, "404 Invalid user");
803                 return ;
804         }
805         
806         _SendUserInfo(Client, uid);
807 }
808
809 void _SendUserInfo(tClient *Client, int UserID)
810 {
811         char    *type, *disabled="", *door="";
812          int    flags = Bank_GetFlags(UserID);
813         
814         if( flags & USER_FLAG_INTERNAL ) {
815                 type = "internal";
816         }
817         else if( flags & USER_FLAG_COKE ) {
818                 if( flags & USER_FLAG_WHEEL )
819                         type = "coke,wheel";
820                 else
821                         type = "coke";
822         }
823         else if( flags & USER_FLAG_WHEEL ) {
824                 type = "wheel";
825         }
826         else {
827                 type = "user";
828         }
829         
830         if( flags & USER_FLAG_DISABLED )
831                 disabled = ",disabled";
832         if( flags & USER_FLAG_DOORGROUP )
833                 door = ",door";
834         
835         // TODO: User flags/type
836         sendf(
837                 Client->Socket, "202 User %s %i %s%s\n",
838                 Bank_GetUserName(UserID), Bank_GetBalance(UserID),
839                 type, disabled
840                 );
841 }
842
843 void Server_Cmd_USERADD(tClient *Client, char *Args)
844 {
845         char    *username, *space;
846         
847         // Check permissions
848         if( !(Bank_GetFlags(Client->UID) & USER_FLAG_WHEEL) ) {
849                 sendf(Client->Socket, "403 Not Wheel\n");
850                 return ;
851         }
852         
853         // Read arguments
854         username = Args;
855         while( *username == ' ' )       username ++;
856         space = strchr(username, ' ');
857         if(space)       *space = '\0';
858         
859         // Try to create user
860         if( Bank_CreateUser(username) == -1 ) {
861                 sendf(Client->Socket, "404 User exists\n");
862                 return ;
863         }
864         
865         sendf(Client->Socket, "200 User Added\n");
866 }
867
868 void Server_Cmd_USERFLAGS(tClient *Client, char *Args)
869 {
870         char    *username, *flags;
871         char    *space;
872          int    mask=0, value=0;
873          int    uid;
874         
875         // Check permissions
876         if( !(Bank_GetFlags(Client->UID) & USER_FLAG_WHEEL) ) {
877                 sendf(Client->Socket, "403 Not Wheel\n");
878                 return ;
879         }
880         
881         // Read arguments
882         // - Username
883         username = Args;
884         while( *username == ' ' )       username ++;
885         space = strchr(username, ' ');
886         if(!space) {
887                 sendf(Client->Socket, "407 USER_FLAGS requires 2 arguments, 1 given\n");
888                 return ;
889         }
890         *space = '\0';
891         // - Flags
892         flags = space + 1;
893         while( *flags == ' ' )  flags ++;
894         space = strchr(flags, ' ');
895         if(space)       *space = '\0';
896         
897         // Get UID
898         uid = Bank_GetUserID(username);
899         if( uid == -1 ) {
900                 sendf(Client->Socket, "404 User '%s' not found\n", username);
901                 return ;
902         }
903         
904         // Parse flags
905         do {
906                  int    bRemove = 0;
907                  int    i;
908                 struct {
909                         const char      *Name;
910                          int    Mask;
911                          int    Value;
912                 }       cFLAGS[] = {
913                          {"disabled", USER_FLAG_DISABLED, USER_FLAG_DISABLED}
914                         ,{"door", USER_FLAG_DOORGROUP, USER_FLAG_DOORGROUP}
915                         ,{"coke", USER_FLAG_COKE, USER_FLAG_COKE}
916                         ,{"wheel", USER_FLAG_WHEEL, USER_FLAG_WHEEL}
917                 //      ,{"internal", USER_FLAG_INTERNAL, USER_FLAG_INTERNAL}
918                 };
919                 const int       ciNumFlags = sizeof(cFLAGS)/sizeof(cFLAGS[0]);
920                 
921                 while( *flags == ' ' )  flags ++;       // Eat whitespace
922                 space = strchr(flags, ',');     // Find the end of the flag
923                 if(space)       *space = '\0';
924                 
925                 // Check for inversion/removal
926                 if( *flags == '!' || *flags == '-' ) {
927                         bRemove = 1;
928                         flags ++;
929                 }
930                 else if( *flags == '+' ) {
931                         flags ++;
932                 }
933                 
934                 // Check flag values
935                 for( i = 0; i < ciNumFlags; i ++ )
936                 {
937                         if( strcmp(flags, cFLAGS[i].Name) == 0 ) {
938                                 mask |= cFLAGS[i].Mask;
939                                 value &= ~cFLAGS[i].Mask;
940                                 if( !bRemove )
941                                         value |= cFLAGS[i].Value;
942                                 break;
943                         }
944                 }
945                 
946                 // Error check
947                 if( i == ciNumFlags ) {
948                         sendf(Client->Socket, "407 Unknown flag value '%s'\n", flags);
949                         return ;
950                 }
951                 
952                 flags = space + 1;
953         } while(space);
954         
955         // Apply flags
956         Bank_SetFlags(uid, mask, value);
957         
958         // Return OK
959         sendf(Client->Socket, "200 User Updated\n");
960 }
961
962 // --- INTERNAL HELPERS ---
963 int sendf(int Socket, const char *Format, ...)
964 {
965         va_list args;
966          int    len;
967         
968         va_start(args, Format);
969         len = vsnprintf(NULL, 0, Format, args);
970         va_end(args);
971         
972         {
973                 char    buf[len+1];
974                 va_start(args, Format);
975                 vsnprintf(buf, len+1, Format, args);
976                 va_end(args);
977                 
978                 #if DEBUG_TRACE_CLIENT
979                 printf("sendf: %s", buf);
980                 #endif
981                 
982                 return send(Socket, buf, len, 0);
983         }
984 }

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