Changed user type from an enum to a collection of flags
[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    (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 = {0};
198         
199         // Initialise Client info
200         clientInfo.Socket = Socket;
201         clientInfo.ID = giServer_NextClientID ++;
202         clientInfo.bIsTrusted = bTrusted;
203         
204         // Read from client
205         /*
206          * Notes:
207          * - The `buf` and `remspace` variables allow a line to span several
208          *   calls to recv(), if a line is not completed in one recv() call
209          *   it is saved to the beginning of `inbuf` and `buf` is updated to
210          *   the end of it.
211          */
212         while( (bytes = recv(Socket, buf, remspace, 0)) > 0 )
213         {
214                 char    *eol, *start;
215                 buf[bytes] = '\0';      // Allow us to use stdlib string functions on it
216                 
217                 // Split by lines
218                 start = inbuf;
219                 while( (eol = strchr(start, '\n')) )
220                 {
221                         *eol = '\0';
222                         
223                         Server_ParseClientCommand(&clientInfo, start);
224                         
225                         start = eol + 1;
226                 }
227                 
228                 // Check if there was an incomplete line
229                 if( *start != '\0' ) {
230                          int    tailBytes = bytes - (start-buf);
231                         // Roll back in buffer
232                         memcpy(inbuf, start, tailBytes);
233                         remspace -= tailBytes;
234                         if(remspace == 0) {
235                                 send(Socket, MSG_STR_TOO_LONG, sizeof(MSG_STR_TOO_LONG), 0);
236                                 buf = inbuf;
237                                 remspace = INPUT_BUFFER_SIZE - 1;
238                         }
239                 }
240                 else {
241                         buf = inbuf;
242                         remspace = INPUT_BUFFER_SIZE - 1;
243                 }
244         }
245         
246         // Check for errors
247         if( bytes < 0 ) {
248                 fprintf(stderr, "ERROR: Unable to recieve from client on socket %i\n", Socket);
249                 return ;
250         }
251         
252         if(giDebugLevel >= 2) {
253                 printf("Client %i: Disconnected\n", clientInfo.ID);
254         }
255 }
256
257 /**
258  * \brief Parses a client command and calls the required helper function
259  * \param Client        Pointer to client state structure
260  * \param CommandString Command from client (single line of the command)
261  * \return Heap String to return to the client
262  */
263 void Server_ParseClientCommand(tClient *Client, char *CommandString)
264 {
265         char    *space, *args;
266          int    i;
267         #if 0
268         char    **argList;
269          int    numArgs = 0;
270         #endif
271         
272         // Split at first space
273         space = strchr(CommandString, ' ');
274         if(space == NULL) {
275                 args = NULL;
276         }
277         else {
278                 *space = '\0';
279                 args = space + 1;
280                 while( *space == ' ' )  space ++;
281                 
282                 #if 0
283                 // Count arguments
284                 numArgs = 1;
285                 for( i = 0; args[i]; )
286                 {
287                         while( CommandString[i] != ' ' ) {
288                                 if( CommandString[i] == '"' ) {
289                                         while( !(CommandString[i] != '\\' CommandString[i+1] == '"' ) )
290                                                 i ++;
291                                         i ++;
292                                 }
293                                 i ++;
294                         }
295                         numArgs ++;
296                         while( CommandString[i] == ' ' )        i ++;
297                 }
298                 #endif
299         }
300         
301         
302         // Find command
303         for( i = 0; i < NUM_COMMANDS; i++ )
304         {
305                 if(strcmp(CommandString, gaServer_Commands[i].Name) == 0) {
306                         gaServer_Commands[i].Function(Client, args);
307                         return ;
308                 }
309         }
310         
311         sendf(Client->Socket, "400 Unknown Command\n");
312 }
313
314 // ---
315 // Commands
316 // ---
317 /**
318  * \brief Set client username
319  * 
320  * Usage: USER <username>
321  */
322 void Server_Cmd_USER(tClient *Client, char *Args)
323 {
324         char    *space = strchr(Args, ' ');
325         if(space)       *space = '\0';  // Remove characters after the ' '
326         
327         // Debug!
328         if( giDebugLevel )
329                 printf("Client %i authenticating as '%s'\n", Client->ID, Args);
330         
331         // Save username
332         if(Client->Username)
333                 free(Client->Username);
334         Client->Username = strdup(Args);
335         
336         #if USE_SALT
337         // Create a salt (that changes if the username is changed)
338         // Yes, I know, I'm a little paranoid, but who isn't?
339         Client->Salt[0] = 0x21 + (rand()&0x3F);
340         Client->Salt[1] = 0x21 + (rand()&0x3F);
341         Client->Salt[2] = 0x21 + (rand()&0x3F);
342         Client->Salt[3] = 0x21 + (rand()&0x3F);
343         Client->Salt[4] = 0x21 + (rand()&0x3F);
344         Client->Salt[5] = 0x21 + (rand()&0x3F);
345         Client->Salt[6] = 0x21 + (rand()&0x3F);
346         Client->Salt[7] = 0x21 + (rand()&0x3F);
347         
348         // TODO: Also send hash type to use, (SHA1 or crypt according to [DAA])
349         sendf(Client->Socket, "100 SALT %s\n", Client->Salt);
350         #else
351         sendf(Client->Socket, "100 User Set\n");
352         #endif
353 }
354
355 /**
356  * \brief Authenticate as a user
357  * 
358  * Usage: PASS <hash>
359  */
360 void Server_Cmd_PASS(tClient *Client, char *Args)
361 {
362         char    *space = strchr(Args, ' ');
363         if(space)       *space = '\0';  // Remove characters after the ' '
364         
365         // Pass on to cokebank
366         Client->UID = GetUserAuth(Client->Salt, Client->Username, Args);
367
368         if( Client->UID != -1 ) {
369                 Client->bIsAuthed = 1;
370                 sendf(Client->Socket, "200 Auth OK\n");
371                 return ;
372         }
373         
374         sendf(Client->Socket, "401 Auth Failure\n");
375 }
376
377 /**
378  * \brief Authenticate as a user without a password
379  * 
380  * Usage: AUTOAUTH <user>
381  */
382 void Server_Cmd_AUTOAUTH(tClient *Client, char *Args)
383 {
384         char    *space = strchr(Args, ' ');
385         if(space)       *space = '\0';  // Remove characters after the ' '
386         
387         // Check if trusted
388         if( !Client->bIsTrusted ) {
389                 if(giDebugLevel)
390                         printf("Client %i: Untrusted client attempting to AUTOAUTH\n", Client->ID);
391                 sendf(Client->Socket, "401 Untrusted\n");
392                 return ;
393         }
394         
395         // Get UID
396         Client->UID = GetUserID( Args );        
397         if( Client->UID < 0 ) {
398                 if(giDebugLevel)
399                         printf("Client %i: Unknown user '%s'\n", Client->ID, Args);
400                 sendf(Client->Socket, "401 Auth Failure\n");
401                 return ;
402         }
403         
404         // You can't be an internal account
405         if( GetFlags(Client->UID) & USER_FLAG_INTERNAL ) {
406                 Client->UID = -1;
407                 sendf(Client->Socket, "401 Auth Failure\n");
408                 return ;
409         }
410         
411         if(giDebugLevel)
412                 printf("Client %i: Authenticated as '%s' (%i)\n", Client->ID, Args, Client->UID);
413         
414         sendf(Client->Socket, "200 Auth OK\n");
415 }
416
417 /**
418  * \brief Set effective user
419  */
420 void Server_Cmd_SETEUSER(tClient *Client, char *Args)
421 {
422         char    *space;
423         
424         space = strchr(Args, ' ');
425         
426         if(space)       *space = '\0';
427         
428         if( !strlen(Args) ) {
429                 sendf(Client->Socket, "407 SETEUSER expects an argument\n");
430                 return ;
431         }
432
433         // Check user permissions
434         if( !(GetFlags(Client->UID) & USER_FLAG_COKE) ) {
435                 sendf(Client->Socket, "403 Not in coke\n");
436                 return ;
437         }
438         
439         // Set id
440         Client->EffectiveUID = GetUserID(Args);
441         if( Client->EffectiveUID == -1 ) {
442                 sendf(Client->Socket, "404 User not found\n");
443                 return ;
444         }
445         
446         // You can't be an internal account
447         if( GetFlags(Client->EffectiveUID) & USER_FLAG_INTERNAL ) {
448                 Client->EffectiveUID = -1;
449                 sendf(Client->Socket, "404 User not found\n");
450                 return ;
451         }
452         
453         sendf(Client->Socket, "200 User set\n");
454 }
455
456 /**
457  * \brief Enumerate the items that the server knows about
458  */
459 void Server_Cmd_ENUMITEMS(tClient *Client, char *Args)
460 {
461          int    i;
462
463         sendf(Client->Socket, "201 Items %i\n", giNumItems);
464
465         for( i = 0; i < giNumItems; i ++ ) {
466                 sendf(Client->Socket,
467                         "202 Item %s:%i %i %s\n",
468                          gaItems[i].Handler->Name, gaItems[i].ID, gaItems[i].Price, gaItems[i].Name
469                          );
470         }
471
472         sendf(Client->Socket, "200 List end\n");
473 }
474
475 tItem *_GetItemFromString(char *String)
476 {
477         tHandler        *handler;
478         char    *type = String;
479         char    *colon = strchr(String, ':');
480          int    num, i;
481         
482         if( !colon ) {
483                 return NULL;
484         }
485
486         num = atoi(colon+1);
487         *colon = '\0';
488
489         // Find handler
490         handler = NULL;
491         for( i = 0; i < giNumHandlers; i ++ )
492         {
493                 if( strcmp(gaHandlers[i]->Name, type) == 0) {
494                         handler = gaHandlers[i];
495                         break;
496                 }
497         }
498         if( !handler ) {
499                 return NULL;
500         }
501
502         // Find item
503         for( i = 0; i < giNumItems; i ++ )
504         {
505                 if( gaItems[i].Handler != handler )     continue;
506                 if( gaItems[i].ID != num )      continue;
507                 return &gaItems[i];
508         }
509         return NULL;
510 }
511
512 /**
513  * \brief Fetch information on a specific item
514  */
515 void Server_Cmd_ITEMINFO(tClient *Client, char *Args)
516 {
517         tItem   *item = _GetItemFromString(Args);
518         
519         if( !item ) {
520                 sendf(Client->Socket, "406 Bad Item ID\n");
521                 return ;
522         }
523         
524         sendf(Client->Socket,
525                 "202 Item %s:%i %i %s\n",
526                  item->Handler->Name, item->ID, item->Price, item->Name
527                  );
528 }
529
530 void Server_Cmd_DISPENSE(tClient *Client, char *Args)
531 {
532         tItem   *item;
533          int    ret;
534          int    uid;
535          
536         if( !Client->bIsAuthed ) {
537                 sendf(Client->Socket, "401 Not Authenticated\n");
538                 return ;
539         }
540
541         item = _GetItemFromString(Args);
542         if( !item ) {
543                 sendf(Client->Socket, "406 Bad Item ID\n");
544                 return ;
545         }
546         
547         if( Client->EffectiveUID != -1 ) {
548                 uid = Client->EffectiveUID;
549         }
550         else {
551                 uid = Client->UID;
552         }
553
554         switch( ret = DispenseItem( Client->UID, uid, item ) )
555         {
556         case 0: sendf(Client->Socket, "200 Dispense OK\n");     return ;
557         case 1: sendf(Client->Socket, "501 Unable to dispense\n");      return ;
558         case 2: sendf(Client->Socket, "402 Poor You\n");        return ;
559         default:
560                 sendf(Client->Socket, "500 Dispense Error\n");
561                 return ;
562         }
563 }
564
565 void Server_Cmd_GIVE(tClient *Client, char *Args)
566 {
567         char    *recipient, *ammount, *reason;
568          int    uid, iAmmount;
569          int    thisUid;
570         
571         if( !Client->bIsAuthed ) {
572                 sendf(Client->Socket, "401 Not Authenticated\n");
573                 return ;
574         }
575
576         recipient = Args;
577
578         ammount = strchr(Args, ' ');
579         if( !ammount ) {
580                 sendf(Client->Socket, "407 Invalid Argument, expected 3 parameters, 1 encountered\n");
581                 return ;
582         }
583         *ammount = '\0';
584         ammount ++;
585
586         reason = strchr(ammount, ' ');
587         if( !reason ) {
588                 sendf(Client->Socket, "407 Invalid Argument, expected 3 parameters, 2 encountered\n");
589                 return ;
590         }
591         *reason = '\0';
592         reason ++;
593
594         // Get recipient
595         uid = GetUserID(recipient);
596         if( uid == -1 ) {
597                 sendf(Client->Socket, "404 Invalid target user\n");
598                 return ;
599         }
600         
601         // You can't alter an internal account
602         if( GetFlags(uid) & USER_FLAG_INTERNAL ) {
603                 sendf(Client->Socket, "404 Invalid target user\n");
604                 return ;
605         }
606
607         // Parse ammount
608         iAmmount = atoi(ammount);
609         if( iAmmount <= 0 ) {
610                 sendf(Client->Socket, "407 Invalid Argument, ammount must be > zero\n");
611                 return ;
612         }
613         
614         if( Client->EffectiveUID != -1 ) {
615                 thisUid = Client->EffectiveUID;
616         }
617         else {
618                 thisUid = Client->UID;
619         }
620
621         // Do give
622         switch( DispenseGive(Client->UID, thisUid, uid, iAmmount, reason) )
623         {
624         case 0:
625                 sendf(Client->Socket, "200 Give OK\n");
626                 return ;
627         case 2:
628                 sendf(Client->Socket, "402 Poor You\n");
629                 return ;
630         default:
631                 sendf(Client->Socket, "500 Unknown error\n");
632                 return ;
633         }
634 }
635
636 void Server_Cmd_ADD(tClient *Client, char *Args)
637 {
638         char    *user, *ammount, *reason;
639          int    uid, iAmmount;
640         
641         if( !Client->bIsAuthed ) {
642                 sendf(Client->Socket, "401 Not Authenticated\n");
643                 return ;
644         }
645
646         user = Args;
647
648         ammount = strchr(Args, ' ');
649         if( !ammount ) {
650                 sendf(Client->Socket, "407 Invalid Argument, expected 3 parameters, 1 encountered\n");
651                 return ;
652         }
653         *ammount = '\0';
654         ammount ++;
655
656         reason = strchr(ammount, ' ');
657         if( !reason ) {
658                 sendf(Client->Socket, "407 Invalid Argument, expected 3 parameters, 2 encountered\n");
659                 return ;
660         }
661         *reason = '\0';
662         reason ++;
663
664         // Check user permissions
665         if( !(GetFlags(Client->UID) & USER_FLAG_COKE)  ) {
666                 sendf(Client->Socket, "403 Not in coke\n");
667                 return ;
668         }
669
670         // Get recipient
671         uid = GetUserID(user);
672         if( uid == -1 ) {
673                 sendf(Client->Socket, "404 Invalid user\n");
674                 return ;
675         }
676         
677         // You can't alter an internal account
678         if( GetFlags(uid) & USER_FLAG_INTERNAL ) {
679                 sendf(Client->Socket, "404 Invalid user\n");
680                 return ;
681         }
682
683         // Parse ammount
684         iAmmount = atoi(ammount);
685         if( iAmmount == 0 && ammount[0] != '0' ) {
686                 sendf(Client->Socket, "407 Invalid Argument\n");
687                 return ;
688         }
689
690         // Do give
691         switch( DispenseAdd(uid, Client->UID, iAmmount, reason) )
692         {
693         case 0:
694                 sendf(Client->Socket, "200 Add OK\n");
695                 return ;
696         case 2:
697                 sendf(Client->Socket, "402 Poor Guy\n");
698                 return ;
699         default:
700                 sendf(Client->Socket, "500 Unknown error\n");
701                 return ;
702         }
703 }
704
705 void Server_Cmd_ENUMUSERS(tClient *Client, char *Args)
706 {
707          int    i, numRet = 0;
708          int    maxBal = INT_MAX, minBal = INT_MIN;
709          int    numUsr = GetMaxID();
710         
711         // Parse arguments
712         if( Args && strlen(Args) )
713         {
714                 char    *min = Args, *max;
715                 
716                 max = strchr(Args, ' ');
717                 if( max ) {
718                         *max = '\0';
719                         max ++;
720                 }
721                 
722                 // If <minBal> != "-"
723                 if( strcmp(min, "-") != 0 )
724                         minBal = atoi(min);
725                 // If <maxBal> != "-"
726                 if( max && strcmp(max, "-") != 0 )
727                         maxBal = atoi(max);
728         }
729         
730         // Get return number
731         for( i = 0; i < numUsr; i ++ )
732         {
733                 int bal = GetBalance(i);
734                 
735                 if( bal == INT_MIN )    continue;
736                 
737                 if( bal < minBal )      continue;
738                 if( bal > maxBal )      continue;
739                 
740                 numRet ++;
741         }
742         
743         // Send count
744         sendf(Client->Socket, "201 Users %i\n", numRet);
745         
746         for( i = 0; i < numUsr; i ++ )
747         {
748                 int bal = GetBalance(i);
749                 
750                 if( bal == INT_MIN )    continue;
751                 
752                 if( bal < minBal )      continue;
753                 if( bal > maxBal )      continue;
754                 
755                 _SendUserInfo(Client, i);
756         }
757         
758         sendf(Client->Socket, "200 List End\n");
759 }
760
761 void Server_Cmd_USERINFO(tClient *Client, char *Args)
762 {
763          int    uid;
764         char    *user = Args;
765         char    *space;
766         
767         space = strchr(user, ' ');
768         if(space)       *space = '\0';
769         
770         // Get recipient
771         uid = GetUserID(user);
772         if( uid == -1 ) {
773                 sendf(Client->Socket, "404 Invalid user");
774                 return ;
775         }
776         
777         _SendUserInfo(Client, uid);
778 }
779
780 void _SendUserInfo(tClient *Client, int UserID)
781 {
782         char    *type, *disabled="", *door="";
783          int    flags = GetFlags(UserID);
784         
785         if( flags & USER_FLAG_INTERNAL ) {
786                 type = "internal";
787         }
788         else if( flags & USER_FLAG_COKE ) {
789                 if( flags & USER_FLAG_WHEEL )
790                         type = "coke,wheel";
791                 else
792                         type = "coke";
793         }
794         else if( flags & USER_FLAG_WHEEL ) {
795                 type = "wheel";
796         }
797         else {
798                 type = "user";
799         }
800         
801         if( flags & USER_FLAG_DISABLED )
802                 disabled = ",disabled";
803         if( flags & USER_FLAG_DOORGROUP )
804                 door = ",door";
805         
806         // TODO: User flags/type
807         sendf(
808                 Client->Socket, "202 User %s %i %s%s\n",
809                 GetUserName(UserID), GetBalance(UserID),
810                 type, disabled
811                 );
812 }
813
814 void Server_Cmd_USERADD(tClient *Client, char *Args)
815 {
816         char    *username, *space;
817         
818         // Check permissions
819         if( !(GetFlags(Client->UID) & USER_FLAG_WHEEL) ) {
820                 sendf(Client->Socket, "403 Not Wheel\n");
821                 return ;
822         }
823         
824         // Read arguments
825         username = Args;
826         while( *username == ' ' )       username ++;
827         space = strchr(username, ' ');
828         if(space)       *space = '\0';
829         
830         // Try to create user
831         if( CreateUser(username) == -1 ) {
832                 sendf(Client->Socket, "404 User exists\n");
833                 return ;
834         }
835         
836         sendf(Client->Socket, "200 User Added\n");
837 }
838
839 void Server_Cmd_USERFLAGS(tClient *Client, char *Args)
840 {
841         char    *username, *flags;
842         char    *space;
843          int    mask=0, value=0;
844          int    uid;
845         
846         // Check permissions
847         if( !(GetFlags(Client->UID) & USER_FLAG_WHEEL) ) {
848                 sendf(Client->Socket, "403 Not Wheel\n");
849                 return ;
850         }
851         
852         // Read arguments
853         // - Username
854         username = Args;
855         while( *username == ' ' )       username ++;
856         space = strchr(username, ' ');
857         if(!space) {
858                 sendf(Client->Socket, "407 USER_FLAGS requires 2 arguments, 1 given\n");
859                 return ;
860         }
861         *space = '\0';
862         // - Flags
863         flags = space + 1;
864         while( *flags == ' ' )  flags ++;
865         space = strchr(flags, ' ');
866         if(space)       *space = '\0';
867         
868         // Get UID
869         uid = GetUserID(username);
870         if( uid == -1 ) {
871                 sendf(Client->Socket, "404 User '%s' not found\n", username);
872                 return ;
873         }
874         
875         // Parse flags
876         do {
877                  int    bRemove = 0;
878                  int    i;
879                 struct {
880                         const char      *Name;
881                          int    Mask;
882                          int    Value;
883                 }       cFLAGS[] = {
884                          {"disabled", USER_FLAG_DISABLED, USER_FLAG_DISABLED}
885                         ,{"door", USER_FLAG_DOORGROUP, USER_FLAG_DOORGROUP}
886                         ,{"coke", USER_FLAG_COKE, USER_FLAG_COKE}
887                         ,{"wheel", USER_FLAG_WHEEL, USER_FLAG_WHEEL}
888                 //      ,{"internal", USER_FLAG_INTERNAL, USER_FLAG_INTERNAL}
889                 };
890                 const int       ciNumFlags = sizeof(cFLAGS)/sizeof(cFLAGS[0]);
891                 
892                 while( *flags == ' ' )  flags ++;       // Eat whitespace
893                 space = strchr(flags, ',');     // Find the end of the flag
894                 if(space)       *space = '\0';
895                 
896                 // Check for inversion/removal
897                 if( *flags == '!' || *flags == '-' ) {
898                         bRemove = 1;
899                         flags ++;
900                 }
901                 else if( *flags == '+' ) {
902                         flags ++;
903                 }
904                 
905                 // Check flag values
906                 for( i = 0; i < ciNumFlags; i ++ )
907                 {
908                         if( strcmp(flags, cFLAGS[i].Name) == 0 ) {
909                                 mask |= cFLAGS[i].Mask;
910                                 value &= ~cFLAGS[i].Mask;
911                                 if( !bRemove )
912                                         value |= cFLAGS[i].Value;
913                                 break;
914                         }
915                 }
916                 
917                 // Error check
918                 if( i == ciNumFlags ) {
919                         sendf(Client->Socket, "407 Unknown flag value '%s'\n", flags);
920                         return ;
921                 }
922                 
923                 flags = space + 1;
924         } while(space);
925         
926         // Apply flags
927         SetFlags(uid, mask, value);
928         
929         // Return OK
930         sendf(Client->Socket, "200 User Updated\n");
931 }
932
933 // --- INTERNAL HELPERS ---
934 int sendf(int Socket, const char *Format, ...)
935 {
936         va_list args;
937          int    len;
938         
939         va_start(args, Format);
940         len = vsnprintf(NULL, 0, Format, args);
941         va_end(args);
942         
943         {
944                 char    buf[len+1];
945                 va_start(args, Format);
946                 vsnprintf(buf, len+1, Format, args);
947                 va_end(args);
948                 
949                 #if DEBUG_TRACE_CLIENT
950                 printf("sendf: %s", buf);
951                 #endif
952                 
953                 return send(Socket, buf, len, 0);
954         }
955 }

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