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