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

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