New silcconfig library and server parser. Merged silc-newconfig-final.patch.
[silc.git] / lib / silcutil / silcutil.c
1 /*
2
3   silcutil.c 
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 1997 - 2002 Pekka Riikonen
8
9   This program is free software; you can redistribute it and/or modify
10   it under the terms of the GNU General Public License as published by
11   the Free Software Foundation; version 2 of the License.
12
13   This program is distributed in the hope that it will be useful,
14   but WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16   GNU General Public License for more details.
17
18 */
19 /*
20  * These are general utility functions that doesn't belong to any specific
21  * group of routines.
22  */
23 /* $Id$ */
24
25 #include "silcincludes.h"
26
27 /* Opens a file indicated by the filename `filename' with flags indicated
28    by the `flags'. */
29
30 int silc_file_open(const char *filename, int flags)
31 {
32   int fd;
33
34   fd = open(filename, flags, 0600);
35
36   return fd;
37 }
38
39 /* Reads data from file descriptor `fd' to `buf'. */
40
41 int silc_file_read(int fd, unsigned char *buf, uint32 buf_len)
42 {
43   return read(fd, (void *)buf, buf_len);
44 }
45
46 /* Writes `buffer' of length of `len' to file descriptor `fd. */
47
48 int silc_file_write(int fd, const char *buffer, uint32 len)
49 {
50   return write(fd, (const void *)buffer, len);
51 }
52
53 /* Closes file descriptor */
54
55 int silc_file_close(int fd)
56 {
57   return close(fd);
58 }
59
60 /* Writes a buffer to the file. */
61
62 int silc_file_writefile(const char *filename, const char *buffer, uint32 len)
63 {
64   int fd;
65         
66   if ((fd = creat(filename, 0644)) == -1) {
67     SILC_LOG_ERROR(("Cannot open file %s for writing: %s", filename,
68                     strerror(errno)));
69     return -1;
70   }
71   
72   if ((write(fd, buffer, len)) == -1) {
73     SILC_LOG_ERROR(("Cannot write to file %s: %s", filename, strerror(errno)));
74     close(fd);
75     return -1;
76   }
77
78   close(fd);
79   
80   return 0;
81 }
82
83 /* Writes a buffer to the file.  If the file is created specific mode is
84    set to the file. */
85
86 int silc_file_writefile_mode(const char *filename, const char *buffer, 
87                              uint32 len, int mode)
88 {
89   int fd;
90         
91   if ((fd = creat(filename, mode)) == -1) {
92     SILC_LOG_ERROR(("Cannot open file %s for writing: %s", filename,
93                     strerror(errno)));
94     return -1;
95   }
96   
97   if ((write(fd, buffer, len)) == -1) {
98     SILC_LOG_ERROR(("Cannot write to file %s: %s", filename, strerror(errno)));
99     close(fd);
100     return -1;
101   }
102
103   close(fd);
104   
105   return 0;
106 }
107
108 /* Reads a file to a buffer. The allocated buffer is returned. Length of
109    the file read is returned to the return_len argument. */
110
111 char *silc_file_readfile(const char *filename, uint32 *return_len)
112 {
113   int fd;
114   char *buffer;
115   int filelen;
116
117   fd = silc_file_open(filename, O_RDONLY);
118   if (fd < 0) {
119     if (errno == ENOENT)
120       return NULL;
121     SILC_LOG_ERROR(("Cannot open file %s: %s", filename, strerror(errno)));
122     return NULL;
123   }
124
125   filelen = lseek(fd, (off_t)0L, SEEK_END);
126   if (filelen < 0) {
127     close(fd);
128     return NULL;
129   }
130   if (lseek(fd, (off_t)0L, SEEK_SET) < 0) {
131     close(fd);
132     return NULL;
133   }
134
135   if (filelen < 0) {
136     SILC_LOG_ERROR(("Cannot open file %s: %s", filename, strerror(errno)));
137     close(fd);
138     return NULL;
139   }
140   
141   buffer = silc_calloc(filelen + 1, sizeof(char));
142   
143   if ((read(fd, buffer, filelen)) == -1) {
144     memset(buffer, 0, sizeof(buffer));
145     close(fd);
146     SILC_LOG_ERROR(("Cannot read from file %s: %s", filename,
147                     strerror(errno)));
148     return NULL;
149   }
150
151   close(fd);
152   buffer[filelen] = EOF;
153
154   if (return_len)
155     *return_len = filelen;
156
157   return buffer;
158 }
159
160 /* Returns files size. Returns 0 on error. */
161
162 uint64 silc_file_size(const char *filename)
163 {
164   int ret;
165   struct stat stats;
166
167 #ifndef SILC_WIN32
168   ret = lstat(filename, &stats);
169 #else
170   ret = stat(filename, &stats);
171 #endif
172   if (ret < 0)
173     return 0;
174
175   return (uint64)stats.st_size;
176 }
177
178 /* Gets line from a buffer. Stops reading when a newline or EOF occurs.
179    This doesn't remove the newline sign from the destination buffer. The
180    argument begin is returned and should be passed again for the function. */
181
182 int silc_gets(char *dest, int destlen, const char *src, int srclen, int begin)
183 {
184   static int start = 0;
185   int i;
186   
187   memset(dest, 0, destlen);
188   
189   if (begin != start)
190     start = 0;
191   
192   i = 0;
193   for ( ; start <= srclen; i++, start++) {
194     if (i > destlen)
195       return -1;
196     
197     dest[i] = src[start];
198     
199     if (dest[i] == EOF) 
200       return EOF;
201     
202     if (dest[i] == '\n') 
203       break;
204   }
205   start++;
206   
207   return start;
208 }
209
210 /* Checks line for illegal characters. Return -1 when illegal character
211    were found. This is used to check for bad lines when reading data from
212    for example a configuration file. */
213
214 int silc_check_line(char *buf) 
215 {
216   /* Illegal characters in line */
217   if (strchr(buf, '#')) return -1;
218   if (strchr(buf, '\'')) return -1;
219   if (strchr(buf, '\\')) return -1;
220   if (strchr(buf, '\r')) return -1;
221   if (strchr(buf, '\a')) return -1;
222   if (strchr(buf, '\b')) return -1;
223   if (strchr(buf, '\f')) return -1;
224   
225   /* Empty line */
226   if (buf[0] == '\n')
227     return -1;
228   
229   return 0;
230 }
231
232 /* Returns current time as string. */
233
234 char *silc_get_time()
235 {
236   time_t curtime;
237   char *return_time;
238
239   curtime = time(NULL);
240   return_time = ctime(&curtime);
241   return_time[strlen(return_time) - 1] = '\0';
242
243   return return_time;
244 }
245
246 /* Converts string to capital characters */
247
248 char *silc_to_upper(char *string)
249 {
250   int i;
251   char *ret = silc_calloc(strlen(string) + 1, sizeof(char));
252
253   for (i = 0; i < strlen(string); i++)
254     ret[i] = toupper(string[i]);
255
256   return ret;
257 }
258
259 static unsigned char pem_enc[64] =
260 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
261
262 /* Encodes data into PEM encoding. Returns NULL terminated PEM encoded
263    data string. Note: This is originally public domain code and is 
264    still PD. */
265
266 char *silc_encode_pem(unsigned char *data, uint32 len)
267 {
268   int i, j;
269   uint32 bits, c, char_count;
270   char *pem;
271
272   char_count = 0;
273   bits = 0;
274   j = 0;
275
276   pem = silc_calloc(((len * 8 + 5) / 6) + 5, sizeof(*pem));
277
278   for (i = 0; i < len; i++) {
279     c = data[i];
280     bits += c;
281     char_count++;
282
283     if (char_count == 3) {
284       pem[j++] = pem_enc[bits  >> 18];
285       pem[j++] = pem_enc[(bits >> 12) & 0x3f];
286       pem[j++] = pem_enc[(bits >> 6)  & 0x3f];
287       pem[j++] = pem_enc[bits & 0x3f];
288       bits = 0;
289       char_count = 0;
290     } else {
291       bits <<= 8;
292     }
293   }
294
295   if (char_count != 0) {
296     bits <<= 16 - (8 * char_count);
297     pem[j++] = pem_enc[bits >> 18];
298     pem[j++] = pem_enc[(bits >> 12) & 0x3f];
299
300     if (char_count == 1) {
301       pem[j++] = '=';
302       pem[j] = '=';
303     } else {
304       pem[j++] = pem_enc[(bits >> 6) & 0x3f];
305       pem[j] = '=';
306     }
307   }
308
309   return pem;
310 }
311
312 /* Same as above but puts newline ('\n') every 72 characters. */
313
314 char *silc_encode_pem_file(unsigned char *data, uint32 data_len)
315 {
316   int i, j;
317   uint32 len, cols;
318   char *pem, *pem2;
319
320   pem = silc_encode_pem(data, data_len);
321   len = strlen(pem);
322
323   pem2 = silc_calloc(len + (len / 72) + 1, sizeof(*pem2));
324
325   for (i = 0, j = 0, cols = 1; i < len; i++, cols++) {
326     if (cols == 72) {
327       pem2[i] = '\n';
328       cols = 0;
329       len++;
330       continue;
331     }
332
333     pem2[i] = pem[j++];
334   }
335
336   silc_free(pem);
337   return pem2;
338 }
339
340 /* Decodes PEM into data. Returns the decoded data. Note: This is
341    originally public domain code and is still PD. */
342
343 unsigned char *silc_decode_pem(unsigned char *pem, uint32 pem_len,
344                                uint32 *ret_len)
345 {
346   int i, j;
347   uint32 len, c, char_count, bits;
348   unsigned char *data;
349   static char ialpha[256], decoder[256];
350
351   for (i = 64 - 1; i >= 0; i--) {
352     ialpha[pem_enc[i]] = 1;
353     decoder[pem_enc[i]] = i;
354   }
355
356   char_count = 0;
357   bits = 0;
358   j = 0;
359
360   if (!pem_len)
361     len = strlen(pem);
362   else
363     len = pem_len;
364
365   data = silc_calloc(((len * 6) / 8), sizeof(*data));
366
367   for (i = 0; i < len; i++) {
368     c = pem[i];
369
370     if (c == '=')
371       break;
372
373     if (c > 127 || !ialpha[c])
374       continue;
375
376     bits += decoder[c];
377     char_count++;
378
379     if (char_count == 4) {
380       data[j++] = bits >> 16;
381       data[j++] = (bits >> 8) & 0xff;
382       data[j++] = bits & 0xff;
383       bits = 0;
384       char_count = 0;
385     } else {
386       bits <<= 6;
387     }
388   }
389
390   switch(char_count) {
391   case 1:
392     silc_free(data);
393     return NULL;
394     break;
395   case 2:
396     data[j++] = bits >> 10;
397     break;
398   case 3:
399     data[j++] = bits >> 16;
400     data[j++] = (bits >> 8) & 0xff;
401     break;
402   }
403
404   if (ret_len)
405     *ret_len = j;
406
407   return data;
408 }
409
410 /* Parse userfqdn string which is in user@fqdn format */
411
412 bool silc_parse_userfqdn(const char *string, char **left, char **right)
413 {
414   uint32 tlen;
415
416   if (!string)
417     return FALSE;
418
419   if (string[0] == '@') {
420     if (left)
421       *left = strdup(string);
422     return TRUE;
423   }
424
425   if (strchr(string, '@')) {
426     tlen = strcspn(string, "@");
427     
428     if (left) {
429       *left = silc_calloc(tlen + 1, sizeof(char));
430       memcpy(*left, string, tlen);
431     }
432     
433     if (right) {
434       *right = silc_calloc((strlen(string) - tlen) + 1, sizeof(char));
435       memcpy(*right, string + tlen + 1, strlen(string) - tlen - 1);
436     }
437   } else {
438     if (left)
439       *left = strdup(string);
440   }
441
442   return TRUE;
443 }
444
445 /* Parses command line. At most `max_args' is taken. Rest of the line
446    will be allocated as the last argument if there are more than `max_args'
447    arguments in the line. Note that the command name is counted as one
448    argument and is saved. */
449
450 void silc_parse_command_line(unsigned char *buffer, 
451                              unsigned char ***parsed,
452                              uint32 **parsed_lens,
453                              uint32 **parsed_types,
454                              uint32 *parsed_num,
455                              uint32 max_args)
456 {
457   int i, len = 0;
458   int argc = 0;
459   const char *cp = buffer;
460   char *tmp;
461
462   *parsed = silc_calloc(1, sizeof(**parsed));
463   *parsed_lens = silc_calloc(1, sizeof(**parsed_lens));
464
465   /* Get the command first */
466   len = strcspn(cp, " ");
467   tmp = silc_to_upper((char *)cp);
468   (*parsed)[0] = silc_calloc(len + 1, sizeof(char));
469   memcpy((*parsed)[0], tmp, len);
470   silc_free(tmp);
471   (*parsed_lens)[0] = len;
472   cp += len;
473   while (*cp == ' ')
474     cp++;
475   argc++;
476
477   /* Parse arguments */
478   if (strchr(cp, ' ') || strlen(cp) != 0) {
479     for (i = 1; i < max_args; i++) {
480
481       if (i != max_args - 1)
482         len = strcspn(cp, " ");
483       else
484         len = strlen(cp);
485       while (len && cp[len - 1] == ' ')
486         len--;
487       if (!len)
488         break;
489       
490       *parsed = silc_realloc(*parsed, sizeof(**parsed) * (argc + 1));
491       *parsed_lens = silc_realloc(*parsed_lens, 
492                                   sizeof(**parsed_lens) * (argc + 1));
493       (*parsed)[argc] = silc_calloc(len + 1, sizeof(char));
494       memcpy((*parsed)[argc], cp, len);
495       (*parsed_lens)[argc] = len;
496       argc++;
497
498       cp += len;
499       if (strlen(cp) == 0)
500         break;
501       else
502         while (*cp == ' ')
503           cp++;
504     }
505   }
506
507   /* Save argument types. Protocol defines all argument types but
508      this implementation makes sure that they are always in correct
509      order hence this simple code. */
510   *parsed_types = silc_calloc(argc, sizeof(**parsed_types));
511   for (i = 0; i < argc; i++)
512     (*parsed_types)[i] = i;
513
514   *parsed_num = argc;
515 }
516
517 /* Formats arguments to a string and returns it after allocating memory
518    for it. It must be remembered to free it later. */
519
520 char *silc_format(char *fmt, ...)
521 {
522   va_list args;
523   static char buf[8192];
524
525   memset(buf, 0, sizeof(buf));
526   va_start(args, fmt);
527   vsnprintf(buf, sizeof(buf) - 1, fmt, args);
528   va_end(args);
529
530   return strdup(buf);
531 }
532
533 /* Renders ID to suitable to print for example to log file. */
534
535 static char rid[256];
536
537 char *silc_id_render(void *id, uint16 type)
538 {
539   char tmp[100];
540   unsigned char tmps[2];
541
542   memset(rid, 0, sizeof(rid));
543   switch(type) {
544   case SILC_ID_SERVER:
545     {
546       SilcServerID *server_id = (SilcServerID *)id;
547       if (server_id->ip.data_len > 4) {
548 #ifdef HAVE_IPV6
549         struct in6_addr ipv6;
550         memmove(&ipv6, server_id->ip.data, sizeof(ipv6));
551         if (!inet_ntop(AF_INET6, &ipv6, tmp, sizeof(tmp)))
552           strcat(rid, tmp);
553 #endif
554       } else {
555         struct in_addr ipv4;
556         memmove(&ipv4.s_addr, server_id->ip.data, 4);
557         strcat(rid, inet_ntoa(ipv4));
558       }
559
560       memset(tmp, 0, sizeof(tmp));
561       snprintf(tmp, sizeof(tmp), ",%d,", ntohs(server_id->port));
562       strcat(rid, tmp);
563       SILC_PUT16_MSB(server_id->rnd, tmps);
564       memset(tmp, 0, sizeof(tmp));
565       snprintf(tmp, sizeof(tmp), "[%02x %02x]", tmps[0], tmps[1]);
566       strcat(rid, tmp);
567     }
568     break;
569   case SILC_ID_CLIENT:
570     {
571       SilcClientID *client_id = (SilcClientID *)id;
572       if (client_id->ip.data_len > 4) {
573 #ifdef HAVE_IPV6
574         struct in6_addr ipv6;
575         memmove(&ipv6, client_id->ip.data, sizeof(ipv6));
576         if (!inet_ntop(AF_INET6, &ipv6, tmp, sizeof(tmp)))
577           strcat(rid, tmp);
578 #endif
579       } else {
580         struct in_addr ipv4;
581         memmove(&ipv4.s_addr, client_id->ip.data, 4);
582         strcat(rid, inet_ntoa(ipv4));
583       }
584
585       memset(tmp, 0, sizeof(tmp));
586       snprintf(tmp, sizeof(tmp), ",%02x,", client_id->rnd);
587       strcat(rid, tmp);
588       memset(tmp, 0, sizeof(tmp));
589       snprintf(tmp, sizeof(tmp), "[%02x %02x %02x %02x...]", 
590                client_id->hash[0], client_id->hash[1],
591                client_id->hash[2], client_id->hash[3]);
592       strcat(rid, tmp);
593     }
594     break;
595   case SILC_ID_CHANNEL:
596     {
597       SilcChannelID *channel_id = (SilcChannelID *)id;
598       if (channel_id->ip.data_len > 4) {
599 #ifdef HAVE_IPV6
600         struct in6_addr ipv6;
601         memmove(&ipv6, channel_id->ip.data, sizeof(ipv6));
602         if (!inet_ntop(AF_INET6, &ipv6, tmp, sizeof(tmp)))
603           strcat(rid, tmp);
604 #endif
605       } else {
606         struct in_addr ipv4;
607         memmove(&ipv4.s_addr, channel_id->ip.data, 4);
608         strcat(rid, inet_ntoa(ipv4));
609       }
610
611       memset(tmp, 0, sizeof(tmp));
612       snprintf(tmp, sizeof(tmp), ",%d,", ntohs(channel_id->port));
613       strcat(rid, tmp);
614       SILC_PUT16_MSB(channel_id->rnd, tmps);
615       memset(tmp, 0, sizeof(tmp));
616       snprintf(tmp, sizeof(tmp), "[%02x %02x]", tmps[0], tmps[1]);
617       strcat(rid, tmp);
618     }
619     break;
620   }
621
622   return rid;
623 }
624
625 /* Compares two strings. Strings may include wildcards * and ?.
626    Returns TRUE if strings match. */
627
628 int silc_string_compare(char *string1, char *string2)
629 {
630   int i;
631   int slen1;
632   int slen2;
633   char *tmpstr1, *tmpstr2;
634
635   if (!string1 || !string2)
636     return FALSE;
637
638   slen1 = strlen(string1);
639   slen2 = strlen(string2);
640
641   /* See if they are same already */
642   if (!strncmp(string1, string2, strlen(string2)))
643     return TRUE;
644
645   if (slen2 < slen1)
646     if (!strchr(string1, '*'))
647       return FALSE;
648   
649   /* Take copies of the original strings as we will change them */
650   tmpstr1 = silc_calloc(slen1 + 1, sizeof(char));
651   memcpy(tmpstr1, string1, slen1);
652   tmpstr2 = silc_calloc(slen2 + 1, sizeof(char));
653   memcpy(tmpstr2, string2, slen2);
654   
655   for (i = 0; i < slen1; i++) {
656     
657     /* * wildcard. Only one * wildcard is possible. */
658     if (tmpstr1[i] == '*')
659       if (!strncmp(tmpstr1, tmpstr2, i)) {
660         memset(tmpstr2, 0, slen2);
661         strncpy(tmpstr2, tmpstr1, i);
662         break;
663       }
664     
665     /* ? wildcard */
666     if (tmpstr1[i] == '?') {
667       if (!strncmp(tmpstr1, tmpstr2, i)) {
668         if (!(slen1 < i + 1))
669           if (tmpstr1[i + 1] != '?' &&
670               tmpstr1[i + 1] != tmpstr2[i + 1])
671             continue;
672         
673         if (!(slen1 < slen2))
674           tmpstr2[i] = '?';
675       }
676     }
677   }
678   
679   /* if using *, remove it */
680   if (strchr(tmpstr1, '*'))
681     *strchr(tmpstr1, '*') = 0;
682   
683   if (!strcmp(tmpstr1, tmpstr2)) {
684     memset(tmpstr1, 0, slen1);
685     memset(tmpstr2, 0, slen2);
686     silc_free(tmpstr1);
687     silc_free(tmpstr2);
688     return TRUE;
689   }
690   
691   memset(tmpstr1, 0, slen1);
692   memset(tmpstr2, 0, slen2);
693   silc_free(tmpstr1);
694   silc_free(tmpstr2);
695   return FALSE;
696 }
697
698 /* Basic has function to hash strings. May be used with the SilcHashTable. 
699    Note that this lowers the characters of the string (with tolower()) so
700    this is used usually with nicknames, channel and server names to provide
701    case insensitive keys. */
702
703 uint32 silc_hash_string(void *key, void *user_context)
704 {
705   char *s = (char *)key;
706   uint32 h = 0, g;
707   
708   while (*s != '\0') {
709     h = (h << 4) + tolower(*s);
710     if ((g = h & 0xf0000000)) {
711       h = h ^ (g >> 24);
712       h = h ^ g;
713     }
714     s++;
715   }
716   
717   return h;
718 }
719
720 /* Basic hash function to hash integers. May be used with the SilcHashTable. */
721
722 uint32 silc_hash_uint(void *key, void *user_context)
723 {
724   return *(uint32 *)key;
725 }
726
727 /* Basic hash funtion to hash pointers. May be used with the SilcHashTable. */
728
729 uint32 silc_hash_ptr(void *key, void *user_context)
730 {
731   return (uint32)key;
732 }
733
734 /* Hash a ID. The `user_context' is the ID type. */
735
736 uint32 silc_hash_id(void *key, void *user_context)
737 {
738   SilcIdType id_type = (SilcIdType)(uint32)user_context;
739   uint32 h = 0;
740   int i;
741
742   switch (id_type) {
743   case SILC_ID_CLIENT:
744     {
745       SilcClientID *id = (SilcClientID *)key;
746       uint32 g;
747   
748       /* The client ID is hashed by hashing the hash of the ID 
749          (which is a truncated MD5 hash of the nickname) so that we
750          can access the entry from the cache with both Client ID but
751          with just a hash from the ID as well. */
752
753       for (i = 0; i < sizeof(id->hash); i++) {
754         h = (h << 4) + id->hash[i];
755         if ((g = h & 0xf0000000)) {
756           h = h ^ (g >> 24);
757           h = h ^ g;
758         }
759       }
760
761       return h;
762     }
763     break;
764   case SILC_ID_SERVER:
765     {
766       SilcServerID *id = (SilcServerID *)key;
767       
768       h = id->port * id->rnd;
769       for (i = 0; i < id->ip.data_len; i++)
770         h ^= id->ip.data[i];
771       
772       return h;
773     }
774     break;
775   case SILC_ID_CHANNEL:
776     {
777       SilcChannelID *id = (SilcChannelID *)key;
778       
779       h = id->port * id->rnd;
780       for (i = 0; i < id->ip.data_len; i++)
781         h ^= id->ip.data[i];
782       
783       return h;
784     }
785     break;
786   default:
787     break;
788   }
789
790   return h;
791 }
792
793 /* Hash binary data. The `user_context' is the data length. */
794
795 uint32 silc_hash_data(void *key, void *user_context)
796 {
797   uint32 len = (uint32)user_context, h = 0;
798   unsigned char *data = (unsigned char *)key;
799   int i;
800
801   h = (data[0] * data[len - 1] + 1) * len;
802   for (i = 0; i < len; i++)
803     h ^= data[i];
804
805   return h;
806 }
807
808 /* Compares two strings. May be used as SilcHashTable comparison function. */
809
810 bool silc_hash_string_compare(void *key1, void *key2, void *user_context)
811 {
812   return !strcasecmp((char *)key1, (char *)key2);
813 }
814
815 /* Compares two ID's. May be used as SilcHashTable comparison function. 
816    The Client ID's compares only the hash of the Client ID not any other
817    part of the Client ID. Other ID's are fully compared. */
818
819 bool silc_hash_id_compare(void *key1, void *key2, void *user_context)
820 {
821   SilcIdType id_type = (SilcIdType)(uint32)user_context;
822   return (id_type == SILC_ID_CLIENT ? 
823           SILC_ID_COMPARE_HASH((SilcClientID *)key1, (SilcClientID *)key2) :
824           SILC_ID_COMPARE_TYPE(key1, key2, id_type));
825 }
826
827 /* Compare two Client ID's entirely and not just the hash from the ID. */
828
829 bool silc_hash_client_id_compare(void *key1, void *key2, void *user_context)
830 {
831   return SILC_ID_COMPARE_TYPE(key1, key2, SILC_ID_CLIENT);
832 }
833
834 /* Compares binary data. May be used as SilcHashTable comparison function. */
835
836 bool silc_hash_data_compare(void *key1, void *key2, void *user_context)
837 {
838   uint32 len = (uint32)user_context;
839   return !memcmp(key1, key2, len);
840 }
841
842 /* Parses mode mask and returns the mode as string. */
843
844 char *silc_client_chmode(uint32 mode, const char *cipher, const char *hmac)
845 {
846   char string[100];
847
848   if (!mode)
849     return NULL;
850
851   memset(string, 0, sizeof(string));
852
853   if (mode & SILC_CHANNEL_MODE_PRIVATE)
854     strncat(string, "p", 1);
855
856   if (mode & SILC_CHANNEL_MODE_SECRET)
857     strncat(string, "s", 1);
858
859   if (mode & SILC_CHANNEL_MODE_PRIVKEY)
860     strncat(string, "k", 1);
861
862   if (mode & SILC_CHANNEL_MODE_INVITE)
863     strncat(string, "i", 1);
864
865   if (mode & SILC_CHANNEL_MODE_TOPIC)
866     strncat(string, "t", 1);
867
868   if (mode & SILC_CHANNEL_MODE_ULIMIT)
869     strncat(string, "l", 1);
870
871   if (mode & SILC_CHANNEL_MODE_PASSPHRASE)
872     strncat(string, "a", 1);
873
874   if (mode & SILC_CHANNEL_MODE_FOUNDER_AUTH)
875     strncat(string, "f", 1);
876
877   if (mode & SILC_CHANNEL_MODE_CIPHER)
878     strncat(string, cipher, strlen(cipher));
879
880   if (mode & SILC_CHANNEL_MODE_HMAC)
881     strncat(string, hmac, strlen(hmac));
882
883   /* Rest of mode is ignored */
884
885   return strdup(string);
886 }
887
888 /* Parses channel user mode mask and returns te mode as string */
889
890 char *silc_client_chumode(uint32 mode)
891 {
892   char string[4];
893
894   if (!mode)
895     return NULL;
896
897   memset(string, 0, sizeof(string));
898
899   if (mode & SILC_CHANNEL_UMODE_CHANFO)
900     strncat(string, "f", 1);
901
902   if (mode & SILC_CHANNEL_UMODE_CHANOP)
903     strncat(string, "o", 1);
904
905   return strdup(string);
906 }
907
908 /* Parses channel user mode and returns it as special mode character. */
909
910 char *silc_client_chumode_char(uint32 mode)
911 {
912   char string[4];
913
914   if (!mode)
915     return NULL;
916
917   memset(string, 0, sizeof(string));
918
919   if (mode & SILC_CHANNEL_UMODE_CHANFO)
920     strncat(string, "*", 1);
921
922   if (mode & SILC_CHANNEL_UMODE_CHANOP)
923     strncat(string, "@", 1);
924
925   return strdup(string);
926 }
927
928 /* Creates fingerprint from data, usually used with SHA1 digests */
929
930 char *silc_fingerprint(const unsigned char *data, uint32 data_len)
931 {
932   char fingerprint[64], *cp;
933   int i;
934
935   memset(fingerprint, 0, sizeof(fingerprint));
936   cp = fingerprint;
937   for (i = 0; i < data_len; i++) {
938     snprintf(cp, sizeof(fingerprint), "%02X", data[i]);
939     cp += 2;
940     
941     if ((i + 1) % 2 == 0)
942       snprintf(cp++, sizeof(fingerprint), " ");
943
944     if ((i + 1) % 10 == 0)
945       snprintf(cp++, sizeof(fingerprint), " ");
946   }
947   i--;
948   if ((i + 1) % 2 == 0)
949     cp[-2] = 0;
950   if ((i + 1) % 10 == 0)
951     cp[-1] = 0;
952   
953   return strdup(fingerprint);
954 }