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   logname = getenv("LOGNAME");
735   if (!logname) {
736     logname = getlogin();
737     if (!logname) {
738       struct passwd *pw;
739
740       pw = getpwuid(getuid());
741       if (!pw) {
742         fprintf(stderr, "silc_get_username: %s\n", strerror(errno));
743         return NULL;
744       }
745       
746       logname = pw->pw_name;
747     }
748   }
749   
750   return strdup(logname);
751 }                          
752
753 /* Returns the real name of ther user. */
754
755 char *silc_get_real_name()
756 {
757   char *realname = NULL;
758   struct passwd *pw;
759     
760   pw = getpwuid(getuid());
761   if (!pw) {
762     fprintf(stderr, "silc_get_username: %s\n", strerror(errno));
763     return NULL;
764   }
765
766   if (strchr(pw->pw_gecos, ','))
767     *strchr(pw->pw_gecos, ',') = 0;
768
769   realname = strdup(pw->pw_gecos);
770
771   return realname;
772 }
773
774 /* Basic has function to hash strings. May be used with the SilcHashTable. 
775    Note that this lowers the characters of the string (with tolower()) so
776    this is used usually with nicknames, channel and server names to provide
777    case insensitive keys. */
778
779 uint32 silc_hash_string(void *key, void *user_context)
780 {
781   char *s = (char *)key;
782   uint32 h = 0, g;
783   
784   while (*s != '\0') {
785     h = (h << 4) + tolower(*s);
786     if ((g = h & 0xf0000000)) {
787       h = h ^ (g >> 24);
788       h = h ^ g;
789     }
790     s++;
791   }
792   
793   return h;
794 }
795
796 /* Basic hash function to hash integers. May be used with the SilcHashTable. */
797
798 uint32 silc_hash_uint(void *key, void *user_context)
799 {
800   return *(uint32 *)key;
801 }
802
803 /* Basic hash funtion to hash pointers. May be used with the SilcHashTable. */
804
805 uint32 silc_hash_ptr(void *key, void *user_context)
806 {
807   return (uint32)key;
808 }
809
810 /* Hash a ID. The `user_context' is the ID type. */
811
812 uint32 silc_hash_id(void *key, void *user_context)
813 {
814   SilcIdType id_type = (SilcIdType)(uint32)user_context;
815   uint32 h = 0;
816   int i;
817
818   switch (id_type) {
819   case SILC_ID_CLIENT:
820     {
821       SilcClientID *id = (SilcClientID *)key;
822       uint32 g;
823   
824       /* The client ID is hashed by hashing the hash of the ID 
825          (which is a truncated MD5 hash of the nickname) so that we
826          can access the entry from the cache with both Client ID but
827          with just a hash from the ID as well. */
828
829       for (i = 0; i < sizeof(id->hash); i++) {
830         h = (h << 4) + id->hash[i];
831         if ((g = h & 0xf0000000)) {
832           h = h ^ (g >> 24);
833           h = h ^ g;
834         }
835       }
836
837       return h;
838     }
839     break;
840   case SILC_ID_SERVER:
841     {
842       SilcServerID *id = (SilcServerID *)key;
843       
844       h = id->port * id->rnd;
845       for (i = 0; i < id->ip.data_len; i++)
846         h ^= id->ip.data[i];
847       
848       return h;
849     }
850     break;
851   case SILC_ID_CHANNEL:
852     {
853       SilcChannelID *id = (SilcChannelID *)key;
854       
855       h = id->port * id->rnd;
856       for (i = 0; i < id->ip.data_len; i++)
857         h ^= id->ip.data[i];
858       
859       return h;
860     }
861     break;
862   default:
863     break;
864   }
865
866   return h;
867 }
868
869 /* Hash binary data. The `user_context' is the data length. */
870
871 uint32 silc_hash_data(void *key, void *user_context)
872 {
873   uint32 len = (uint32)user_context, h = 0;
874   unsigned char *data = (unsigned char *)key;
875   int i;
876
877   h = (data[0] * data[len - 1] + 1) * len;
878   for (i = 0; i < len; i++)
879     h ^= data[i];
880
881   return h;
882 }
883
884 /* Compares two strings. May be used as SilcHashTable comparison function. */
885
886 bool silc_hash_string_compare(void *key1, void *key2, void *user_context)
887 {
888   return !strcasecmp((char *)key1, (char *)key2);
889 }
890
891 /* Compares two ID's. May be used as SilcHashTable comparison function. 
892    The Client ID's compares only the hash of the Client ID not any other
893    part of the Client ID. Other ID's are fully compared. */
894
895 bool silc_hash_id_compare(void *key1, void *key2, void *user_context)
896 {
897   SilcIdType id_type = (SilcIdType)(uint32)user_context;
898   return (id_type == SILC_ID_CLIENT ? 
899           SILC_ID_COMPARE_HASH((SilcClientID *)key1, (SilcClientID *)key2) :
900           SILC_ID_COMPARE_TYPE(key1, key2, id_type));
901 }
902
903 /* Compare two Client ID's entirely and not just the hash from the ID. */
904
905 bool silc_hash_client_id_compare(void *key1, void *key2, void *user_context)
906 {
907   return SILC_ID_COMPARE_TYPE(key1, key2, SILC_ID_CLIENT);
908 }
909
910 /* Compares binary data. May be used as SilcHashTable comparison function. */
911
912 bool silc_hash_data_compare(void *key1, void *key2, void *user_context)
913 {
914   uint32 len = (uint32)user_context;
915   return !memcmp(key1, key2, len);
916 }
917
918 /* Parses mode mask and returns the mode as string. */
919
920 char *silc_client_chmode(uint32 mode, const char *cipher, const char *hmac)
921 {
922   char string[100];
923
924   if (!mode)
925     return NULL;
926
927   memset(string, 0, sizeof(string));
928
929   if (mode & SILC_CHANNEL_MODE_PRIVATE)
930     strncat(string, "p", 1);
931
932   if (mode & SILC_CHANNEL_MODE_SECRET)
933     strncat(string, "s", 1);
934
935   if (mode & SILC_CHANNEL_MODE_PRIVKEY)
936     strncat(string, "k", 1);
937
938   if (mode & SILC_CHANNEL_MODE_INVITE)
939     strncat(string, "i", 1);
940
941   if (mode & SILC_CHANNEL_MODE_TOPIC)
942     strncat(string, "t", 1);
943
944   if (mode & SILC_CHANNEL_MODE_ULIMIT)
945     strncat(string, "l", 1);
946
947   if (mode & SILC_CHANNEL_MODE_PASSPHRASE)
948     strncat(string, "a", 1);
949
950   if (mode & SILC_CHANNEL_MODE_FOUNDER_AUTH)
951     strncat(string, "f", 1);
952
953   if (mode & SILC_CHANNEL_MODE_CIPHER)
954     strncat(string, cipher, strlen(cipher));
955
956   if (mode & SILC_CHANNEL_MODE_HMAC)
957     strncat(string, hmac, strlen(hmac));
958
959   /* Rest of mode is ignored */
960
961   return strdup(string);
962 }
963
964 /* Parses channel user mode mask and returns te mode as string */
965
966 char *silc_client_chumode(uint32 mode)
967 {
968   char string[4];
969
970   if (!mode)
971     return NULL;
972
973   memset(string, 0, sizeof(string));
974
975   if (mode & SILC_CHANNEL_UMODE_CHANFO)
976     strncat(string, "f", 1);
977
978   if (mode & SILC_CHANNEL_UMODE_CHANOP)
979     strncat(string, "o", 1);
980
981   return strdup(string);
982 }
983
984 /* Parses channel user mode and returns it as special mode character. */
985
986 char *silc_client_chumode_char(uint32 mode)
987 {
988   char string[4];
989
990   if (!mode)
991     return NULL;
992
993   memset(string, 0, sizeof(string));
994
995   if (mode & SILC_CHANNEL_UMODE_CHANFO)
996     strncat(string, "*", 1);
997
998   if (mode & SILC_CHANNEL_UMODE_CHANOP)
999     strncat(string, "@", 1);
1000
1001   return strdup(string);
1002 }