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

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