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