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