Completed removing SET, implemented GIVE
[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         if(giDebugLevel)
405                 printf("Client %i: Authenticated as '%s' (%i)\n", Client->ID, Args, Client->UID);
406         
407         sendf(Client->Socket, "200 Auth OK\n");
408 }
409
410 /**
411  * \brief Set effective user
412  */
413 void Server_Cmd_SETEUSER(tClient *Client, char *Args)
414 {
415         char    *space;
416         
417         space = strchr(Args, ' ');
418         
419         if(space)       *space = '\0';
420         
421         if( !strlen(Args) ) {
422                 sendf(Client->Socket, "407 SETEUSER expects an argument\n");
423                 return ;
424         }
425
426         // Check user permissions
427         if( (GetFlags(Client->UID) & USER_FLAG_TYPEMASK) < USER_TYPE_COKE ) {
428                 sendf(Client->Socket, "403 Not in coke\n");
429                 return ;
430         }
431         
432         // Set id
433         Client->EffectiveUID = GetUserID(Args);
434         if( Client->EffectiveUID == -1 ) {
435                 sendf(Client->Socket, "404 User not found\n");
436                 return ;
437         }
438         
439         sendf(Client->Socket, "200 User set\n");
440 }
441
442 /**
443  * \brief Enumerate the items that the server knows about
444  */
445 void Server_Cmd_ENUMITEMS(tClient *Client, char *Args)
446 {
447          int    i;
448
449         sendf(Client->Socket, "201 Items %i\n", giNumItems);
450
451         for( i = 0; i < giNumItems; i ++ ) {
452                 sendf(Client->Socket,
453                         "202 Item %s:%i %i %s\n",
454                          gaItems[i].Handler->Name, gaItems[i].ID, gaItems[i].Price, gaItems[i].Name
455                          );
456         }
457
458         sendf(Client->Socket, "200 List end\n");
459 }
460
461 tItem *_GetItemFromString(char *String)
462 {
463         tHandler        *handler;
464         char    *type = String;
465         char    *colon = strchr(String, ':');
466          int    num, i;
467         
468         if( !colon ) {
469                 return NULL;
470         }
471
472         num = atoi(colon+1);
473         *colon = '\0';
474
475         // Find handler
476         handler = NULL;
477         for( i = 0; i < giNumHandlers; i ++ )
478         {
479                 if( strcmp(gaHandlers[i]->Name, type) == 0) {
480                         handler = gaHandlers[i];
481                         break;
482                 }
483         }
484         if( !handler ) {
485                 return NULL;
486         }
487
488         // Find item
489         for( i = 0; i < giNumItems; i ++ )
490         {
491                 if( gaItems[i].Handler != handler )     continue;
492                 if( gaItems[i].ID != num )      continue;
493                 return &gaItems[i];
494         }
495         return NULL;
496 }
497
498 /**
499  * \brief Fetch information on a specific item
500  */
501 void Server_Cmd_ITEMINFO(tClient *Client, char *Args)
502 {
503         tItem   *item = _GetItemFromString(Args);
504         
505         if( !item ) {
506                 sendf(Client->Socket, "406 Bad Item ID\n");
507                 return ;
508         }
509         
510         sendf(Client->Socket,
511                 "202 Item %s:%i %i %s\n",
512                  item->Handler->Name, item->ID, item->Price, item->Name
513                  );
514 }
515
516 void Server_Cmd_DISPENSE(tClient *Client, char *Args)
517 {
518         tItem   *item;
519          int    ret;
520          int    uid;
521          
522         if( !Client->bIsAuthed ) {
523                 sendf(Client->Socket, "401 Not Authenticated\n");
524                 return ;
525         }
526
527         item = _GetItemFromString(Args);
528         if( !item ) {
529                 sendf(Client->Socket, "406 Bad Item ID\n");
530                 return ;
531         }
532         
533         if( Client->EffectiveUID != -1 ) {
534                 uid = Client->EffectiveUID;
535         }
536         else {
537                 uid = Client->UID;
538         }
539
540         switch( ret = DispenseItem( Client->UID, uid, item ) )
541         {
542         case 0: sendf(Client->Socket, "200 Dispense OK\n");     return ;
543         case 1: sendf(Client->Socket, "501 Unable to dispense\n");      return ;
544         case 2: sendf(Client->Socket, "402 Poor You\n");        return ;
545         default:
546                 sendf(Client->Socket, "500 Dispense Error\n");
547                 return ;
548         }
549 }
550
551 void Server_Cmd_GIVE(tClient *Client, char *Args)
552 {
553         char    *recipient, *ammount, *reason;
554          int    uid, iAmmount;
555          int    thisUid;
556         
557         if( !Client->bIsAuthed ) {
558                 sendf(Client->Socket, "401 Not Authenticated\n");
559                 return ;
560         }
561
562         recipient = Args;
563
564         ammount = strchr(Args, ' ');
565         if( !ammount ) {
566                 sendf(Client->Socket, "407 Invalid Argument, expected 3 parameters, 1 encountered\n");
567                 return ;
568         }
569         *ammount = '\0';
570         ammount ++;
571
572         reason = strchr(ammount, ' ');
573         if( !reason ) {
574                 sendf(Client->Socket, "407 Invalid Argument, expected 3 parameters, 2 encountered\n");
575                 return ;
576         }
577         *reason = '\0';
578         reason ++;
579
580         // Get recipient
581         uid = GetUserID(recipient);
582         if( uid == -1 ) {
583                 sendf(Client->Socket, "404 Invalid target user\n");
584                 return ;
585         }
586
587         // Parse ammount
588         iAmmount = atoi(ammount);
589         if( iAmmount <= 0 ) {
590                 sendf(Client->Socket, "407 Invalid Argument, ammount must be > zero\n");
591                 return ;
592         }
593         
594         if( Client->EffectiveUID != -1 ) {
595                 thisUid = Client->EffectiveUID;
596         }
597         else {
598                 thisUid = Client->UID;
599         }
600
601         // Do give
602         switch( DispenseGive(Client->UID, thisUid, uid, iAmmount, reason) )
603         {
604         case 0:
605                 sendf(Client->Socket, "200 Give OK\n");
606                 return ;
607         case 2:
608                 sendf(Client->Socket, "402 Poor You\n");
609                 return ;
610         default:
611                 sendf(Client->Socket, "500 Unknown error\n");
612                 return ;
613         }
614 }
615
616 void Server_Cmd_ADD(tClient *Client, char *Args)
617 {
618         char    *user, *ammount, *reason;
619          int    uid, iAmmount;
620         
621         if( !Client->bIsAuthed ) {
622                 sendf(Client->Socket, "401 Not Authenticated\n");
623                 return ;
624         }
625
626         user = Args;
627
628         ammount = strchr(Args, ' ');
629         if( !ammount ) {
630                 sendf(Client->Socket, "407 Invalid Argument, expected 3 parameters, 1 encountered\n");
631                 return ;
632         }
633         *ammount = '\0';
634         ammount ++;
635
636         reason = strchr(ammount, ' ');
637         if( !reason ) {
638                 sendf(Client->Socket, "407 Invalid Argument, expected 3 parameters, 2 encountered\n");
639                 return ;
640         }
641         *reason = '\0';
642         reason ++;
643
644         // Check user permissions
645         if( (GetFlags(Client->UID) & USER_FLAG_TYPEMASK) < USER_TYPE_COKE ) {
646                 sendf(Client->Socket, "403 Not in coke\n");
647                 return ;
648         }
649
650         // Get recipient
651         uid = GetUserID(user);
652
653         // Check user permissions
654         if( (GetFlags(Client->UID) & USER_FLAG_TYPEMASK) < USER_TYPE_COKE ) {
655                 sendf(Client->Socket, "403 Not in coke\n");
656                 return ;
657         }
658         if( uid == -1 ) {
659                 sendf(Client->Socket, "404 Invalid user\n");
660                 return ;
661         }
662
663         // Parse ammount
664         iAmmount = atoi(ammount);
665         if( iAmmount == 0 && ammount[0] != '0' ) {
666                 sendf(Client->Socket, "407 Invalid Argument\n");
667                 return ;
668         }
669
670         // Do give
671         switch( DispenseAdd(uid, Client->UID, iAmmount, reason) )
672         {
673         case 0:
674                 sendf(Client->Socket, "200 Add OK\n");
675                 return ;
676         case 2:
677                 sendf(Client->Socket, "402 Poor Guy\n");
678                 return ;
679         default:
680                 sendf(Client->Socket, "500 Unknown error\n");
681                 return ;
682         }
683 }
684
685 void Server_Cmd_ENUMUSERS(tClient *Client, char *Args)
686 {
687          int    i, numRet = 0;
688          int    maxBal = INT_MAX, minBal = INT_MIN;
689          int    numUsr = GetMaxID();
690         
691         // Parse arguments
692         if( Args && strlen(Args) )
693         {
694                 char    *min = Args, *max;
695                 
696                 max = strchr(Args, ' ');
697                 if( max ) {
698                         *max = '\0';
699                         max ++;
700                 }
701                 
702                 // If <minBal> != "-"
703                 if( strcmp(min, "-") != 0 )
704                         minBal = atoi(min);
705                 // If <maxBal> != "-"
706                 if( max && strcmp(max, "-") != 0 )
707                         maxBal = atoi(max);
708         }
709         
710         // Get return number
711         for( i = 0; i < numUsr; i ++ )
712         {
713                 int bal = GetBalance(i);
714                 
715                 if( bal == INT_MIN )    continue;
716                 
717                 if( bal < minBal )      continue;
718                 if( bal > maxBal )      continue;
719                 
720                 numRet ++;
721         }
722         
723         // Send count
724         sendf(Client->Socket, "201 Users %i\n", numRet);
725         
726         for( i = 0; i < numUsr; i ++ )
727         {
728                 int bal = GetBalance(i);
729                 
730                 if( bal == INT_MIN )    continue;
731                 
732                 if( bal < minBal )      continue;
733                 if( bal > maxBal )      continue;
734                 
735                 _SendUserInfo(Client, i);
736         }
737         
738         sendf(Client->Socket, "200 List End\n");
739 }
740
741 void Server_Cmd_USERINFO(tClient *Client, char *Args)
742 {
743          int    uid;
744         char    *user = Args;
745         char    *space;
746         
747         space = strchr(user, ' ');
748         if(space)       *space = '\0';
749         
750         // Get recipient
751         uid = GetUserID(user);
752         if( uid == -1 ) {
753                 sendf(Client->Socket, "404 Invalid user");
754                 return ;
755         }
756         
757         _SendUserInfo(Client, uid);
758 }
759
760 void _SendUserInfo(tClient *Client, int UserID)
761 {
762         char    *type, *disabled="";
763          int    flags = GetFlags(UserID);
764         
765         switch( flags & USER_FLAG_TYPEMASK )
766         {
767         default:
768         case USER_TYPE_NORMAL:  type = "user";  break;
769         case USER_TYPE_COKE:    type = "coke";  break;
770         case USER_TYPE_WHEEL:   type = "wheel"; break;
771         case USER_TYPE_GOD:     type = "meta";  break;
772         }
773         
774         if( flags & USER_FLAG_DISABLED )
775                 disabled = ",disabled";
776         if( flags & USER_FLAG_DOORGROUP )
777                 disabled = ",door";
778         
779         // TODO: User flags/type
780         sendf(
781                 Client->Socket, "202 User %s %i %s%s\n",
782                 GetUserName(UserID), GetBalance(UserID),
783                 type, disabled
784                 );
785 }
786
787 void Server_Cmd_USERADD(tClient *Client, char *Args)
788 {
789         char    *username, *space;
790         
791         // Check permissions
792         if( (GetFlags(Client->UID) & USER_FLAG_TYPEMASK) < USER_TYPE_WHEEL ) {
793                 sendf(Client->Socket, "403 Not Wheel\n");
794                 return ;
795         }
796         
797         // Read arguments
798         username = Args;
799         while( *username == ' ' )       username ++;
800         space = strchr(username, ' ');
801         if(space)       *space = '\0';
802         
803         // Try to create user
804         if( CreateUser(username) == -1 ) {
805                 sendf(Client->Socket, "404 User exists\n");
806                 return ;
807         }
808         
809         sendf(Client->Socket, "200 User Added\n");
810 }
811
812 void Server_Cmd_USERFLAGS(tClient *Client, char *Args)
813 {
814         char    *username, *flags;
815         char    *space;
816          int    mask=0, value=0;
817          int    uid;
818         
819         // Check permissions
820         if( (GetFlags(Client->UID) & USER_FLAG_TYPEMASK) < USER_TYPE_WHEEL ) {
821                 sendf(Client->Socket, "403 Not Wheel\n");
822                 return ;
823         }
824         
825         // Read arguments
826         // - Username
827         username = Args;
828         while( *username == ' ' )       username ++;
829         space = strchr(username, ' ');
830         if(!space) {
831                 sendf(Client->Socket, "407 USER_FLAGS requires 2 arguments, 1 given\n");
832                 return ;
833         }
834         *space = '\0';
835         // - Flags
836         flags = space + 1;
837         while( *flags == ' ' )  flags ++;
838         space = strchr(flags, ' ');
839         if(space)       *space = '\0';
840         
841         // Get UID
842         uid = GetUserID(username);
843         if( uid == -1 ) {
844                 sendf(Client->Socket, "404 User '%s' not found\n", username);
845                 return ;
846         }
847         
848         // Parse flags
849         do {
850                  int    bRemove = 0;
851                  int    i;
852                 struct {
853                         const char      *Name;
854                          int    Mask;
855                          int    Value;
856                 }       cFLAGS[] = {
857                         {"disabled", USER_FLAG_DISABLED, USER_FLAG_DISABLED},
858                         {"door", USER_FLAG_DOORGROUP, USER_FLAG_DOORGROUP},
859                         {"user", USER_FLAG_TYPEMASK, USER_TYPE_NORMAL},
860                         {"coke", USER_FLAG_TYPEMASK, USER_TYPE_COKE},
861                         {"wheel", USER_FLAG_TYPEMASK, USER_TYPE_WHEEL},
862                         {"meta", USER_FLAG_TYPEMASK, USER_TYPE_GOD}
863                 };
864                 const int       ciNumFlags = sizeof(cFLAGS)/sizeof(cFLAGS[0]);
865                 
866                 while( *flags == ' ' )  flags ++;       // Eat whitespace
867                 space = strchr(flags, ',');     // Find the end of the flag
868                 if(space)       *space = '\0';
869                 
870                 // Check for inversion/removal
871                 if( *flags == '!' || *flags == '-' ) {
872                         bRemove = 1;
873                         flags ++;
874                 }
875                 else if( *flags == '+' ) {
876                         flags ++;
877                 }
878                 
879                 // Check flag values
880                 for( i = 0; i < ciNumFlags; i ++ )
881                 {
882                         if( strcmp(flags, cFLAGS[i].Name) == 0 ) {
883                                 mask |= cFLAGS[i].Mask;
884                                 value &= ~cFLAGS[i].Mask;
885                                 if( !bRemove )
886                                         value |= cFLAGS[i].Value;
887                                 break;
888                         }
889                 }
890                 
891                 // Error check
892                 if( i == ciNumFlags ) {
893                         sendf(Client->Socket, "407 Unknown flag value '%s'\n", flags);
894                         return ;
895                 }
896                 
897                 flags = space + 1;
898         } while(space);
899         
900         // Apply flags
901         SetFlags(uid, mask, value);
902         
903         // Return OK
904         sendf(Client->Socket, "200 User Updated\n");
905 }
906
907 // --- INTERNAL HELPERS ---
908 int sendf(int Socket, const char *Format, ...)
909 {
910         va_list args;
911          int    len;
912         
913         va_start(args, Format);
914         len = vsnprintf(NULL, 0, Format, args);
915         va_end(args);
916         
917         {
918                 char    buf[len+1];
919                 va_start(args, Format);
920                 vsnprintf(buf, len+1, Format, args);
921                 va_end(args);
922                 
923                 #if DEBUG_TRACE_CLIENT
924                 printf("sendf: %s", buf);
925                 #endif
926                 
927                 return send(Socket, buf, len, 0);
928         }
929 }

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