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

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