Implemented limits on `dispense acct` and in ENUM_USERS
[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         if( Args && strlen(Args) )
619         {
620                 char    *min = Args, *max;
621                 
622                 max = strchr(Args, ' ');
623                 if( max ) {
624                         *max = '\0';
625                         max ++;
626                 }
627                 
628                 // If <minBal> != "-"
629                 if( strcmp(min, "-") != 0 )
630                         minBal = atoi(min);
631                 // If <maxBal> != "-"
632                 if( max && strcmp(max, "-") != 0 )
633                         maxBal = atoi(max);
634         }
635         
636         // Get return number
637         for( i = 0; i < numUsr; i ++ )
638         {
639                 int bal = GetBalance(i);
640                 
641                 if( bal == INT_MIN )    continue;
642                 
643                 if( bal < minBal )      continue;
644                 if( bal > maxBal )      continue;
645                 
646                 numRet ++;
647         }
648         
649         // Send count
650         sendf(Client->Socket, "201 Users %i\n", numRet);
651         
652         for( i = 0; i < numUsr; i ++ )
653         {
654                 int bal = GetBalance(i);
655                 
656                 if( bal == INT_MIN )    continue;
657                 
658                 if( bal < minBal )      continue;
659                 if( bal > maxBal )      continue;
660                 
661                 // TODO: User flags
662                 _SendUserInfo(Client, i);
663         }
664         
665         sendf(Client->Socket, "200 List End\n");
666 }
667
668 void Server_Cmd_USERINFO(tClient *Client, char *Args)
669 {
670          int    uid;
671         char    *user = Args;
672         char    *space;
673         
674         space = strchr(user, ' ');
675         if(space)       *space = '\0';
676         
677         // Get recipient
678         uid = GetUserID(user);
679         if( uid == -1 ) {
680                 sendf(Client->Socket, "404 Invalid user");
681                 return ;
682         }
683         
684         _SendUserInfo(Client, uid);
685 }
686
687 void _SendUserInfo(tClient *Client, int UserID)
688 {
689         char    *type, *disabled="";
690          int    flags = GetFlags(UserID);
691         
692         switch( flags & USER_FLAG_TYPEMASK )
693         {
694         default:
695         case USER_TYPE_NORMAL:  type = "user";  break;
696         case USER_TYPE_COKE:    type = "coke";  break;
697         case USER_TYPE_WHEEL:   type = "wheel"; break;
698         case USER_TYPE_GOD:     type = "meta";  break;
699         }
700         
701         if( flags & USER_FLAG_DISABLED )
702                 disabled = ",disabled";
703         
704         // TODO: User flags/type
705         sendf(
706                 Client->Socket, "202 User %s %i %s%s\n",
707                 GetUserName(UserID), GetBalance(UserID),
708                 type, disabled
709                 );
710 }
711
712 /**
713  * \brief Authenticate a user
714  * \return User ID, or -1 if authentication failed
715  */
716 int GetUserAuth(const char *Salt, const char *Username, const uint8_t *ProvidedHash)
717 {
718         #if 0
719         uint8_t h[20];
720          int    ofs = strlen(Username) + strlen(Salt);
721         char    input[ ofs + 40 + 1];
722         char    tmp[4 + strlen(Username) + 1];  // uid=%s
723         #endif
724         
725         #if HACK_TPG_NOAUTH
726         if( strcmp(Username, "tpg") == 0 )
727                 return GetUserID("tpg");
728         #endif
729         #if HACK_ROOT_NOAUTH
730         if( strcmp(Username, "root") == 0 )
731                 return GetUserID("root");
732         #endif
733         
734         #if 0
735         //
736         strcpy(input, Username);
737         strcpy(input, Salt);
738         // TODO: Get user's SHA-1 hash
739         sprintf(tmp, "uid=%s", Username);
740         ldap_search_s(ld, "", LDAP_SCOPE_BASE, tmp, "userPassword", 0, res);
741         
742         sprintf(input+ofs, "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
743                 h[ 0], h[ 1], h[ 2], h[ 3], h[ 4], h[ 5], h[ 6], h[ 7], h[ 8], h[ 9],
744                 h[10], h[11], h[12], h[13], h[14], h[15], h[16], h[17], h[18], h[19]
745                 );
746         // Then create the hash from the provided salt
747         // Compare that with the provided hash
748         #endif
749         
750         return -1;
751 }
752
753 // --- INTERNAL HELPERS ---
754 int sendf(int Socket, const char *Format, ...)
755 {
756         va_list args;
757          int    len;
758         
759         va_start(args, Format);
760         len = vsnprintf(NULL, 0, Format, args);
761         va_end(args);
762         
763         {
764                 char    buf[len+1];
765                 va_start(args, Format);
766                 vsnprintf(buf, len+1, Format, args);
767                 va_end(args);
768                 
769                 #if DEBUG_TRACE_CLIENT
770                 printf("sendf: %s", buf);
771                 #endif
772                 
773                 return send(Socket, buf, len, 0);
774         }
775 }
776
777 // TODO: Move to another file
778 void HexBin(uint8_t *Dest, char *Src, int BufSize)
779 {
780          int    i;
781         for( i = 0; i < BufSize; i ++ )
782         {
783                 uint8_t val = 0;
784                 
785                 if('0' <= *Src && *Src <= '9')
786                         val |= (*Src-'0') << 4;
787                 else if('A' <= *Src && *Src <= 'F')
788                         val |= (*Src-'A'+10) << 4;
789                 else if('a' <= *Src && *Src <= 'f')
790                         val |= (*Src-'a'+10) << 4;
791                 else
792                         break;
793                 Src ++;
794                 
795                 if('0' <= *Src && *Src <= '9')
796                         val |= (*Src-'0');
797                 else if('A' <= *Src && *Src <= 'F')
798                         val |= (*Src-'A'+10);
799                 else if('a' <= *Src && *Src <= 'f')
800                         val |= (*Src-'a'+10);
801                 else
802                         break;
803                 Src ++;
804                 
805                 Dest[i] = val;
806         }
807         for( ; i < BufSize; i++ )
808                 Dest[i] = 0;
809 }
810
811 /**
812  * \brief Decode a Base64 value
813  */
814 int UnBase64(uint8_t *Dest, char *Src, int BufSize)
815 {
816         uint32_t        val;
817          int    i, j;
818         char    *start_src = Src;
819         
820         for( i = 0; i+2 < BufSize; i += 3 )
821         {
822                 val = 0;
823                 for( j = 0; j < 4; j++, Src ++ ) {
824                         if('A' <= *Src && *Src <= 'Z')
825                                 val |= (*Src - 'A') << ((3-j)*6);
826                         else if('a' <= *Src && *Src <= 'z')
827                                 val |= (*Src - 'a' + 26) << ((3-j)*6);
828                         else if('0' <= *Src && *Src <= '9')
829                                 val |= (*Src - '0' + 52) << ((3-j)*6);
830                         else if(*Src == '+')
831                                 val |= 62 << ((3-j)*6);
832                         else if(*Src == '/')
833                                 val |= 63 << ((3-j)*6);
834                         else if(!*Src)
835                                 break;
836                         else if(*Src != '=')
837                                 j --;   // Ignore invalid characters
838                 }
839                 Dest[i  ] = (val >> 16) & 0xFF;
840                 Dest[i+1] = (val >> 8) & 0xFF;
841                 Dest[i+2] = val & 0xFF;
842                 if(j != 4)      break;
843         }
844         
845         // Finish things off
846         if(i   < BufSize)
847                 Dest[i] = (val >> 16) & 0xFF;
848         if(i+1 < BufSize)
849                 Dest[i+1] = (val >> 8) & 0xFF;
850         
851         return Src - start_src;
852 }

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