updates.
[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\r
168   ret = lstat(filename, &stats);
169 #else\r
170   ret = stat(filename, &stats);\r
171 #endif\r
172   if (ret < 0)\r
173     return 0;\r
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 = strlen(string1);
632   int slen2 = strlen(string2);
633   char *tmpstr1, *tmpstr2;
634
635   if (!string1 || !string2)
636     return FALSE;
637
638   /* See if they are same already */
639   if (!strncmp(string1, string2, strlen(string2)))
640     return TRUE;
641
642   if (slen2 < slen1)
643     if (!strchr(string1, '*'))
644       return FALSE;
645   
646   /* Take copies of the original strings as we will change them */
647   tmpstr1 = silc_calloc(slen1 + 1, sizeof(char));
648   memcpy(tmpstr1, string1, slen1);
649   tmpstr2 = silc_calloc(slen2 + 1, sizeof(char));
650   memcpy(tmpstr2, string2, slen2);
651   
652   for (i = 0; i < slen1; i++) {
653     
654     /* * wildcard. Only one * wildcard is possible. */
655     if (tmpstr1[i] == '*')
656       if (!strncmp(tmpstr1, tmpstr2, i)) {
657         memset(tmpstr2, 0, slen2);
658         strncpy(tmpstr2, tmpstr1, i);
659         break;
660       }
661     
662     /* ? wildcard */
663     if (tmpstr1[i] == '?') {
664       if (!strncmp(tmpstr1, tmpstr2, i)) {
665         if (!(slen1 < i + 1))
666           if (tmpstr1[i + 1] != '?' &&
667               tmpstr1[i + 1] != tmpstr2[i + 1])
668             continue;
669         
670         if (!(slen1 < slen2))
671           tmpstr2[i] = '?';
672       }
673     }
674   }
675   
676   /* if using *, remove it */
677   if (strchr(tmpstr1, '*'))
678     *strchr(tmpstr1, '*') = 0;
679   
680   if (!strcmp(tmpstr1, tmpstr2)) {
681     memset(tmpstr1, 0, slen1);
682     memset(tmpstr2, 0, slen2);
683     silc_free(tmpstr1);
684     silc_free(tmpstr2);
685     return TRUE;
686   }
687   
688   memset(tmpstr1, 0, slen1);
689   memset(tmpstr2, 0, slen2);
690   silc_free(tmpstr1);
691   silc_free(tmpstr2);
692   return FALSE;
693 }
694
695 /* Basic has function to hash strings. May be used with the SilcHashTable. 
696    Note that this lowers the characters of the string (with tolower()) so
697    this is used usually with nicknames, channel and server names to provide
698    case insensitive keys. */
699
700 uint32 silc_hash_string(void *key, void *user_context)
701 {
702   char *s = (char *)key;
703   uint32 h = 0, g;
704   
705   while (*s != '\0') {
706     h = (h << 4) + tolower(*s);
707     if ((g = h & 0xf0000000)) {
708       h = h ^ (g >> 24);
709       h = h ^ g;
710     }
711     s++;
712   }
713   
714   return h;
715 }
716
717 /* Basic hash function to hash integers. May be used with the SilcHashTable. */
718
719 uint32 silc_hash_uint(void *key, void *user_context)
720 {
721   return *(uint32 *)key;
722 }
723
724 /* Basic hash funtion to hash pointers. May be used with the SilcHashTable. */
725
726 uint32 silc_hash_ptr(void *key, void *user_context)
727 {
728   return (uint32)key;
729 }
730
731 /* Hash a ID. The `user_context' is the ID type. */
732
733 uint32 silc_hash_id(void *key, void *user_context)
734 {
735   SilcIdType id_type = (SilcIdType)(uint32)user_context;
736   uint32 h = 0;
737   int i;
738
739   switch (id_type) {
740   case SILC_ID_CLIENT:
741     {
742       SilcClientID *id = (SilcClientID *)key;
743       uint32 g;
744   
745       /* The client ID is hashed by hashing the hash of the ID 
746          (which is a truncated MD5 hash of the nickname) so that we
747          can access the entry from the cache with both Client ID but
748          with just a hash from the ID as well. */
749
750       for (i = 0; i < sizeof(id->hash); i++) {
751         h = (h << 4) + id->hash[i];
752         if ((g = h & 0xf0000000)) {
753           h = h ^ (g >> 24);
754           h = h ^ g;
755         }
756       }
757
758       return h;
759     }
760     break;
761   case SILC_ID_SERVER:
762     {
763       SilcServerID *id = (SilcServerID *)key;
764       
765       h = id->port * id->rnd;
766       for (i = 0; i < id->ip.data_len; i++)
767         h ^= id->ip.data[i];
768       
769       return h;
770     }
771     break;
772   case SILC_ID_CHANNEL:
773     {
774       SilcChannelID *id = (SilcChannelID *)key;
775       
776       h = id->port * id->rnd;
777       for (i = 0; i < id->ip.data_len; i++)
778         h ^= id->ip.data[i];
779       
780       return h;
781     }
782     break;
783   default:
784     break;
785   }
786
787   return h;
788 }
789
790 /* Hash binary data. The `user_context' is the data length. */
791
792 uint32 silc_hash_data(void *key, void *user_context)
793 {
794   uint32 len = (uint32)user_context, h = 0;
795   unsigned char *data = (unsigned char *)key;
796   int i;
797
798   h = (data[0] * data[len - 1] + 1) * len;
799   for (i = 0; i < len; i++)
800     h ^= data[i];
801
802   return h;
803 }
804
805 /* Compares two strings. May be used as SilcHashTable comparison function. */
806
807 bool silc_hash_string_compare(void *key1, void *key2, void *user_context)
808 {
809   return !strcasecmp((char *)key1, (char *)key2);
810 }
811
812 /* Compares two ID's. May be used as SilcHashTable comparison function. 
813    The Client ID's compares only the hash of the Client ID not any other
814    part of the Client ID. Other ID's are fully compared. */
815
816 bool silc_hash_id_compare(void *key1, void *key2, void *user_context)
817 {
818   SilcIdType id_type = (SilcIdType)(uint32)user_context;
819   return (id_type == SILC_ID_CLIENT ? 
820           SILC_ID_COMPARE_HASH((SilcClientID *)key1, (SilcClientID *)key2) :
821           SILC_ID_COMPARE_TYPE(key1, key2, id_type));
822 }
823
824 /* Compare two Client ID's entirely and not just the hash from the ID. */
825
826 bool silc_hash_client_id_compare(void *key1, void *key2, void *user_context)
827 {
828   return SILC_ID_COMPARE_TYPE(key1, key2, SILC_ID_CLIENT);
829 }
830
831 /* Compares binary data. May be used as SilcHashTable comparison function. */
832
833 bool silc_hash_data_compare(void *key1, void *key2, void *user_context)
834 {
835   uint32 len = (uint32)user_context;
836   return !memcmp(key1, key2, len);
837 }
838
839 /* Parses mode mask and returns the mode as string. */
840
841 char *silc_client_chmode(uint32 mode, const char *cipher, const char *hmac)
842 {
843   char string[100];
844
845   if (!mode)
846     return NULL;
847
848   memset(string, 0, sizeof(string));
849
850   if (mode & SILC_CHANNEL_MODE_PRIVATE)
851     strncat(string, "p", 1);
852
853   if (mode & SILC_CHANNEL_MODE_SECRET)
854     strncat(string, "s", 1);
855
856   if (mode & SILC_CHANNEL_MODE_PRIVKEY)
857     strncat(string, "k", 1);
858
859   if (mode & SILC_CHANNEL_MODE_INVITE)
860     strncat(string, "i", 1);
861
862   if (mode & SILC_CHANNEL_MODE_TOPIC)
863     strncat(string, "t", 1);
864
865   if (mode & SILC_CHANNEL_MODE_ULIMIT)
866     strncat(string, "l", 1);
867
868   if (mode & SILC_CHANNEL_MODE_PASSPHRASE)
869     strncat(string, "a", 1);
870
871   if (mode & SILC_CHANNEL_MODE_FOUNDER_AUTH)
872     strncat(string, "f", 1);
873
874   if (mode & SILC_CHANNEL_MODE_CIPHER)
875     strncat(string, cipher, strlen(cipher));
876
877   if (mode & SILC_CHANNEL_MODE_HMAC)
878     strncat(string, hmac, strlen(hmac));
879
880   /* Rest of mode is ignored */
881
882   return strdup(string);
883 }
884
885 /* Parses channel user mode mask and returns te mode as string */
886
887 char *silc_client_chumode(uint32 mode)
888 {
889   char string[4];
890
891   if (!mode)
892     return NULL;
893
894   memset(string, 0, sizeof(string));
895
896   if (mode & SILC_CHANNEL_UMODE_CHANFO)
897     strncat(string, "f", 1);
898
899   if (mode & SILC_CHANNEL_UMODE_CHANOP)
900     strncat(string, "o", 1);
901
902   return strdup(string);
903 }
904
905 /* Parses channel user mode and returns it as special mode character. */
906
907 char *silc_client_chumode_char(uint32 mode)
908 {
909   char string[4];
910
911   if (!mode)
912     return NULL;
913
914   memset(string, 0, sizeof(string));
915
916   if (mode & SILC_CHANNEL_UMODE_CHANFO)
917     strncat(string, "*", 1);
918
919   if (mode & SILC_CHANNEL_UMODE_CHANOP)
920     strncat(string, "@", 1);
921
922   return strdup(string);
923 }
924
925 /* Creates fingerprint from data, usually used with SHA1 digests */
926
927 char *silc_fingerprint(const unsigned char *data, uint32 data_len)
928 {
929   char fingerprint[64], *cp;
930   int i;
931
932   memset(fingerprint, 0, sizeof(fingerprint));
933   cp = fingerprint;
934   for (i = 0; i < data_len; i++) {
935     snprintf(cp, sizeof(fingerprint), "%02X", data[i]);
936     cp += 2;
937     
938     if ((i + 1) % 2 == 0)
939       snprintf(cp++, sizeof(fingerprint), " ");
940
941     if ((i + 1) % 10 == 0)
942       snprintf(cp++, sizeof(fingerprint), " ");
943   }
944   i--;
945   if ((i + 1) % 2 == 0)
946     cp[-2] = 0;
947   if ((i + 1) % 10 == 0)
948     cp[-1] = 0;
949   
950   return strdup(fingerprint);
951 }