Added coke group check to ADD
[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 // HACKS
22 #define HACK_TPG_NOAUTH 1
23 #define HACK_ROOT_NOAUTH        1
24
25 #define DEBUG_TRACE_CLIENT      0
26
27 // Statistics
28 #define MAX_CONNECTION_QUEUE    5
29 #define INPUT_BUFFER_SIZE       256
30
31 #define HASH_TYPE       SHA1
32 #define HASH_LENGTH     20
33
34 #define MSG_STR_TOO_LONG        "499 Command too long (limit "EXPSTR(INPUT_BUFFER_SIZE)")\n"
35
36 // === TYPES ===
37 typedef struct sClient
38 {
39          int    Socket; // Client socket ID
40          int    ID;     // Client ID
41          
42          int    bIsTrusted;     // Is the connection from a trusted host/port
43         
44         char    *Username;
45         char    Salt[9];
46         
47          int    UID;
48          int    bIsAuthed;
49 }       tClient;
50
51 // === PROTOTYPES ===
52 void    Server_Start(void);
53 void    Server_Cleanup(void);
54 void    Server_HandleClient(int Socket, int bTrusted);
55 void    Server_ParseClientCommand(tClient *Client, char *CommandString);
56 // --- Commands ---
57 void    Server_Cmd_USER(tClient *Client, char *Args);
58 void    Server_Cmd_PASS(tClient *Client, char *Args);
59 void    Server_Cmd_AUTOAUTH(tClient *Client, char *Args);
60 void    Server_Cmd_ENUMITEMS(tClient *Client, char *Args);
61 void    Server_Cmd_ITEMINFO(tClient *Client, char *Args);
62 void    Server_Cmd_DISPENSE(tClient *Client, char *Args);
63 void    Server_Cmd_GIVE(tClient *Client, char *Args);
64 void    Server_Cmd_ADD(tClient *Client, char *Args);
65 void    Server_Cmd_ENUMUSERS(tClient *Client, char *Args);
66 void    Server_Cmd_USERINFO(tClient *Client, char *Args);
67 void    _SendUserInfo(tClient *Client, int UserID);
68 // --- Helpers ---
69  int    sendf(int Socket, const char *Format, ...);
70  int    GetUserAuth(const char *Salt, const char *Username, const uint8_t *Hash);
71 void    HexBin(uint8_t *Dest, char *Src, int BufSize);
72
73 // === GLOBALS ===
74  int    giServer_Port = 1020;
75  int    giServer_NextClientID = 1;
76 // - Commands
77 struct sClientCommand {
78         char    *Name;
79         void    (*Function)(tClient *Client, char *Arguments);
80 }       gaServer_Commands[] = {
81         {"USER", Server_Cmd_USER},
82         {"PASS", Server_Cmd_PASS},
83         {"AUTOAUTH", Server_Cmd_AUTOAUTH},
84         {"ENUM_ITEMS", Server_Cmd_ENUMITEMS},
85         {"ITEM_INFO", Server_Cmd_ITEMINFO},
86         {"DISPENSE", Server_Cmd_DISPENSE},
87         {"GIVE", Server_Cmd_GIVE},
88         {"ADD", Server_Cmd_ADD},
89         {"ENUM_USERS", Server_Cmd_ENUMUSERS},
90         {"USER_INFO", Server_Cmd_USERINFO}
91 };
92 #define NUM_COMMANDS    (sizeof(gaServer_Commands)/sizeof(gaServer_Commands[0]))
93  int    giServer_Socket;
94
95 // === CODE ===
96 /**
97  * \brief Open listenting socket and serve connections
98  */
99 void Server_Start(void)
100 {
101          int    client_socket;
102         struct sockaddr_in      server_addr, client_addr;
103
104         atexit(Server_Cleanup);
105
106         // Create Server
107         giServer_Socket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
108         if( giServer_Socket < 0 ) {
109                 fprintf(stderr, "ERROR: Unable to create server socket\n");
110                 return ;
111         }
112         
113         // Make listen address
114         memset(&server_addr, 0, sizeof(server_addr));
115         server_addr.sin_family = AF_INET;       // Internet Socket
116         server_addr.sin_addr.s_addr = htonl(INADDR_ANY);        // Listen on all interfaces
117         server_addr.sin_port = htons(giServer_Port);    // Port
118
119         // Bind
120         if( bind(giServer_Socket, (struct sockaddr *) &server_addr, sizeof(server_addr)) < 0 ) {
121                 fprintf(stderr, "ERROR: Unable to bind to 0.0.0.0:%i\n", giServer_Port);
122                 perror("Binding");
123                 return ;
124         }
125         
126         // Listen
127         if( listen(giServer_Socket, MAX_CONNECTION_QUEUE) < 0 ) {
128                 fprintf(stderr, "ERROR: Unable to listen to socket\n");
129                 perror("Listen");
130                 return ;
131         }
132         
133         printf("Listening on 0.0.0.0:%i\n", giServer_Port);
134         
135         for(;;)
136         {
137                 uint    len = sizeof(client_addr);
138                  int    bTrusted = 0;
139                 
140                 client_socket = accept(giServer_Socket, (struct sockaddr *) &client_addr, &len);
141                 if(client_socket < 0) {
142                         fprintf(stderr, "ERROR: Unable to accept client connection\n");
143                         return ;
144                 }
145                 
146                 if(giDebugLevel >= 2) {
147                         char    ipstr[INET_ADDRSTRLEN];
148                         inet_ntop(AF_INET, &client_addr.sin_addr, ipstr, INET_ADDRSTRLEN);
149                         printf("Client connection from %s:%i\n",
150                                 ipstr, ntohs(client_addr.sin_port));
151                 }
152                 
153                 // Trusted Connections
154                 if( ntohs(client_addr.sin_port) < 1024 )
155                 {
156                         // TODO: Make this runtime configurable
157                         switch( ntohl( client_addr.sin_addr.s_addr ) )
158                         {
159                         case 0x7F000001:        // 127.0.0.1    localhost
160                         //case 0x825E0D00:      // 130.95.13.0
161                         case 0x825E0D12:        // 130.95.13.18 mussel
162                         case 0x825E0D17:        // 130.95.13.23 martello
163                                 bTrusted = 1;
164                                 break;
165                         default:
166                                 break;
167                         }
168                 }
169                 
170                 // TODO: Multithread this?
171                 Server_HandleClient(client_socket, bTrusted);
172                 
173                 close(client_socket);
174         }
175 }
176
177 void Server_Cleanup(void)
178 {
179         printf("Close(%i)\n", giServer_Socket);
180         close(giServer_Socket);
181 }
182
183 /**
184  * \brief Reads from a client socket and parses the command strings
185  * \param Socket        Client socket number/handle
186  * \param bTrusted      Is the client trusted?
187  */
188 void Server_HandleClient(int Socket, int bTrusted)
189 {
190         char    inbuf[INPUT_BUFFER_SIZE];
191         char    *buf = inbuf;
192          int    remspace = INPUT_BUFFER_SIZE-1;
193          int    bytes = -1;
194         tClient clientInfo = {0};
195         
196         // Initialise Client info
197         clientInfo.Socket = Socket;
198         clientInfo.ID = giServer_NextClientID ++;
199         clientInfo.bIsTrusted = bTrusted;
200         
201         // Read from client
202         /*
203          * Notes:
204          * - The `buf` and `remspace` variables allow a line to span several
205          *   calls to recv(), if a line is not completed in one recv() call
206          *   it is saved to the beginning of `inbuf` and `buf` is updated to
207          *   the end of it.
208          */
209         while( (bytes = recv(Socket, buf, remspace, 0)) > 0 )
210         {
211                 char    *eol, *start;
212                 buf[bytes] = '\0';      // Allow us to use stdlib string functions on it
213                 
214                 // Split by lines
215                 start = inbuf;
216                 while( (eol = strchr(start, '\n')) )
217                 {
218                         *eol = '\0';
219                         
220                         Server_ParseClientCommand(&clientInfo, start);
221                         
222                         start = eol + 1;
223                 }
224                 
225                 // Check if there was an incomplete line
226                 if( *start != '\0' ) {
227                          int    tailBytes = bytes - (start-buf);
228                         // Roll back in buffer
229                         memcpy(inbuf, start, tailBytes);
230                         remspace -= tailBytes;
231                         if(remspace == 0) {
232                                 send(Socket, MSG_STR_TOO_LONG, sizeof(MSG_STR_TOO_LONG), 0);
233                                 buf = inbuf;
234                                 remspace = INPUT_BUFFER_SIZE - 1;
235                         }
236                 }
237                 else {
238                         buf = inbuf;
239                         remspace = INPUT_BUFFER_SIZE - 1;
240                 }
241         }
242         
243         // Check for errors
244         if( bytes < 0 ) {
245                 fprintf(stderr, "ERROR: Unable to recieve from client on socket %i\n", Socket);
246                 return ;
247         }
248         
249         if(giDebugLevel >= 2) {
250                 printf("Client %i: Disconnected\n", clientInfo.ID);
251         }
252 }
253
254 /**
255  * \brief Parses a client command and calls the required helper function
256  * \param Client        Pointer to client state structure
257  * \param CommandString Command from client (single line of the command)
258  * \return Heap String to return to the client
259  */
260 void Server_ParseClientCommand(tClient *Client, char *CommandString)
261 {
262         char    *space, *args;
263          int    i;
264         
265         // Split at first space
266         space = strchr(CommandString, ' ');
267         if(space == NULL) {
268                 args = NULL;
269         }
270         else {
271                 *space = '\0';
272                 args = space + 1;
273         }
274         
275         // Find command
276         for( i = 0; i < NUM_COMMANDS; i++ )
277         {
278                 if(strcmp(CommandString, gaServer_Commands[i].Name) == 0) {
279                         gaServer_Commands[i].Function(Client, args);
280                         return ;
281                 }
282         }
283         
284         sendf(Client->Socket, "400 Unknown Command\n");
285 }
286
287 // ---
288 // Commands
289 // ---
290 /**
291  * \brief Set client username
292  * 
293  * Usage: USER <username>
294  */
295 void Server_Cmd_USER(tClient *Client, char *Args)
296 {       
297         // Debug!
298         if( giDebugLevel )
299                 printf("Client %i authenticating as '%s'\n", Client->ID, Args);
300         
301         // Save username
302         if(Client->Username)
303                 free(Client->Username);
304         Client->Username = strdup(Args);
305         
306         #if USE_SALT
307         // Create a salt (that changes if the username is changed)
308         // Yes, I know, I'm a little paranoid, but who isn't?
309         Client->Salt[0] = 0x21 + (rand()&0x3F);
310         Client->Salt[1] = 0x21 + (rand()&0x3F);
311         Client->Salt[2] = 0x21 + (rand()&0x3F);
312         Client->Salt[3] = 0x21 + (rand()&0x3F);
313         Client->Salt[4] = 0x21 + (rand()&0x3F);
314         Client->Salt[5] = 0x21 + (rand()&0x3F);
315         Client->Salt[6] = 0x21 + (rand()&0x3F);
316         Client->Salt[7] = 0x21 + (rand()&0x3F);
317         
318         // TODO: Also send hash type to use, (SHA1 or crypt according to [DAA])
319         sendf(Client->Socket, "100 SALT %s\n", Client->Salt);
320         #else
321         sendf(Client->Socket, "100 User Set\n");
322         #endif
323 }
324
325 /**
326  * \brief Authenticate as a user
327  * 
328  * Usage: PASS <hash>
329  */
330 void Server_Cmd_PASS(tClient *Client, char *Args)
331 {
332         uint8_t clienthash[HASH_LENGTH] = {0};
333         
334         // Read user's hash
335         HexBin(clienthash, Args, HASH_LENGTH);
336         
337         // TODO: Decrypt password passed
338         
339         Client->UID = GetUserAuth(Client->Salt, Client->Username, clienthash);
340
341         if( Client->UID != -1 ) {
342                 Client->bIsAuthed = 1;
343                 sendf(Client->Socket, "200 Auth OK\n");
344                 return ;
345         }
346
347         if( giDebugLevel ) {
348                  int    i;
349                 printf("Client %i: Password hash ", Client->ID);
350                 for(i=0;i<HASH_LENGTH;i++)
351                         printf("%02x", clienthash[i]&0xFF);
352                 printf("\n");
353         }
354         
355         sendf(Client->Socket, "401 Auth Failure\n");
356 }
357
358 /**
359  * \brief Authenticate as a user without a password
360  * 
361  * Usage: AUTOAUTH <user>
362  */
363 void Server_Cmd_AUTOAUTH(tClient *Client, char *Args)
364 {
365         char    *spos = strchr(Args, ' ');
366         if(spos)        *spos = '\0';   // Remove characters after the ' '
367         
368         // Check if trusted
369         if( !Client->bIsTrusted ) {
370                 if(giDebugLevel)
371                         printf("Client %i: Untrusted client attempting to AUTOAUTH\n", Client->ID);
372                 sendf(Client->Socket, "401 Untrusted\n");
373                 return ;
374         }
375         
376         // Get UID
377         Client->UID = GetUserID( Args );
378         if( Client->UID < 0 ) {
379                 if(giDebugLevel)
380                         printf("Client %i: Unknown user '%s'\n", Client->ID, Args);
381                 sendf(Client->Socket, "401 Auth Failure\n");
382                 return ;
383         }
384         
385         if(giDebugLevel)
386                 printf("Client %i: Authenticated as '%s' (%i)\n", Client->ID, Args, Client->UID);
387         
388         sendf(Client->Socket, "200 Auth OK\n");
389 }
390
391 /**
392  * \brief Enumerate the items that the server knows about
393  */
394 void Server_Cmd_ENUMITEMS(tClient *Client, char *Args)
395 {
396          int    i;
397
398         sendf(Client->Socket, "201 Items %i", giNumItems);
399
400         for( i = 0; i < giNumItems; i ++ ) {
401                 sendf(Client->Socket, " %s:%i", gaItems[i].Handler->Name, gaItems[i].ID);
402         }
403
404         sendf(Client->Socket, "\n");
405 }
406
407 tItem *_GetItemFromString(char *String)
408 {
409         tHandler        *handler;
410         char    *type = String;
411         char    *colon = strchr(String, ':');
412          int    num, i;
413         
414         if( !colon ) {
415                 return NULL;
416         }
417
418         num = atoi(colon+1);
419         *colon = '\0';
420
421         // Find handler
422         handler = NULL;
423         for( i = 0; i < giNumHandlers; i ++ )
424         {
425                 if( strcmp(gaHandlers[i]->Name, type) == 0) {
426                         handler = gaHandlers[i];
427                         break;
428                 }
429         }
430         if( !handler ) {
431                 return NULL;
432         }
433
434         // Find item
435         for( i = 0; i < giNumItems; i ++ )
436         {
437                 if( gaItems[i].Handler != handler )     continue;
438                 if( gaItems[i].ID != num )      continue;
439                 return &gaItems[i];
440         }
441         return NULL;
442 }
443
444 /**
445  * \brief Fetch information on a specific item
446  */
447 void Server_Cmd_ITEMINFO(tClient *Client, char *Args)
448 {
449         tItem   *item = _GetItemFromString(Args);
450         
451         if( !item ) {
452                 sendf(Client->Socket, "406 Bad Item ID\n");
453                 return ;
454         }
455         
456         sendf(Client->Socket,
457                 "202 Item %s:%i %i %s\n",
458                  item->Handler->Name, item->ID, item->Price, item->Name
459                  );
460 }
461
462 void Server_Cmd_DISPENSE(tClient *Client, char *Args)
463 {
464         tItem   *item;
465          int    ret;
466         if( !Client->bIsAuthed ) {
467                 sendf(Client->Socket, "401 Not Authenticated\n");
468                 return ;
469         }
470
471         item = _GetItemFromString(Args);
472         if( !item ) {
473                 sendf(Client->Socket, "406 Bad Item ID\n");
474                 return ;
475         }
476
477         switch( ret = DispenseItem( Client->UID, item ) )
478         {
479         case 0: sendf(Client->Socket, "200 Dispense OK\n");     return ;
480         case 1: sendf(Client->Socket, "501 Unable to dispense\n");      return ;
481         case 2: sendf(Client->Socket, "402 Poor You\n");        return ;
482         default:
483                 sendf(Client->Socket, "500 Dispense Error\n");
484                 return ;
485         }
486 }
487
488 void Server_Cmd_GIVE(tClient *Client, char *Args)
489 {
490         char    *recipient, *ammount, *reason;
491          int    uid, iAmmount;
492         
493         if( !Client->bIsAuthed ) {
494                 sendf(Client->Socket, "401 Not Authenticated\n");
495                 return ;
496         }
497
498         recipient = Args;
499
500         ammount = strchr(Args, ' ');
501         if( !ammount ) {
502                 sendf(Client->Socket, "407 Invalid Argument, expected 3 parameters, 1 encountered\n");
503                 return ;
504         }
505         *ammount = '\0';
506         ammount ++;
507
508         reason = strchr(ammount, ' ');
509         if( !reason ) {
510                 sendf(Client->Socket, "407 Invalid Argument, expected 3 parameters, 2 encountered\n");
511                 return ;
512         }
513         *reason = '\0';
514         reason ++;
515
516         // Get recipient
517         uid = GetUserID(recipient);
518         if( uid == -1 ) {
519                 sendf(Client->Socket, "404 Invalid target user\n");
520                 return ;
521         }
522
523         // Parse ammount
524         iAmmount = atoi(ammount);
525         if( iAmmount <= 0 ) {
526                 sendf(Client->Socket, "407 Invalid Argument, ammount must be > zero\n");
527                 return ;
528         }
529
530         // Do give
531         switch( DispenseGive(Client->UID, uid, iAmmount, reason) )
532         {
533         case 0:
534                 sendf(Client->Socket, "200 Give OK\n");
535                 return ;
536         case 2:
537                 sendf(Client->Socket, "402 Poor You\n");
538                 return ;
539         default:
540                 sendf(Client->Socket, "500 Unknown error\n");
541                 return ;
542         }
543 }
544
545 void Server_Cmd_ADD(tClient *Client, char *Args)
546 {
547         char    *user, *ammount, *reason;
548          int    uid, iAmmount;
549         
550         if( !Client->bIsAuthed ) {
551                 sendf(Client->Socket, "401 Not Authenticated\n");
552                 return ;
553         }
554
555         user = Args;
556
557         ammount = strchr(Args, ' ');
558         if( !ammount ) {
559                 sendf(Client->Socket, "407 Invalid Argument, expected 3 parameters, 1 encountered\n");
560                 return ;
561         }
562         *ammount = '\0';
563         ammount ++;
564
565         reason = strchr(ammount, ' ');
566         if( !reason ) {
567                 sendf(Client->Socket, "407 Invalid Argument, expected 3 parameters, 2 encountered\n");
568                 return ;
569         }
570         *reason = '\0';
571         reason ++;
572
573         // TODO: Check if the current user is in coke/higher
574         if( (GetFlags(Client->UID) & USER_FLAG_TYPEMASK) < USER_TYPE_COKE ) {
575                 sendf(Client->Socket, "403 Not in coke\n");
576                 return ;
577         }
578
579         // Get recipient
580         uid = GetUserID(user);
581         if( uid == -1 ) {
582                 sendf(Client->Socket, "404 Invalid user\n");
583                 return ;
584         }
585
586         // Parse ammount
587         iAmmount = atoi(ammount);
588         if( iAmmount == 0 && ammount[0] != '0' ) {
589                 sendf(Client->Socket, "407 Invalid Argument\n");
590                 return ;
591         }
592
593         // Do give
594         switch( DispenseAdd(uid, Client->UID, iAmmount, reason) )
595         {
596         case 0:
597                 sendf(Client->Socket, "200 Add OK\n");
598                 return ;
599         case 2:
600                 sendf(Client->Socket, "402 Poor Guy\n");
601                 return ;
602         default:
603                 sendf(Client->Socket, "500 Unknown error\n");
604                 return ;
605         }
606 }
607
608 void Server_Cmd_ENUMUSERS(tClient *Client, char *Args)
609 {
610          int    i, numRet = 0;
611          int    maxBal = INT_MAX, minBal = INT_MIN;
612          int    numUsr = GetMaxID();
613         
614         // Parse arguments
615         //minBal = atoi(Args);
616         
617         // Get return number
618         for( i = 0; i < numUsr; i ++ )
619         {
620                 int bal = GetBalance(i);
621                 
622                 if( bal == INT_MIN )    continue;
623                 
624                 if( bal < minBal )      continue;
625                 if( bal > maxBal )      continue;
626                 
627                 numRet ++;
628         }
629         
630         // Send count
631         sendf(Client->Socket, "201 Users %i\n", numRet);
632         
633         for( i = 0; i < numUsr; i ++ )
634         {
635                 int bal = GetBalance(i);
636                 
637                 if( bal == INT_MIN )    continue;
638                 
639                 if( bal < minBal )      continue;
640                 if( bal > maxBal )      continue;
641                 
642                 // TODO: User flags
643                 _SendUserInfo(Client, i);
644         }
645         
646         sendf(Client->Socket, "200 List End\n");
647 }
648
649 void Server_Cmd_USERINFO(tClient *Client, char *Args)
650 {
651          int    uid;
652         char    *user = Args;
653         char    *space;
654         
655         space = strchr(user, ' ');
656         if(space)       *space = '\0';
657         
658         // Get recipient
659         uid = GetUserID(user);
660         if( uid == -1 ) {
661                 sendf(Client->Socket, "404 Invalid user");
662                 return ;
663         }
664         
665         _SendUserInfo(Client, uid);
666 }
667
668 void _SendUserInfo(tClient *Client, int UserID)
669 {
670         char    *type, *disabled="";
671          int    flags = GetFlags(UserID);
672         
673         switch( flags & USER_FLAG_TYPEMASK )
674         {
675         default:
676         case USER_TYPE_NORMAL:  type = "user";  break;
677         case USER_TYPE_COKE:    type = "coke";  break;
678         case USER_TYPE_WHEEL:   type = "wheel"; break;
679         case USER_TYPE_GOD:     type = "meta";  break;
680         }
681         
682         if( flags & USER_FLAG_DISABLED )
683                 disabled = ",disabled";
684         
685         // TODO: User flags/type
686         sendf(
687                 Client->Socket, "202 User %s %i %s%s\n",
688                 GetUserName(UserID), GetBalance(UserID),
689                 type, disabled
690                 );
691 }
692
693 /**
694  * \brief Authenticate a user
695  * \return User ID, or -1 if authentication failed
696  */
697 int GetUserAuth(const char *Salt, const char *Username, const uint8_t *ProvidedHash)
698 {
699         #if 0
700         uint8_t h[20];
701          int    ofs = strlen(Username) + strlen(Salt);
702         char    input[ ofs + 40 + 1];
703         char    tmp[4 + strlen(Username) + 1];  // uid=%s
704         #endif
705         
706         #if HACK_TPG_NOAUTH
707         if( strcmp(Username, "tpg") == 0 )
708                 return GetUserID("tpg");
709         #endif
710         #if HACK_ROOT_NOAUTH
711         if( strcmp(Username, "root") == 0 )
712                 return GetUserID("root");
713         #endif
714         
715         #if 0
716         //
717         strcpy(input, Username);
718         strcpy(input, Salt);
719         // TODO: Get user's SHA-1 hash
720         sprintf(tmp, "uid=%s", Username);
721         ldap_search_s(ld, "", LDAP_SCOPE_BASE, tmp, "userPassword", 0, res);
722         
723         sprintf(input+ofs, "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
724                 h[ 0], h[ 1], h[ 2], h[ 3], h[ 4], h[ 5], h[ 6], h[ 7], h[ 8], h[ 9],
725                 h[10], h[11], h[12], h[13], h[14], h[15], h[16], h[17], h[18], h[19]
726                 );
727         // Then create the hash from the provided salt
728         // Compare that with the provided hash
729         #endif
730         
731         return -1;
732 }
733
734 // --- INTERNAL HELPERS ---
735 int sendf(int Socket, const char *Format, ...)
736 {
737         va_list args;
738          int    len;
739         
740         va_start(args, Format);
741         len = vsnprintf(NULL, 0, Format, args);
742         va_end(args);
743         
744         {
745                 char    buf[len+1];
746                 va_start(args, Format);
747                 vsnprintf(buf, len+1, Format, args);
748                 va_end(args);
749                 
750                 #if DEBUG_TRACE_CLIENT
751                 printf("sendf: %s", buf);
752                 #endif
753                 
754                 return send(Socket, buf, len, 0);
755         }
756 }
757
758 // TODO: Move to another file
759 void HexBin(uint8_t *Dest, char *Src, int BufSize)
760 {
761          int    i;
762         for( i = 0; i < BufSize; i ++ )
763         {
764                 uint8_t val = 0;
765                 
766                 if('0' <= *Src && *Src <= '9')
767                         val |= (*Src-'0') << 4;
768                 else if('A' <= *Src && *Src <= 'F')
769                         val |= (*Src-'A'+10) << 4;
770                 else if('a' <= *Src && *Src <= 'f')
771                         val |= (*Src-'a'+10) << 4;
772                 else
773                         break;
774                 Src ++;
775                 
776                 if('0' <= *Src && *Src <= '9')
777                         val |= (*Src-'0');
778                 else if('A' <= *Src && *Src <= 'F')
779                         val |= (*Src-'A'+10);
780                 else if('a' <= *Src && *Src <= 'f')
781                         val |= (*Src-'a'+10);
782                 else
783                         break;
784                 Src ++;
785                 
786                 Dest[i] = val;
787         }
788         for( ; i < BufSize; i++ )
789                 Dest[i] = 0;
790 }
791
792 /**
793  * \brief Decode a Base64 value
794  */
795 int UnBase64(uint8_t *Dest, char *Src, int BufSize)
796 {
797         uint32_t        val;
798          int    i, j;
799         char    *start_src = Src;
800         
801         for( i = 0; i+2 < BufSize; i += 3 )
802         {
803                 val = 0;
804                 for( j = 0; j < 4; j++, Src ++ ) {
805                         if('A' <= *Src && *Src <= 'Z')
806                                 val |= (*Src - 'A') << ((3-j)*6);
807                         else if('a' <= *Src && *Src <= 'z')
808                                 val |= (*Src - 'a' + 26) << ((3-j)*6);
809                         else if('0' <= *Src && *Src <= '9')
810                                 val |= (*Src - '0' + 52) << ((3-j)*6);
811                         else if(*Src == '+')
812                                 val |= 62 << ((3-j)*6);
813                         else if(*Src == '/')
814                                 val |= 63 << ((3-j)*6);
815                         else if(!*Src)
816                                 break;
817                         else if(*Src != '=')
818                                 j --;   // Ignore invalid characters
819                 }
820                 Dest[i  ] = (val >> 16) & 0xFF;
821                 Dest[i+1] = (val >> 8) & 0xFF;
822                 Dest[i+2] = val & 0xFF;
823                 if(j != 4)      break;
824         }
825         
826         // Finish things off
827         if(i   < BufSize)
828                 Dest[i] = (val >> 16) & 0xFF;
829         if(i+1 < BufSize)
830                 Dest[i+1] = (val >> 8) & 0xFF;
831         
832         return Src - start_src;
833 }

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