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

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