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