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   char tmp[256];
368
369   if (!string)
370     return FALSE;
371
372   if (strchr(string, '!')) {
373     tlen = strcspn(string, "!");
374     memset(tmp, 0, sizeof(tmp));
375     memcpy(tmp, string, tlen);
376
377     if (num)
378       *num = atoi(tmp);
379
380     if (tlen >= strlen(string))
381       return FALSE;
382
383     string += tlen + 1;
384   }
385
386   if (strchr(string, '@')) {
387     tlen = strcspn(string, "@");
388     
389     if (nickname) {
390       *nickname = silc_calloc(tlen + 1, sizeof(char));
391       memcpy(*nickname, string, tlen);
392     }
393     
394     if (server) {
395       *server = silc_calloc((strlen(string) - tlen) + 1, sizeof(char));
396       memcpy(*server, string + tlen + 1, strlen(string) - tlen - 1);
397     }
398   } else {
399     if (nickname)
400       *nickname = strdup(string);
401   }
402
403   return TRUE;
404 }
405
406 /* Parses command line. At most `max_args' is taken. Rest of the line
407    will be allocated as the last argument if there are more than `max_args'
408    arguments in the line. Note that the command name is counted as one
409    argument and is saved. */
410
411 void silc_parse_command_line(unsigned char *buffer, 
412                              unsigned char ***parsed,
413                              uint32 **parsed_lens,
414                              uint32 **parsed_types,
415                              uint32 *parsed_num,
416                              uint32 max_args)
417 {
418   int i, len = 0;
419   int argc = 0;
420   const char *cp = buffer;
421
422   *parsed = silc_calloc(1, sizeof(**parsed));
423   *parsed_lens = silc_calloc(1, sizeof(**parsed_lens));
424
425   /* Get the command first */
426   len = strcspn(cp, " ");
427   (*parsed)[0] = silc_to_upper((char *)cp);
428   (*parsed_lens)[0] = len;
429   cp += len + 1;
430   argc++;
431
432   /* Parse arguments */
433   if (strchr(cp, ' ') || strlen(cp) != 0) {
434     for (i = 1; i < max_args; i++) {
435
436       if (i != max_args - 1)
437         len = strcspn(cp, " ");
438       else
439         len = strlen(cp);
440       
441       *parsed = silc_realloc(*parsed, sizeof(**parsed) * (argc + 1));
442       *parsed_lens = silc_realloc(*parsed_lens, 
443                                   sizeof(**parsed_lens) * (argc + 1));
444       (*parsed)[argc] = silc_calloc(len + 1, sizeof(char));
445       memcpy((*parsed)[argc], cp, len);
446       (*parsed_lens)[argc] = len;
447       argc++;
448
449       cp += len;
450       if (strlen(cp) == 0)
451         break;
452       else
453         cp++;
454     }
455   }
456
457   /* Save argument types. Protocol defines all argument types but
458      this implementation makes sure that they are always in correct
459      order hence this simple code. */
460   *parsed_types = silc_calloc(argc, sizeof(**parsed_types));
461   for (i = 0; i < argc; i++)
462     (*parsed_types)[i] = i;
463
464   *parsed_num = argc;
465 }
466
467 /* Formats arguments to a string and returns it after allocating memory
468    for it. It must be remembered to free it later. */
469
470 char *silc_format(char *fmt, ...)
471 {
472   va_list args;
473   static char buf[8192];
474
475   memset(buf, 0, sizeof(buf));
476   va_start(args, fmt);
477   vsnprintf(buf, sizeof(buf) - 1, fmt, args);
478   va_end(args);
479
480   return strdup(buf);
481 }
482
483 /* Renders ID to suitable to print for example to log file. */
484
485 static char rid[256];
486
487 char *silc_id_render(void *id, uint16 type)
488 {
489   char tmp[100];
490   unsigned char tmps[2];
491
492   memset(rid, 0, sizeof(rid));
493   switch(type) {
494   case SILC_ID_SERVER:
495     {
496       SilcServerID *server_id = (SilcServerID *)id;
497       struct in_addr ipv4;
498
499       if (server_id->ip.data_len > 4) {
500
501       } else {
502         SILC_GET32_MSB(ipv4.s_addr, server_id->ip.data);
503         strcat(rid, inet_ntoa(ipv4));
504       }
505
506       memset(tmp, 0, sizeof(tmp));
507       snprintf(tmp, sizeof(tmp), ",%d,", ntohs(server_id->port));
508       strcat(rid, tmp);
509       SILC_PUT16_MSB(server_id->rnd, tmps);
510       memset(tmp, 0, sizeof(tmp));
511       snprintf(tmp, sizeof(tmp), "[%02x %02x]", tmps[0], tmps[1]);
512       strcat(rid, tmp);
513     }
514     break;
515   case SILC_ID_CLIENT:
516     {
517       SilcClientID *client_id = (SilcClientID *)id;
518       struct in_addr ipv4;
519
520       if (client_id->ip.data_len > 4) {
521
522       } else {
523         SILC_GET32_MSB(ipv4.s_addr, client_id->ip.data);
524         strcat(rid, inet_ntoa(ipv4));
525       }
526
527       memset(tmp, 0, sizeof(tmp));
528       snprintf(tmp, sizeof(tmp), ",%02x,", client_id->rnd);
529       strcat(rid, tmp);
530       memset(tmp, 0, sizeof(tmp));
531       snprintf(tmp, sizeof(tmp), "[%02x %02x %02x %02x...]", 
532                client_id->hash[0], client_id->hash[1],
533                client_id->hash[2], client_id->hash[3]);
534       strcat(rid, tmp);
535     }
536     break;
537   case SILC_ID_CHANNEL:
538     {
539       SilcChannelID *channel_id = (SilcChannelID *)id;
540       struct in_addr ipv4;
541
542       if (channel_id->ip.data_len > 4) {
543
544       } else {
545         SILC_GET32_MSB(ipv4.s_addr, channel_id->ip.data);
546         strcat(rid, inet_ntoa(ipv4));
547       }
548
549       memset(tmp, 0, sizeof(tmp));
550       snprintf(tmp, sizeof(tmp), ",%d,", ntohs(channel_id->port));
551       strcat(rid, tmp);
552       SILC_PUT16_MSB(channel_id->rnd, tmps);
553       memset(tmp, 0, sizeof(tmp));
554       snprintf(tmp, sizeof(tmp), "[%02x %02x]", tmps[0], tmps[1]);
555       strcat(rid, tmp);
556     }
557     break;
558   }
559
560   return rid;
561 }
562
563 /* Compares two strings. Strings may include wildcards * and ?.
564    Returns TRUE if strings match. */
565
566 int silc_string_compare(char *string1, char *string2)
567 {
568   int i;
569   int slen1 = strlen(string1);
570   int slen2 = strlen(string2);
571   char *tmpstr1, *tmpstr2;
572
573   if (!string1 || !string2)
574     return FALSE;
575
576   /* See if they are same already */
577   if (!strncmp(string1, string2, strlen(string2)))
578     return TRUE;
579
580   if (slen2 < slen1)
581     if (!strchr(string1, '*'))
582       return FALSE;
583   
584   /* Take copies of the original strings as we will change them */
585   tmpstr1 = silc_calloc(slen1 + 1, sizeof(char));
586   memcpy(tmpstr1, string1, slen1);
587   tmpstr2 = silc_calloc(slen2 + 1, sizeof(char));
588   memcpy(tmpstr2, string2, slen2);
589   
590   for (i = 0; i < slen1; i++) {
591     
592     /* * wildcard. Only one * wildcard is possible. */
593     if (tmpstr1[i] == '*')
594       if (!strncmp(tmpstr1, tmpstr2, i)) {
595         memset(tmpstr2, 0, slen2);
596         strncpy(tmpstr2, tmpstr1, i);
597         break;
598       }
599     
600     /* ? wildcard */
601     if (tmpstr1[i] == '?') {
602       if (!strncmp(tmpstr1, tmpstr2, i)) {
603         if (!(slen1 < i + 1))
604           if (tmpstr1[i + 1] != '?' &&
605               tmpstr1[i + 1] != tmpstr2[i + 1])
606             continue;
607         
608         if (!(slen1 < slen2))
609           tmpstr2[i] = '?';
610       }
611     }
612   }
613   
614   /* if using *, remove it */
615   if (strchr(tmpstr1, '*'))
616     *strchr(tmpstr1, '*') = 0;
617   
618   if (!strcmp(tmpstr1, tmpstr2)) {
619     memset(tmpstr1, 0, slen1);
620     memset(tmpstr2, 0, slen2);
621     silc_free(tmpstr1);
622     silc_free(tmpstr2);
623     return TRUE;
624   }
625   
626   memset(tmpstr1, 0, slen1);
627   memset(tmpstr2, 0, slen2);
628   silc_free(tmpstr1);
629   silc_free(tmpstr2);
630   return FALSE;
631 }
632
633 /* Inspects the `string' for wildcards and returns regex string that can
634    be used by the GNU regex library. A comma (`,') in the `string' means
635    that the string is list. */
636
637 char *silc_string_regexify(const char *string)
638 {
639   int i, len, count;
640   char *regex;
641
642   len = strlen(string);
643   count = 4;
644   for (i = 0; i < len; i++)
645     if (string[i] == '*' || string[i] == '?')
646       count++;
647
648   regex = silc_calloc(len + count, sizeof(*regex));
649
650   count = 0;
651   regex[count] = '(';
652   count++;
653
654   for (i = 0; i < len; i++) {
655     if (string[i] == '*' || string[i] == '?') {
656       regex[count] = '.';
657       count++;
658     } else if (string[i] == ',') {
659       regex[count] = '|';
660       count++;
661       continue;
662     }
663
664     regex[count] = string[i];
665     count++;
666   }
667
668   regex[count - 1] = ')';
669   regex[count] = '$';
670
671   return regex;
672 }
673
674 /* Combines two regex strings into one regex string so that they can be
675    used as one by the GNU regex library. The `string2' is combine into
676    the `string1'. */
677
678 char *silc_string_regex_combine(const char *string1, const char *string2)
679 {
680   char *tmp;
681   int len1, len2;
682
683   len1 = strlen(string1);
684   len2 = strlen(string2);
685
686   tmp = silc_calloc(2 + len1 + len2, sizeof(*tmp));
687   strncat(tmp, string1, len1 - 2);
688   strncat(tmp, "|", 1);
689   strncat(tmp, string2 + 1, len2 - 1);
690
691   return tmp;
692 }
693
694 /* Matches the two strings and returns TRUE if the strings match. */
695
696 int silc_string_regex_match(const char *regex, const char *string)
697 {
698   regex_t preg;
699   int ret = FALSE;
700   
701   if (regcomp(&preg, regex, REG_NOSUB | REG_EXTENDED) < 0)
702     return FALSE;
703
704   if (regexec(&preg, string, 0, NULL, 0) == 0)
705     ret = TRUE;
706
707   regfree(&preg);
708
709   return ret;
710 }
711
712 /* Do regex match to the two strings `string1' and `string2'. If the
713    `string2' matches the `string1' this returns TRUE. */
714
715 int silc_string_match(const char *string1, const char *string2)
716 {
717   char *s1;
718   int ret = FALSE;
719
720   s1 = silc_string_regexify(string1);
721   ret = silc_string_regex_match(s1, string2);
722   silc_free(s1);
723
724   return ret;
725 }
726
727 /* Returns the username of the user. If the global variable LOGNAME
728    does not exists we will get the name from the password file. */
729
730 char *silc_get_username()
731 {
732   char *logname = NULL;
733   
734   if (!getenv("LOGNAME")) {
735     fprintf(stderr, "Error: environment variable $LOGNAME not set\n");
736     return NULL;
737   }
738
739   logname = strdup(getenv("LOGNAME"));
740   if (!logname) {
741     logname = getlogin();
742     if (!logname) {
743       struct passwd *pw;
744
745       pw = getpwuid(getuid());
746       if (!pw) {
747         fprintf(stderr, "silc_get_username: %s\n", strerror(errno));
748         return NULL;
749       }
750       
751       logname = strdup(pw->pw_name);
752     }
753   }
754   
755   return logname;
756 }                          
757
758 /* Returns the real name of ther user. */
759
760 char *silc_get_real_name()
761 {
762   char *realname = NULL;
763   struct passwd *pw;
764     
765   pw = getpwuid(getuid());
766   if (!pw) {
767     fprintf(stderr, "silc_get_username: %s\n", strerror(errno));
768     return NULL;
769   }
770
771   if (strchr(pw->pw_gecos, ','))
772     *strchr(pw->pw_gecos, ',') = 0;
773
774   realname = strdup(pw->pw_gecos);
775
776   return realname;
777 }
778
779 /* Basic has function to hash strings. May be used with the SilcHashTable. 
780    Note that this lowers the characters of the string (with tolower()) so
781    this is used usually with nicknames, channel and server names to provide
782    case insensitive keys. */
783
784 uint32 silc_hash_string(void *key, void *user_context)
785 {
786   char *s = (char *)key;
787   uint32 h = 0, g;
788   
789   while (*s != '\0') {
790     h = (h << 4) + tolower(*s);
791     if ((g = h & 0xf0000000)) {
792       h = h ^ (g >> 24);
793       h = h ^ g;
794     }
795     s++;
796   }
797   
798   return h;
799 }
800
801 /* Basic hash function to hash integers. May be used with the SilcHashTable. */
802
803 uint32 silc_hash_uint(void *key, void *user_context)
804 {
805   return *(uint32 *)key;
806 }
807
808 /* Basic hash funtion to hash pointers. May be used with the SilcHashTable. */
809
810 uint32 silc_hash_ptr(void *key, void *user_context)
811 {
812   return (uint32)key;
813 }
814
815 /* Hash a ID. The `user_context' is the ID type. */
816
817 uint32 silc_hash_id(void *key, void *user_context)
818 {
819   SilcIdType id_type = (SilcIdType)(uint32)user_context;
820   uint32 h = 0;
821   int i;
822
823   switch (id_type) {
824   case SILC_ID_CLIENT:
825     {
826       SilcClientID *id = (SilcClientID *)key;
827       uint32 g;
828   
829       /* The client ID is hashed by hashing the hash of the ID 
830          (which is a truncated MD5 hash of the nickname) so that we
831          can access the entry from the cache with both Client ID but
832          with just a hash from the ID as well. */
833
834       for (i = 0; i < sizeof(id->hash); i++) {
835         h = (h << 4) + id->hash[i];
836         if ((g = h & 0xf0000000)) {
837           h = h ^ (g >> 24);
838           h = h ^ g;
839         }
840       }
841
842       return h;
843     }
844     break;
845   case SILC_ID_SERVER:
846     {
847       SilcServerID *id = (SilcServerID *)key;
848       
849       h = id->port * id->rnd;
850       for (i = 0; i < id->ip.data_len; i++)
851         h ^= id->ip.data[i];
852       
853       return h;
854     }
855     break;
856   case SILC_ID_CHANNEL:
857     {
858       SilcChannelID *id = (SilcChannelID *)key;
859       
860       h = id->port * id->rnd;
861       for (i = 0; i < id->ip.data_len; i++)
862         h ^= id->ip.data[i];
863       
864       return h;
865     }
866     break;
867   default:
868     break;
869   }
870
871   return h;
872 }
873
874 /* Hash binary data. The `user_context' is the data length. */
875
876 uint32 silc_hash_data(void *key, void *user_context)
877 {
878   uint32 len = (uint32)user_context, h = 0;
879   unsigned char *data = (unsigned char *)key;
880   int i;
881
882   h = (data[0] * data[len - 1] + 1) * len;
883   for (i = 0; i < len; i++)
884     h ^= data[i];
885
886   return h;
887 }
888
889 /* Compares two strings. May be used as SilcHashTable comparison function. */
890
891 bool silc_hash_string_compare(void *key1, void *key2, void *user_context)
892 {
893   return !strcasecmp((char *)key1, (char *)key2);
894 }
895
896 /* Compares two ID's. May be used as SilcHashTable comparison function. 
897    The Client ID's compares only the hash of the Client ID not any other
898    part of the Client ID. Other ID's are fully compared. */
899
900 bool silc_hash_id_compare(void *key1, void *key2, void *user_context)
901 {
902   SilcIdType id_type = (SilcIdType)(uint32)user_context;
903   return (id_type == SILC_ID_CLIENT ? 
904           SILC_ID_COMPARE_HASH((SilcClientID *)key1, (SilcClientID *)key2) :
905           SILC_ID_COMPARE_TYPE(key1, key2, id_type));
906 }
907
908 /* Compare two Client ID's entirely and not just the hash from the ID. */
909
910 bool silc_hash_client_id_compare(void *key1, void *key2, void *user_context)
911 {
912   return SILC_ID_COMPARE_TYPE(key1, key2, SILC_ID_CLIENT);
913 }
914
915 /* Compares binary data. May be used as SilcHashTable comparison function. */
916
917 bool silc_hash_data_compare(void *key1, void *key2, void *user_context)
918 {
919   uint32 len = (uint32)user_context;
920   return !memcmp(key1, key2, len);
921 }
922
923 /* Parses mode mask and returns the mode as string. */
924
925 char *silc_client_chmode(uint32 mode, const char *cipher, const char *hmac)
926 {
927   char string[100];
928
929   if (!mode)
930     return NULL;
931
932   memset(string, 0, sizeof(string));
933
934   if (mode & SILC_CHANNEL_MODE_PRIVATE)
935     strncat(string, "p", 1);
936
937   if (mode & SILC_CHANNEL_MODE_SECRET)
938     strncat(string, "s", 1);
939
940   if (mode & SILC_CHANNEL_MODE_PRIVKEY)
941     strncat(string, "k", 1);
942
943   if (mode & SILC_CHANNEL_MODE_INVITE)
944     strncat(string, "i", 1);
945
946   if (mode & SILC_CHANNEL_MODE_TOPIC)
947     strncat(string, "t", 1);
948
949   if (mode & SILC_CHANNEL_MODE_ULIMIT)
950     strncat(string, "l", 1);
951
952   if (mode & SILC_CHANNEL_MODE_PASSPHRASE)
953     strncat(string, "a", 1);
954
955   if (mode & SILC_CHANNEL_MODE_FOUNDER_AUTH)
956     strncat(string, "f", 1);
957
958   if (mode & SILC_CHANNEL_MODE_CIPHER)
959     strncat(string, cipher, strlen(cipher));
960
961   if (mode & SILC_CHANNEL_MODE_HMAC)
962     strncat(string, hmac, strlen(hmac));
963
964   /* Rest of mode is ignored */
965
966   return strdup(string);
967 }
968
969 /* Parses channel user mode mask and returns te mode as string */
970
971 char *silc_client_chumode(uint32 mode)
972 {
973   char string[4];
974
975   if (!mode)
976     return NULL;
977
978   memset(string, 0, sizeof(string));
979
980   if (mode & SILC_CHANNEL_UMODE_CHANFO)
981     strncat(string, "f", 1);
982
983   if (mode & SILC_CHANNEL_UMODE_CHANOP)
984     strncat(string, "o", 1);
985
986   return strdup(string);
987 }
988
989 /* Parses channel user mode and returns it as special mode character. */
990
991 char *silc_client_chumode_char(uint32 mode)
992 {
993   char string[4];
994
995   if (!mode)
996     return NULL;
997
998   memset(string, 0, sizeof(string));
999
1000   if (mode & SILC_CHANNEL_UMODE_CHANFO)
1001     strncat(string, "*", 1);
1002
1003   if (mode & SILC_CHANNEL_UMODE_CHANOP)
1004     strncat(string, "@", 1);
1005
1006   return strdup(string);
1007 }