Changed ENUM_ITEMS to the ENUM_USERS format
[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\n", giNumItems);
399
400         for( i = 0; i < giNumItems; i ++ ) {
401                 sendf(Client->Socket,
402                         "202 Item %s:%i %i %s\n",
403                          gaItems[i].Handler->Name, gaItems[i].ID, gaItems[i].Price, gaItems[i].Name
404                          );
405         }
406
407         sendf(Client->Socket, "200 List end\n");
408 }
409
410 tItem *_GetItemFromString(char *String)
411 {
412         tHandler        *handler;
413         char    *type = String;
414         char    *colon = strchr(String, ':');
415          int    num, i;
416         
417         if( !colon ) {
418                 return NULL;
419         }
420
421         num = atoi(colon+1);
422         *colon = '\0';
423
424         // Find handler
425         handler = NULL;
426         for( i = 0; i < giNumHandlers; i ++ )
427         {
428                 if( strcmp(gaHandlers[i]->Name, type) == 0) {
429                         handler = gaHandlers[i];
430                         break;
431                 }
432         }
433         if( !handler ) {
434                 return NULL;
435         }
436
437         // Find item
438         for( i = 0; i < giNumItems; i ++ )
439         {
440                 if( gaItems[i].Handler != handler )     continue;
441                 if( gaItems[i].ID != num )      continue;
442                 return &gaItems[i];
443         }
444         return NULL;
445 }
446
447 /**
448  * \brief Fetch information on a specific item
449  */
450 void Server_Cmd_ITEMINFO(tClient *Client, char *Args)
451 {
452         tItem   *item = _GetItemFromString(Args);
453         
454         if( !item ) {
455                 sendf(Client->Socket, "406 Bad Item ID\n");
456                 return ;
457         }
458         
459         sendf(Client->Socket,
460                 "202 Item %s:%i %i %s\n",
461                  item->Handler->Name, item->ID, item->Price, item->Name
462                  );
463 }
464
465 void Server_Cmd_DISPENSE(tClient *Client, char *Args)
466 {
467         tItem   *item;
468          int    ret;
469         if( !Client->bIsAuthed ) {
470                 sendf(Client->Socket, "401 Not Authenticated\n");
471                 return ;
472         }
473
474         item = _GetItemFromString(Args);
475         if( !item ) {
476                 sendf(Client->Socket, "406 Bad Item ID\n");
477                 return ;
478         }
479
480         switch( ret = DispenseItem( Client->UID, item ) )
481         {
482         case 0: sendf(Client->Socket, "200 Dispense OK\n");     return ;
483         case 1: sendf(Client->Socket, "501 Unable to dispense\n");      return ;
484         case 2: sendf(Client->Socket, "402 Poor You\n");        return ;
485         default:
486                 sendf(Client->Socket, "500 Dispense Error\n");
487                 return ;
488         }
489 }
490
491 void Server_Cmd_GIVE(tClient *Client, char *Args)
492 {
493         char    *recipient, *ammount, *reason;
494          int    uid, iAmmount;
495         
496         if( !Client->bIsAuthed ) {
497                 sendf(Client->Socket, "401 Not Authenticated\n");
498                 return ;
499         }
500
501         recipient = Args;
502
503         ammount = strchr(Args, ' ');
504         if( !ammount ) {
505                 sendf(Client->Socket, "407 Invalid Argument, expected 3 parameters, 1 encountered\n");
506                 return ;
507         }
508         *ammount = '\0';
509         ammount ++;
510
511         reason = strchr(ammount, ' ');
512         if( !reason ) {
513                 sendf(Client->Socket, "407 Invalid Argument, expected 3 parameters, 2 encountered\n");
514                 return ;
515         }
516         *reason = '\0';
517         reason ++;
518
519         // Get recipient
520         uid = GetUserID(recipient);
521         if( uid == -1 ) {
522                 sendf(Client->Socket, "404 Invalid target user\n");
523                 return ;
524         }
525
526         // Parse ammount
527         iAmmount = atoi(ammount);
528         if( iAmmount <= 0 ) {
529                 sendf(Client->Socket, "407 Invalid Argument, ammount must be > zero\n");
530                 return ;
531         }
532
533         // Do give
534         switch( DispenseGive(Client->UID, uid, iAmmount, reason) )
535         {
536         case 0:
537                 sendf(Client->Socket, "200 Give OK\n");
538                 return ;
539         case 2:
540                 sendf(Client->Socket, "402 Poor You\n");
541                 return ;
542         default:
543                 sendf(Client->Socket, "500 Unknown error\n");
544                 return ;
545         }
546 }
547
548 void Server_Cmd_ADD(tClient *Client, char *Args)
549 {
550         char    *user, *ammount, *reason;
551          int    uid, iAmmount;
552         
553         if( !Client->bIsAuthed ) {
554                 sendf(Client->Socket, "401 Not Authenticated\n");
555                 return ;
556         }
557
558         user = Args;
559
560         ammount = strchr(Args, ' ');
561         if( !ammount ) {
562                 sendf(Client->Socket, "407 Invalid Argument, expected 3 parameters, 1 encountered\n");
563                 return ;
564         }
565         *ammount = '\0';
566         ammount ++;
567
568         reason = strchr(ammount, ' ');
569         if( !reason ) {
570                 sendf(Client->Socket, "407 Invalid Argument, expected 3 parameters, 2 encountered\n");
571                 return ;
572         }
573         *reason = '\0';
574         reason ++;
575
576         // TODO: Check if the current user is in coke/higher
577         if( (GetFlags(Client->UID) & USER_FLAG_TYPEMASK) < USER_TYPE_COKE ) {
578                 sendf(Client->Socket, "403 Not in coke\n");
579                 return ;
580         }
581
582         // Get recipient
583         uid = GetUserID(user);
584         if( uid == -1 ) {
585                 sendf(Client->Socket, "404 Invalid user\n");
586                 return ;
587         }
588
589         // Parse ammount
590         iAmmount = atoi(ammount);
591         if( iAmmount == 0 && ammount[0] != '0' ) {
592                 sendf(Client->Socket, "407 Invalid Argument\n");
593                 return ;
594         }
595
596         // Do give
597         switch( DispenseAdd(uid, Client->UID, iAmmount, reason) )
598         {
599         case 0:
600                 sendf(Client->Socket, "200 Add OK\n");
601                 return ;
602         case 2:
603                 sendf(Client->Socket, "402 Poor Guy\n");
604                 return ;
605         default:
606                 sendf(Client->Socket, "500 Unknown error\n");
607                 return ;
608         }
609 }
610
611 void Server_Cmd_ENUMUSERS(tClient *Client, char *Args)
612 {
613          int    i, numRet = 0;
614          int    maxBal = INT_MAX, minBal = INT_MIN;
615          int    numUsr = GetMaxID();
616         
617         // Parse arguments
618         //minBal = atoi(Args);
619         
620         // Get return number
621         for( i = 0; i < numUsr; i ++ )
622         {
623                 int bal = GetBalance(i);
624                 
625                 if( bal == INT_MIN )    continue;
626                 
627                 if( bal < minBal )      continue;
628                 if( bal > maxBal )      continue;
629                 
630                 numRet ++;
631         }
632         
633         // Send count
634         sendf(Client->Socket, "201 Users %i\n", numRet);
635         
636         for( i = 0; i < numUsr; i ++ )
637         {
638                 int bal = GetBalance(i);
639                 
640                 if( bal == INT_MIN )    continue;
641                 
642                 if( bal < minBal )      continue;
643                 if( bal > maxBal )      continue;
644                 
645                 // TODO: User flags
646                 _SendUserInfo(Client, i);
647         }
648         
649         sendf(Client->Socket, "200 List End\n");
650 }
651
652 void Server_Cmd_USERINFO(tClient *Client, char *Args)
653 {
654          int    uid;
655         char    *user = Args;
656         char    *space;
657         
658         space = strchr(user, ' ');
659         if(space)       *space = '\0';
660         
661         // Get recipient
662         uid = GetUserID(user);
663         if( uid == -1 ) {
664                 sendf(Client->Socket, "404 Invalid user");
665                 return ;
666         }
667         
668         _SendUserInfo(Client, uid);
669 }
670
671 void _SendUserInfo(tClient *Client, int UserID)
672 {
673         char    *type, *disabled="";
674          int    flags = GetFlags(UserID);
675         
676         switch( flags & USER_FLAG_TYPEMASK )
677         {
678         default:
679         case USER_TYPE_NORMAL:  type = "user";  break;
680         case USER_TYPE_COKE:    type = "coke";  break;
681         case USER_TYPE_WHEEL:   type = "wheel"; break;
682         case USER_TYPE_GOD:     type = "meta";  break;
683         }
684         
685         if( flags & USER_FLAG_DISABLED )
686                 disabled = ",disabled";
687         
688         // TODO: User flags/type
689         sendf(
690                 Client->Socket, "202 User %s %i %s%s\n",
691                 GetUserName(UserID), GetBalance(UserID),
692                 type, disabled
693                 );
694 }
695
696 /**
697  * \brief Authenticate a user
698  * \return User ID, or -1 if authentication failed
699  */
700 int GetUserAuth(const char *Salt, const char *Username, const uint8_t *ProvidedHash)
701 {
702         #if 0
703         uint8_t h[20];
704          int    ofs = strlen(Username) + strlen(Salt);
705         char    input[ ofs + 40 + 1];
706         char    tmp[4 + strlen(Username) + 1];  // uid=%s
707         #endif
708         
709         #if HACK_TPG_NOAUTH
710         if( strcmp(Username, "tpg") == 0 )
711                 return GetUserID("tpg");
712         #endif
713         #if HACK_ROOT_NOAUTH
714         if( strcmp(Username, "root") == 0 )
715                 return GetUserID("root");
716         #endif
717         
718         #if 0
719         //
720         strcpy(input, Username);
721         strcpy(input, Salt);
722         // TODO: Get user's SHA-1 hash
723         sprintf(tmp, "uid=%s", Username);
724         ldap_search_s(ld, "", LDAP_SCOPE_BASE, tmp, "userPassword", 0, res);
725         
726         sprintf(input+ofs, "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
727                 h[ 0], h[ 1], h[ 2], h[ 3], h[ 4], h[ 5], h[ 6], h[ 7], h[ 8], h[ 9],
728                 h[10], h[11], h[12], h[13], h[14], h[15], h[16], h[17], h[18], h[19]
729                 );
730         // Then create the hash from the provided salt
731         // Compare that with the provided hash
732         #endif
733         
734         return -1;
735 }
736
737 // --- INTERNAL HELPERS ---
738 int sendf(int Socket, const char *Format, ...)
739 {
740         va_list args;
741          int    len;
742         
743         va_start(args, Format);
744         len = vsnprintf(NULL, 0, Format, args);
745         va_end(args);
746         
747         {
748                 char    buf[len+1];
749                 va_start(args, Format);
750                 vsnprintf(buf, len+1, Format, args);
751                 va_end(args);
752                 
753                 #if DEBUG_TRACE_CLIENT
754                 printf("sendf: %s", buf);
755                 #endif
756                 
757                 return send(Socket, buf, len, 0);
758         }
759 }
760
761 // TODO: Move to another file
762 void HexBin(uint8_t *Dest, char *Src, int BufSize)
763 {
764          int    i;
765         for( i = 0; i < BufSize; i ++ )
766         {
767                 uint8_t val = 0;
768                 
769                 if('0' <= *Src && *Src <= '9')
770                         val |= (*Src-'0') << 4;
771                 else if('A' <= *Src && *Src <= 'F')
772                         val |= (*Src-'A'+10) << 4;
773                 else if('a' <= *Src && *Src <= 'f')
774                         val |= (*Src-'a'+10) << 4;
775                 else
776                         break;
777                 Src ++;
778                 
779                 if('0' <= *Src && *Src <= '9')
780                         val |= (*Src-'0');
781                 else if('A' <= *Src && *Src <= 'F')
782                         val |= (*Src-'A'+10);
783                 else if('a' <= *Src && *Src <= 'f')
784                         val |= (*Src-'a'+10);
785                 else
786                         break;
787                 Src ++;
788                 
789                 Dest[i] = val;
790         }
791         for( ; i < BufSize; i++ )
792                 Dest[i] = 0;
793 }
794
795 /**
796  * \brief Decode a Base64 value
797  */
798 int UnBase64(uint8_t *Dest, char *Src, int BufSize)
799 {
800         uint32_t        val;
801          int    i, j;
802         char    *start_src = Src;
803         
804         for( i = 0; i+2 < BufSize; i += 3 )
805         {
806                 val = 0;
807                 for( j = 0; j < 4; j++, Src ++ ) {
808                         if('A' <= *Src && *Src <= 'Z')
809                                 val |= (*Src - 'A') << ((3-j)*6);
810                         else if('a' <= *Src && *Src <= 'z')
811                                 val |= (*Src - 'a' + 26) << ((3-j)*6);
812                         else if('0' <= *Src && *Src <= '9')
813                                 val |= (*Src - '0' + 52) << ((3-j)*6);
814                         else if(*Src == '+')
815                                 val |= 62 << ((3-j)*6);
816                         else if(*Src == '/')
817                                 val |= 63 << ((3-j)*6);
818                         else if(!*Src)
819                                 break;
820                         else if(*Src != '=')
821                                 j --;   // Ignore invalid characters
822                 }
823                 Dest[i  ] = (val >> 16) & 0xFF;
824                 Dest[i+1] = (val >> 8) & 0xFF;
825                 Dest[i+2] = val & 0xFF;
826                 if(j != 4)      break;
827         }
828         
829         // Finish things off
830         if(i   < BufSize)
831                 Dest[i] = (val >> 16) & 0xFF;
832         if(i+1 < BufSize)
833                 Dest[i+1] = (val >> 8) & 0xFF;
834         
835         return Src - start_src;
836 }

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