3 * UCC (University [of WA] Computer Club) Electronic Accounting System
5 * server.c - Client Server Code
7 * This file is licenced under the 3-clause BSD Licence. See the file
8 * COPYING for full details.
13 #include <sys/socket.h>
14 #include <netinet/in.h>
15 #include <arpa/inet.h>
21 #define DEBUG_TRACE_CLIENT 0
24 #define MAX_CONNECTION_QUEUE 5
25 #define INPUT_BUFFER_SIZE 256
27 #define HASH_TYPE SHA1
28 #define HASH_LENGTH 20
30 #define MSG_STR_TOO_LONG "499 Command too long (limit "EXPSTR(INPUT_BUFFER_SIZE)")\n"
33 typedef struct sClient
35 int Socket; // Client socket ID
38 int bIsTrusted; // Is the connection from a trusted host/port
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);
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_DONATE(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);
70 int Server_int_ParseFlags(tClient *Client, const char *Str, int *Mask, int *Value);
71 int sendf(int Socket, const char *Format, ...);
75 const struct sClientCommand {
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 {"DONATE", Server_Cmd_DONATE},
88 {"ADD", Server_Cmd_ADD},
89 {"ENUM_USERS", Server_Cmd_ENUMUSERS},
90 {"USER_INFO", Server_Cmd_USERINFO},
91 {"USER_ADD", Server_Cmd_USERADD},
92 {"USER_FLAGS", Server_Cmd_USERFLAGS}
94 #define NUM_COMMANDS ((int)(sizeof(gaServer_Commands)/sizeof(gaServer_Commands[0])))
97 int giServer_Port = 1020;
98 int giServer_NextClientID = 1;
103 * \brief Open listenting socket and serve connections
105 void Server_Start(void)
108 struct sockaddr_in server_addr, client_addr;
110 atexit(Server_Cleanup);
113 giServer_Socket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
114 if( giServer_Socket < 0 ) {
115 fprintf(stderr, "ERROR: Unable to create server socket\n");
119 // Make listen address
120 memset(&server_addr, 0, sizeof(server_addr));
121 server_addr.sin_family = AF_INET; // Internet Socket
122 server_addr.sin_addr.s_addr = htonl(INADDR_ANY); // Listen on all interfaces
123 server_addr.sin_port = htons(giServer_Port); // Port
126 if( bind(giServer_Socket, (struct sockaddr *) &server_addr, sizeof(server_addr)) < 0 ) {
127 fprintf(stderr, "ERROR: Unable to bind to 0.0.0.0:%i\n", giServer_Port);
133 if( listen(giServer_Socket, MAX_CONNECTION_QUEUE) < 0 ) {
134 fprintf(stderr, "ERROR: Unable to listen to socket\n");
139 printf("Listening on 0.0.0.0:%i\n", giServer_Port);
143 uint len = sizeof(client_addr);
146 client_socket = accept(giServer_Socket, (struct sockaddr *) &client_addr, &len);
147 if(client_socket < 0) {
148 fprintf(stderr, "ERROR: Unable to accept client connection\n");
152 if(giDebugLevel >= 2) {
153 char ipstr[INET_ADDRSTRLEN];
154 inet_ntop(AF_INET, &client_addr.sin_addr, ipstr, INET_ADDRSTRLEN);
155 printf("Client connection from %s:%i\n",
156 ipstr, ntohs(client_addr.sin_port));
159 // Trusted Connections
160 if( ntohs(client_addr.sin_port) < 1024 )
162 // TODO: Make this runtime configurable
163 switch( ntohl( client_addr.sin_addr.s_addr ) )
165 case 0x7F000001: // 127.0.0.1 localhost
166 //case 0x825E0D00: // 130.95.13.0
167 case 0x825E0D12: // 130.95.13.18 mussel
168 case 0x825E0D17: // 130.95.13.23 martello
176 // TODO: Multithread this?
177 Server_HandleClient(client_socket, bTrusted);
179 close(client_socket);
183 void Server_Cleanup(void)
185 printf("Close(%i)\n", giServer_Socket);
186 close(giServer_Socket);
190 * \brief Reads from a client socket and parses the command strings
191 * \param Socket Client socket number/handle
192 * \param bTrusted Is the client trusted?
194 void Server_HandleClient(int Socket, int bTrusted)
196 char inbuf[INPUT_BUFFER_SIZE];
198 int remspace = INPUT_BUFFER_SIZE-1;
202 memset(&clientInfo, 0, sizeof(clientInfo));
204 // Initialise Client info
205 clientInfo.Socket = Socket;
206 clientInfo.ID = giServer_NextClientID ++;
207 clientInfo.bIsTrusted = bTrusted;
208 clientInfo.EffectiveUID = -1;
213 * - The `buf` and `remspace` variables allow a line to span several
214 * calls to recv(), if a line is not completed in one recv() call
215 * it is saved to the beginning of `inbuf` and `buf` is updated to
218 while( (bytes = recv(Socket, buf, remspace, 0)) > 0 )
221 buf[bytes] = '\0'; // Allow us to use stdlib string functions on it
225 while( (eol = strchr(start, '\n')) )
229 Server_ParseClientCommand(&clientInfo, start);
234 // Check if there was an incomplete line
235 if( *start != '\0' ) {
236 int tailBytes = bytes - (start-buf);
237 // Roll back in buffer
238 memcpy(inbuf, start, tailBytes);
239 remspace -= tailBytes;
241 send(Socket, MSG_STR_TOO_LONG, sizeof(MSG_STR_TOO_LONG), 0);
243 remspace = INPUT_BUFFER_SIZE - 1;
248 remspace = INPUT_BUFFER_SIZE - 1;
254 fprintf(stderr, "ERROR: Unable to recieve from client on socket %i\n", Socket);
258 if(giDebugLevel >= 2) {
259 printf("Client %i: Disconnected\n", clientInfo.ID);
264 * \brief Parses a client command and calls the required helper function
265 * \param Client Pointer to client state structure
266 * \param CommandString Command from client (single line of the command)
267 * \return Heap String to return to the client
269 void Server_ParseClientCommand(tClient *Client, char *CommandString)
278 // Split at first space
279 space = strchr(CommandString, ' ');
286 while( *space == ' ' ) space ++;
291 for( i = 0; args[i]; )
293 while( CommandString[i] != ' ' ) {
294 if( CommandString[i] == '"' ) {
295 while( !(CommandString[i] != '\\' CommandString[i+1] == '"' ) )
302 while( CommandString[i] == ' ' ) i ++;
309 for( i = 0; i < NUM_COMMANDS; i++ )
311 if(strcmp(CommandString, gaServer_Commands[i].Name) == 0) {
312 gaServer_Commands[i].Function(Client, args);
317 sendf(Client->Socket, "400 Unknown Command\n");
324 * \brief Set client username
326 * Usage: USER <username>
328 void Server_Cmd_USER(tClient *Client, char *Args)
330 char *space = strchr(Args, ' ');
331 if(space) *space = '\0'; // Remove characters after the ' '
335 printf("Client %i authenticating as '%s'\n", Client->ID, Args);
339 free(Client->Username);
340 Client->Username = strdup(Args);
343 // Create a salt (that changes if the username is changed)
344 // Yes, I know, I'm a little paranoid, but who isn't?
345 Client->Salt[0] = 0x21 + (rand()&0x3F);
346 Client->Salt[1] = 0x21 + (rand()&0x3F);
347 Client->Salt[2] = 0x21 + (rand()&0x3F);
348 Client->Salt[3] = 0x21 + (rand()&0x3F);
349 Client->Salt[4] = 0x21 + (rand()&0x3F);
350 Client->Salt[5] = 0x21 + (rand()&0x3F);
351 Client->Salt[6] = 0x21 + (rand()&0x3F);
352 Client->Salt[7] = 0x21 + (rand()&0x3F);
354 // TODO: Also send hash type to use, (SHA1 or crypt according to [DAA])
355 sendf(Client->Socket, "100 SALT %s\n", Client->Salt);
357 sendf(Client->Socket, "100 User Set\n");
362 * \brief Authenticate as a user
366 void Server_Cmd_PASS(tClient *Client, char *Args)
368 char *space = strchr(Args, ' ');
369 if(space) *space = '\0'; // Remove characters after the ' '
371 // Pass on to cokebank
372 Client->UID = Bank_GetUserAuth(Client->Salt, Client->Username, Args);
374 if( Client->UID != -1 ) {
375 Client->bIsAuthed = 1;
376 sendf(Client->Socket, "200 Auth OK\n");
380 sendf(Client->Socket, "401 Auth Failure\n");
384 * \brief Authenticate as a user without a password
386 * Usage: AUTOAUTH <user>
388 void Server_Cmd_AUTOAUTH(tClient *Client, char *Args)
390 char *space = strchr(Args, ' ');
391 if(space) *space = '\0'; // Remove characters after the ' '
394 if( !Client->bIsTrusted ) {
396 printf("Client %i: Untrusted client attempting to AUTOAUTH\n", Client->ID);
397 sendf(Client->Socket, "401 Untrusted\n");
402 Client->UID = Bank_GetAcctByName( Args );
403 if( Client->UID < 0 ) {
405 printf("Client %i: Unknown user '%s'\n", Client->ID, Args);
406 sendf(Client->Socket, "401 Auth Failure\n");
410 // You can't be an internal account
411 if( Bank_GetFlags(Client->UID) & USER_FLAG_INTERNAL ) {
413 sendf(Client->Socket, "401 Auth Failure\n");
418 printf("Client %i: Authenticated as '%s' (%i)\n", Client->ID, Args, Client->UID);
420 sendf(Client->Socket, "200 Auth OK\n");
424 * \brief Set effective user
426 void Server_Cmd_SETEUSER(tClient *Client, char *Args)
430 space = strchr(Args, ' ');
432 if(space) *space = '\0';
434 if( !strlen(Args) ) {
435 sendf(Client->Socket, "407 SETEUSER expects an argument\n");
439 // Check user permissions
440 if( !(Bank_GetFlags(Client->UID) & USER_FLAG_COKE) ) {
441 sendf(Client->Socket, "403 Not in coke\n");
446 Client->EffectiveUID = Bank_GetAcctByName(Args);
447 if( Client->EffectiveUID == -1 ) {
448 sendf(Client->Socket, "404 User not found\n");
452 // You can't be an internal account
453 if( Bank_GetFlags(Client->EffectiveUID) & USER_FLAG_INTERNAL ) {
454 Client->EffectiveUID = -1;
455 sendf(Client->Socket, "404 User not found\n");
459 sendf(Client->Socket, "200 User set\n");
463 * \brief Enumerate the items that the server knows about
465 void Server_Cmd_ENUMITEMS(tClient *Client, char *Args)
469 if( Args != NULL && strlen(Args) ) {
470 sendf(Client->Socket, "407 ENUM_ITEMS takes no arguments\n");
474 sendf(Client->Socket, "201 Items %i\n", giNumItems);
476 for( i = 0; i < giNumItems; i ++ ) {
477 sendf(Client->Socket,
478 "202 Item %s:%i %i %s\n",
479 gaItems[i].Handler->Name, gaItems[i].ID, gaItems[i].Price, gaItems[i].Name
483 sendf(Client->Socket, "200 List end\n");
486 tItem *_GetItemFromString(char *String)
490 char *colon = strchr(String, ':');
502 for( i = 0; i < giNumHandlers; i ++ )
504 if( strcmp(gaHandlers[i]->Name, type) == 0) {
505 handler = gaHandlers[i];
514 for( i = 0; i < giNumItems; i ++ )
516 if( gaItems[i].Handler != handler ) continue;
517 if( gaItems[i].ID != num ) continue;
524 * \brief Fetch information on a specific item
526 void Server_Cmd_ITEMINFO(tClient *Client, char *Args)
528 tItem *item = _GetItemFromString(Args);
531 sendf(Client->Socket, "406 Bad Item ID\n");
535 sendf(Client->Socket,
536 "202 Item %s:%i %i %s\n",
537 item->Handler->Name, item->ID, item->Price, item->Name
541 void Server_Cmd_DISPENSE(tClient *Client, char *Args)
547 if( !Client->bIsAuthed ) {
548 sendf(Client->Socket, "401 Not Authenticated\n");
552 item = _GetItemFromString(Args);
554 sendf(Client->Socket, "406 Bad Item ID\n");
558 if( Client->EffectiveUID != -1 ) {
559 uid = Client->EffectiveUID;
565 switch( ret = DispenseItem( Client->UID, uid, item ) )
567 case 0: sendf(Client->Socket, "200 Dispense OK\n"); return ;
568 case 1: sendf(Client->Socket, "501 Unable to dispense\n"); return ;
569 case 2: sendf(Client->Socket, "402 Poor You\n"); return ;
571 sendf(Client->Socket, "500 Dispense Error\n");
576 void Server_Cmd_GIVE(tClient *Client, char *Args)
578 char *recipient, *ammount, *reason;
582 if( !Client->bIsAuthed ) {
583 sendf(Client->Socket, "401 Not Authenticated\n");
589 ammount = strchr(Args, ' ');
591 sendf(Client->Socket, "407 Invalid Argument, expected 3 parameters, 1 encountered\n");
597 reason = strchr(ammount, ' ');
599 sendf(Client->Socket, "407 Invalid Argument, expected 3 parameters, 2 encountered\n");
606 uid = Bank_GetAcctByName(recipient);
608 sendf(Client->Socket, "404 Invalid target user\n");
612 // You can't alter an internal account
613 if( Bank_GetFlags(uid) & USER_FLAG_INTERNAL ) {
614 sendf(Client->Socket, "404 Invalid target user\n");
619 iAmmount = atoi(ammount);
620 if( iAmmount <= 0 ) {
621 sendf(Client->Socket, "407 Invalid Argument, ammount must be > zero\n");
625 if( Client->EffectiveUID != -1 ) {
626 thisUid = Client->EffectiveUID;
629 thisUid = Client->UID;
633 switch( DispenseGive(Client->UID, thisUid, uid, iAmmount, reason) )
636 sendf(Client->Socket, "200 Give OK\n");
639 sendf(Client->Socket, "402 Poor You\n");
642 sendf(Client->Socket, "500 Unknown error\n");
647 void Server_Cmd_DONATE(tClient *Client, char *Args)
649 char *ammount, *reason;
653 if( !Client->bIsAuthed ) {
654 sendf(Client->Socket, "401 Not Authenticated\n");
660 // Get the start of the reason
661 reason = strchr(Args, ' ');
663 sendf(Client->Socket, "407 Invalid Argument, expected 2 parameters, 1 encountered\n");
669 // Check the end of the reason
670 if( strchr(reason, ' ') ) {
671 sendf(Client->Socket, "407 Invalid Argument, expected 2 parameters, more encountered\n");
676 iAmmount = atoi(ammount);
677 if( iAmmount <= 0 ) {
678 sendf(Client->Socket, "407 Invalid Argument, ammount must be > zero\n");
682 // Handle effective users
683 if( Client->EffectiveUID != -1 ) {
684 thisUid = Client->EffectiveUID;
687 thisUid = Client->UID;
691 switch( DispenseDonate(Client->UID, thisUid, iAmmount, reason) )
694 sendf(Client->Socket, "200 Give OK\n");
697 sendf(Client->Socket, "402 Poor You\n");
700 sendf(Client->Socket, "500 Unknown error\n");
705 void Server_Cmd_ADD(tClient *Client, char *Args)
707 char *user, *ammount, *reason;
710 if( !Client->bIsAuthed ) {
711 sendf(Client->Socket, "401 Not Authenticated\n");
717 ammount = strchr(Args, ' ');
719 sendf(Client->Socket, "407 Invalid Argument, expected 3 parameters, 1 encountered\n");
725 reason = strchr(ammount, ' ');
727 sendf(Client->Socket, "407 Invalid Argument, expected 3 parameters, 2 encountered\n");
733 // Check user permissions
734 if( !(Bank_GetFlags(Client->UID) & USER_FLAG_COKE) ) {
735 sendf(Client->Socket, "403 Not in coke\n");
740 uid = Bank_GetAcctByName(user);
742 sendf(Client->Socket, "404 Invalid user\n");
746 // You can't alter an internal account
747 if( Bank_GetFlags(uid) & USER_FLAG_INTERNAL ) {
748 sendf(Client->Socket, "404 Invalid user\n");
753 iAmmount = atoi(ammount);
754 if( iAmmount == 0 && ammount[0] != '0' ) {
755 sendf(Client->Socket, "407 Invalid Argument\n");
760 switch( DispenseAdd(Client->UID, uid, iAmmount, reason) )
763 sendf(Client->Socket, "200 Add OK\n");
766 sendf(Client->Socket, "402 Poor Guy\n");
769 sendf(Client->Socket, "500 Unknown error\n");
774 void Server_Cmd_ENUMUSERS(tClient *Client, char *Args)
778 int maxBal = INT_MAX, minBal = INT_MIN;
779 int flagMask = 0, flagVal = 0;
780 int sort = BANK_ITFLAG_SORT_NAME;
781 time_t lastSeenAfter=0, lastSeenBefore=0;
783 int flags; // Iterator flags
784 int balValue; // Balance value for iterator
785 time_t timeValue; // Time value for iterator
788 if( Args && strlen(Args) )
790 char *space = Args, *type, *val;
795 space = strchr(space, ' ');
796 if(space) *space = '\0';
799 val = strchr(type, ':');
806 if( strcmp(type, "min_balance") == 0 ) {
810 else if( strcmp(type, "max_balance") == 0 ) {
814 else if( strcmp(type, "flags") == 0 ) {
815 if( Server_int_ParseFlags(Client, val, &flagMask, &flagVal) )
818 // - Last seen before timestamp
819 else if( strcmp(type, "last_seen_before") == 0 ) {
820 lastSeenAfter = atoll(val);
822 // - Last seen after timestamp
823 else if( strcmp(type, "last_seen_after") == 0 ) {
824 lastSeenAfter = atoll(val);
827 else if( strcmp(type, "sort") == 0 ) {
828 char *dash = strchr(val, '-');
833 if( strcmp(val, "name") == 0 ) {
834 sort = BANK_ITFLAG_SORT_NAME;
836 else if( strcmp(val, "balance") == 0 ) {
837 sort = BANK_ITFLAG_SORT_BAL;
839 else if( strcmp(val, "lastseen") == 0 ) {
840 sort = BANK_ITFLAG_SORT_LASTSEEN;
843 sendf(Client->Socket, "407 Unknown sort field ('%s')\n", val);
846 // Handle sort direction
848 if( strcmp(dash, "desc") == 0 ) {
849 sort |= BANK_ITFLAG_REVSORT;
852 sendf(Client->Socket, "407 Unknown sort direction '%s'\n", dash);
859 sendf(Client->Socket, "407 Unknown argument to ENUM_USERS '%s:%s'\n", type, val);
866 sendf(Client->Socket, "407 Unknown argument to ENUM_USERS '%s'\n", type);
872 *space = ' '; // Repair (to be nice)
874 while(*space == ' ') space ++;
880 if( maxBal != INT_MAX ) {
881 flags = sort|BANK_ITFLAG_MAXBALANCE;
884 else if( minBal != INT_MIN ) {
885 flags = sort|BANK_ITFLAG_MINBALANCE;
892 if( lastSeenBefore ) {
893 timeValue = lastSeenBefore;
894 flags |= BANK_ITFLAG_SEENBEFORE;
896 else if( lastSeenAfter ) {
897 timeValue = lastSeenAfter;
898 flags |= BANK_ITFLAG_SEENAFTER;
903 it = Bank_Iterator(flagMask, flagVal, flags, balValue, timeValue);
906 while( (i = Bank_IteratorNext(it)) != -1 )
908 int bal = Bank_GetBalance(i);
910 if( bal == INT_MIN ) continue;
912 if( bal < minBal ) continue;
913 if( bal > maxBal ) continue;
918 Bank_DelIterator(it);
921 sendf(Client->Socket, "201 Users %i\n", numRet);
925 it = Bank_Iterator(flagMask, flagVal, flags, balValue, timeValue);
927 while( (i = Bank_IteratorNext(it)) != -1 )
929 int bal = Bank_GetBalance(i);
931 if( bal == INT_MIN ) continue;
933 if( bal < minBal ) continue;
934 if( bal > maxBal ) continue;
936 _SendUserInfo(Client, i);
939 Bank_DelIterator(it);
941 sendf(Client->Socket, "200 List End\n");
944 void Server_Cmd_USERINFO(tClient *Client, char *Args)
950 space = strchr(user, ' ');
951 if(space) *space = '\0';
954 uid = Bank_GetAcctByName(user);
956 sendf(Client->Socket, "404 Invalid user");
960 _SendUserInfo(Client, uid);
963 void _SendUserInfo(tClient *Client, int UserID)
965 char *type, *disabled="", *door="";
966 int flags = Bank_GetFlags(UserID);
968 if( flags & USER_FLAG_INTERNAL ) {
971 else if( flags & USER_FLAG_COKE ) {
972 if( flags & USER_FLAG_ADMIN )
977 else if( flags & USER_FLAG_ADMIN ) {
984 if( flags & USER_FLAG_DISABLED )
985 disabled = ",disabled";
986 if( flags & USER_FLAG_DOORGROUP )
989 // TODO: User flags/type
991 Client->Socket, "202 User %s %i %s%s\n",
992 Bank_GetAcctName(UserID), Bank_GetBalance(UserID),
997 void Server_Cmd_USERADD(tClient *Client, char *Args)
999 char *username, *space;
1001 // Check permissions
1002 if( !(Bank_GetFlags(Client->UID) & USER_FLAG_ADMIN) ) {
1003 sendf(Client->Socket, "403 Not a coke admin\n");
1009 while( *username == ' ' ) username ++;
1010 space = strchr(username, ' ');
1011 if(space) *space = '\0';
1013 // Try to create user
1014 if( Bank_CreateAcct(username) == -1 ) {
1015 sendf(Client->Socket, "404 User exists\n");
1020 char *thisName = Bank_GetAcctName(Client->UID);
1021 Log_Info("Account '%s' created by '%s'", username, thisName);
1025 sendf(Client->Socket, "200 User Added\n");
1028 void Server_Cmd_USERFLAGS(tClient *Client, char *Args)
1030 char *username, *flags;
1032 int mask=0, value=0;
1035 // Check permissions
1036 if( !(Bank_GetFlags(Client->UID) & USER_FLAG_ADMIN) ) {
1037 sendf(Client->Socket, "403 Not a coke admin\n");
1044 while( *username == ' ' ) username ++;
1045 space = strchr(username, ' ');
1047 sendf(Client->Socket, "407 USER_FLAGS requires 2 arguments, 1 given\n");
1053 while( *flags == ' ' ) flags ++;
1054 space = strchr(flags, ' ');
1055 if(space) *space = '\0';
1058 uid = Bank_GetAcctByName(username);
1060 sendf(Client->Socket, "404 User '%s' not found\n", username);
1065 if( Server_int_ParseFlags(Client, flags, &mask, &value) )
1069 Bank_SetFlags(uid, mask, value);
1072 sendf(Client->Socket, "200 User Updated\n");
1075 // --- INTERNAL HELPERS ---
1076 int sendf(int Socket, const char *Format, ...)
1081 va_start(args, Format);
1082 len = vsnprintf(NULL, 0, Format, args);
1087 va_start(args, Format);
1088 vsnprintf(buf, len+1, Format, args);
1091 #if DEBUG_TRACE_CLIENT
1092 printf("sendf: %s", buf);
1095 return send(Socket, buf, len, 0);
1099 int Server_int_ParseFlags(tClient *Client, const char *Str, int *Mask, int *Value)
1106 {"disabled", USER_FLAG_DISABLED, USER_FLAG_DISABLED}
1107 ,{"door", USER_FLAG_DOORGROUP, USER_FLAG_DOORGROUP}
1108 ,{"coke", USER_FLAG_COKE, USER_FLAG_COKE}
1109 ,{"admin", USER_FLAG_ADMIN, USER_FLAG_ADMIN}
1110 ,{"internal", USER_FLAG_INTERNAL, USER_FLAG_INTERNAL}
1112 const int ciNumFlags = sizeof(cFLAGS)/sizeof(cFLAGS[0]);
1124 while( *Str == ' ' ) Str ++; // Eat whitespace
1125 space = strchr(Str, ','); // Find the end of the flag
1131 // Check for inversion/removal
1132 if( *Str == '!' || *Str == '-' ) {
1136 else if( *Str == '+' ) {
1140 // Check flag values
1141 for( i = 0; i < ciNumFlags; i ++ )
1143 if( strncmp(Str, cFLAGS[i].Name, len) == 0 ) {
1144 *Mask |= cFLAGS[i].Mask;
1145 *Value &= ~cFLAGS[i].Mask;
1147 *Value |= cFLAGS[i].Value;
1153 if( i == ciNumFlags ) {
1155 strncpy(val, Str, len+1);
1156 sendf(Client->Socket, "407 Unknown flag value '%s'\n", val);