Started implementing protocol version 1.1 and narrowing down
[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 #undef _PUT_STRING
500
501 /* Compares two strings. Strings may include wildcards '*' and '?'.
502    Returns TRUE if strings match. */
503
504 int silc_string_compare(char *string1, char *string2)
505 {
506   int i;
507   int slen1;
508   int slen2;
509   char *tmpstr1, *tmpstr2;
510
511   if (!string1 || !string2)
512     return FALSE;
513
514   slen1 = strlen(string1);
515   slen2 = strlen(string2);
516
517   /* See if they are same already */
518   if (!strncmp(string1, string2, strlen(string2)))
519     return TRUE;
520
521   if (slen2 < slen1)
522     if (!strchr(string1, '*'))
523       return FALSE;
524
525   /* Take copies of the original strings as we will change them */
526   tmpstr1 = silc_calloc(slen1 + 1, sizeof(char));
527   memcpy(tmpstr1, string1, slen1);
528   tmpstr2 = silc_calloc(slen2 + 1, sizeof(char));
529   memcpy(tmpstr2, string2, slen2);
530
531   for (i = 0; i < slen1; i++) {
532
533     /* * wildcard. Only one * wildcard is possible. */
534     if (tmpstr1[i] == '*')
535       if (!strncmp(tmpstr1, tmpstr2, i)) {
536         memset(tmpstr2, 0, slen2);
537         strncpy(tmpstr2, tmpstr1, i);
538         break;
539       }
540
541     /* ? wildcard */
542     if (tmpstr1[i] == '?') {
543       if (!strncmp(tmpstr1, tmpstr2, i)) {
544         if (!(slen1 < i + 1))
545           if (tmpstr1[i + 1] != '?' &&
546               tmpstr1[i + 1] != tmpstr2[i + 1])
547             continue;
548
549         if (!(slen1 < slen2))
550           tmpstr2[i] = '?';
551       }
552     }
553   }
554
555   /* if using *, remove it */
556   if (strchr(tmpstr1, '*'))
557     *strchr(tmpstr1, '*') = 0;
558
559   if (!strcmp(tmpstr1, tmpstr2)) {
560     memset(tmpstr1, 0, slen1);
561     memset(tmpstr2, 0, slen2);
562     silc_free(tmpstr1);
563     silc_free(tmpstr2);
564     return TRUE;
565   }
566
567   memset(tmpstr1, 0, slen1);
568   memset(tmpstr2, 0, slen2);
569   silc_free(tmpstr1);
570   silc_free(tmpstr2);
571   return FALSE;
572 }
573
574 /* Basic has function to hash strings. May be used with the SilcHashTable.
575    Note that this lowers the characters of the string (with tolower()) so
576    this is used usually with nicknames, channel and server names to provide
577    case insensitive keys. */
578
579 SilcUInt32 silc_hash_string(void *key, void *user_context)
580 {
581   char *s = (char *)key;
582   SilcUInt32 h = 0, g;
583
584   while (*s != '\0') {
585     h = (h << 4) + tolower(*s);
586     if ((g = h & 0xf0000000)) {
587       h = h ^ (g >> 24);
588       h = h ^ g;
589     }
590     s++;
591   }
592
593   return h;
594 }
595
596 /* Basic hash function to hash integers. May be used with the SilcHashTable. */
597
598 SilcUInt32 silc_hash_uint(void *key, void *user_context)
599 {
600   return *(SilcUInt32 *)key;
601 }
602
603 /* Basic hash funtion to hash pointers. May be used with the SilcHashTable. */
604
605 SilcUInt32 silc_hash_ptr(void *key, void *user_context)
606 {
607   return (SilcUInt32)key;
608 }
609
610 /* Hash a ID. The `user_context' is the ID type. */
611
612 SilcUInt32 silc_hash_id(void *key, void *user_context)
613 {
614   SilcIdType id_type = (SilcIdType)(SilcUInt32)user_context;
615   SilcUInt32 h = 0;
616   int i;
617
618   switch (id_type) {
619   case SILC_ID_CLIENT:
620     {
621       SilcClientID *id = (SilcClientID *)key;
622       SilcUInt32 g;
623
624       /* The client ID is hashed by hashing the hash of the ID
625          (which is a truncated MD5 hash of the nickname) so that we
626          can access the entry from the cache with both Client ID but
627          with just a hash from the ID as well. */
628
629       for (i = 0; i < sizeof(id->hash); i++) {
630         h = (h << 4) + id->hash[i];
631         if ((g = h & 0xf0000000)) {
632           h = h ^ (g >> 24);
633           h = h ^ g;
634         }
635       }
636
637       return h;
638     }
639     break;
640   case SILC_ID_SERVER:
641     {
642       SilcServerID *id = (SilcServerID *)key;
643
644       h = id->port * id->rnd;
645       for (i = 0; i < id->ip.data_len; i++)
646         h ^= id->ip.data[i];
647
648       return h;
649     }
650     break;
651   case SILC_ID_CHANNEL:
652     {
653       SilcChannelID *id = (SilcChannelID *)key;
654
655       h = id->port * id->rnd;
656       for (i = 0; i < id->ip.data_len; i++)
657         h ^= id->ip.data[i];
658
659       return h;
660     }
661     break;
662   default:
663     break;
664   }
665
666   return h;
667 }
668
669 /* Hash binary data. The `user_context' is the data length. */
670
671 SilcUInt32 silc_hash_data(void *key, void *user_context)
672 {
673   SilcUInt32 len = (SilcUInt32)user_context, h = 0;
674   unsigned char *data = (unsigned char *)key;
675   int i;
676
677   h = (data[0] * data[len - 1] + 1) * len;
678   for (i = 0; i < len; i++)
679     h ^= data[i];
680
681   return h;
682 }
683
684 /* Hashed SILC Public key. */
685
686 SilcUInt32 silc_hash_public_key(void *key, void *user_context)
687 {
688   SilcPublicKey pk = (SilcPublicKey)key;
689   return (pk->len + silc_hash_string(pk->name, NULL) +
690           silc_hash_string(pk->identifier, NULL) +
691           silc_hash_data(pk->pk, (void *)pk->pk_len));
692 }
693
694 /* Compares two strings. It may be used as SilcHashTable comparison
695    function. */
696
697 bool silc_hash_string_compare(void *key1, void *key2, void *user_context)
698 {
699   return !strcasecmp((char *)key1, (char *)key2);
700 }
701
702 /* Compares two ID's. May be used as SilcHashTable comparison function.
703    The Client ID's compares only the hash of the Client ID not any other
704    part of the Client ID. Other ID's are fully compared. */
705
706 bool silc_hash_id_compare(void *key1, void *key2, void *user_context)
707 {
708   SilcIdType id_type = (SilcIdType)(SilcUInt32)user_context;
709   return (id_type == SILC_ID_CLIENT ?
710           SILC_ID_COMPARE_HASH((SilcClientID *)key1, (SilcClientID *)key2) :
711           SILC_ID_COMPARE_TYPE(key1, key2, id_type));
712 }
713
714 /* Compare two Client ID's entirely and not just the hash from the ID. */
715
716 bool silc_hash_client_id_compare(void *key1, void *key2, void *user_context)
717 {
718   return SILC_ID_COMPARE_TYPE(key1, key2, SILC_ID_CLIENT);
719 }
720
721 /* Compares binary data. May be used as SilcHashTable comparison function. */
722
723 bool silc_hash_data_compare(void *key1, void *key2, void *user_context)
724 {
725   SilcUInt32 len = (SilcUInt32)user_context;
726   return !memcmp(key1, key2, len);
727 }
728
729 /* Compares two SILC Public keys. It may be used as SilcHashTable
730    comparison function. */
731
732 bool silc_hash_public_key_compare(void *key1, void *key2, void *user_context)
733 {
734   return silc_pkcs_public_key_compare(key1, key2);
735 }
736
737 /* Parses mode mask and returns the mode as string. */
738
739 char *silc_client_chmode(SilcUInt32 mode, const char *cipher, const char *hmac)
740 {
741   char string[100];
742
743   if (!mode)
744     return NULL;
745
746   memset(string, 0, sizeof(string));
747
748   if (mode & SILC_CHANNEL_MODE_PRIVATE)
749     strncat(string, "p", 1);
750
751   if (mode & SILC_CHANNEL_MODE_SECRET)
752     strncat(string, "s", 1);
753
754   if (mode & SILC_CHANNEL_MODE_PRIVKEY)
755     strncat(string, "k", 1);
756
757   if (mode & SILC_CHANNEL_MODE_INVITE)
758     strncat(string, "i", 1);
759
760   if (mode & SILC_CHANNEL_MODE_TOPIC)
761     strncat(string, "t", 1);
762
763   if (mode & SILC_CHANNEL_MODE_ULIMIT)
764     strncat(string, "l", 1);
765
766   if (mode & SILC_CHANNEL_MODE_PASSPHRASE)
767     strncat(string, "a", 1);
768
769   if (mode & SILC_CHANNEL_MODE_FOUNDER_AUTH)
770     strncat(string, "f", 1);
771
772   if (mode & SILC_CHANNEL_MODE_SILENCE_USERS)
773     strncat(string, "m", 1);
774
775   if (mode & SILC_CHANNEL_MODE_SILENCE_OPERS)
776     strncat(string, "M", 1);
777
778   if (mode & SILC_CHANNEL_MODE_CIPHER)
779     strncat(string, cipher, strlen(cipher));
780
781   if (mode & SILC_CHANNEL_MODE_HMAC)
782     strncat(string, hmac, strlen(hmac));
783
784   /* Rest of mode is ignored */
785
786   return strdup(string);
787 }
788
789 /* Parses channel user mode mask and returns te mode as string */
790
791 char *silc_client_chumode(SilcUInt32 mode)
792 {
793   char string[4];
794
795   if (!mode)
796     return NULL;
797
798   memset(string, 0, sizeof(string));
799
800   if (mode & SILC_CHANNEL_UMODE_CHANFO)
801     strncat(string, "f", 1);
802
803   if (mode & SILC_CHANNEL_UMODE_CHANOP)
804     strncat(string, "o", 1);
805
806   return strdup(string);
807 }
808
809 /* Parses channel user mode and returns it as special mode character. */
810
811 char *silc_client_chumode_char(SilcUInt32 mode)
812 {
813   char string[4];
814
815   if (!mode)
816     return NULL;
817
818   memset(string, 0, sizeof(string));
819
820   if (mode & SILC_CHANNEL_UMODE_CHANFO)
821     strncat(string, "*", 1);
822
823   if (mode & SILC_CHANNEL_UMODE_CHANOP)
824     strncat(string, "@", 1);
825
826   return strdup(string);
827 }
828
829 /* Creates fingerprint from data, usually used with SHA1 digests */
830
831 char *silc_fingerprint(const unsigned char *data, SilcUInt32 data_len)
832 {
833   char fingerprint[64], *cp;
834   int i;
835
836   memset(fingerprint, 0, sizeof(fingerprint));
837   cp = fingerprint;
838   for (i = 0; i < data_len; i++) {
839     snprintf(cp, sizeof(fingerprint), "%02X", data[i]);
840     cp += 2;
841
842     if ((i + 1) % 2 == 0)
843       snprintf(cp++, sizeof(fingerprint), " ");
844
845     if ((i + 1) % 10 == 0)
846       snprintf(cp++, sizeof(fingerprint), " ");
847   }
848   i--;
849   if ((i + 1) % 2 == 0)
850     cp[-2] = 0;
851   if ((i + 1) % 10 == 0)
852     cp[-1] = 0;
853
854   return strdup(fingerprint);
855 }
856
857 /* Return TRUE if the `data' is ASCII string. */
858
859 bool silc_string_is_ascii(const unsigned char *data, SilcUInt32 data_len)
860 {
861   int i;
862
863   for (i = 0; i < data_len; i++) {
864     if (!isascii(data[i]))
865       return FALSE;
866   }
867
868   return TRUE;
869 }
870
871 /* Parses SILC protocol style version string. */
872
873 bool silc_parse_version_string(const char *version,
874                                SilcUInt32 *protocol_version,
875                                char **protocol_version_string,
876                                SilcUInt32 *software_version,
877                                char **software_version_string,
878                                char **vendor_version)
879 {
880   char *cp, buf[32];
881   int maj = 0, min = 0;
882
883   if (!strstr(version, "SILC-"))
884     return FALSE;
885
886   cp = (char *)version + 5;
887   if (!cp)
888     return FALSE;
889
890   /* Take protocol version */
891
892   maj = atoi(cp);
893   cp = strchr(cp, '.');
894   if (cp) {
895     min = atoi(cp + 1);
896     cp++;
897   }
898
899   memset(buf, 0, sizeof(buf));
900   snprintf(buf, sizeof(buf) - 1, "%d%d", maj, min);
901   if (protocol_version)
902     *protocol_version = atoi(buf);
903   memset(buf, 0, sizeof(buf));
904   snprintf(buf, sizeof(buf) - 1, "%d.%d", maj, min);
905   if (protocol_version_string)
906     *protocol_version_string = strdup(buf);
907
908   /* Take software version */
909
910   maj = 0;
911   min = 0;
912   cp = strchr(cp, '-');
913   if (!cp)
914     return FALSE;
915
916   maj = atoi(cp + 1);
917   cp = strchr(cp, '.');
918   if (cp)
919     min = atoi(cp + 1);
920
921   memset(buf, 0, sizeof(buf));
922   snprintf(buf, sizeof(buf) - 1, "%d%d", maj, min);
923   if (software_version)
924     *software_version = atoi(buf);
925   memset(buf, 0, sizeof(buf));
926   snprintf(buf, sizeof(buf) - 1, "%d.%d", maj, min);
927   if (software_version_string)
928     *software_version_string = strdup(buf);
929
930   /* Take vendor string */
931
932   cp++;
933   if (cp) {
934     cp = strchr(cp, '.');
935     if (cp && cp + 1 && vendor_version)
936       *vendor_version = strdup(cp + 1);
937   }
938
939   return TRUE;
940 }
941
942 /* Converts version string x.x into number representation. */
943
944 SilcUInt32 silc_version_to_num(const char *version)
945 {
946   int maj = 0, min = 0;
947   char *cp, buf[32];
948
949   if (!version)
950     return 0;
951
952   cp = (char *)version;
953   maj = atoi(cp);
954   cp = strchr(cp, '.');
955   if (cp)
956     min = atoi(cp + 1);
957
958   memset(buf, 0, sizeof(buf));
959   snprintf(buf, sizeof(buf) - 1, "%d%d", maj, min);
960   return (SilcUInt32)atoi(buf);
961 }
962
963 /* Displays input prompt on command line and takes input data from user */
964
965 char *silc_get_input(const char *prompt, bool echo_off)
966 {
967 #ifdef SILC_UNIX
968   if (echo_off) {
969     char *ret = NULL;
970 #ifdef HAVE_TERMIOS_H
971     char input[2048];
972     int fd;
973     struct termios to;
974     struct termios to_old;
975
976     fd = open("/dev/tty", O_RDONLY);
977     if (fd < 0) {
978       fprintf(stderr, "silc: %s\n", strerror(errno));
979       return NULL;
980     }
981
982     signal(SIGINT, SIG_IGN);
983
984     /* Get terminal info */
985     tcgetattr(fd, &to);
986     to_old = to;
987
988     /* Echo OFF */
989     to.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
990     tcsetattr(fd, TCSANOW, &to);
991
992     memset(input, 0, sizeof(input));
993
994     printf("%s", prompt);
995     fflush(stdout);
996
997     if ((read(fd, input, sizeof(input))) < 0) {
998       fprintf(stderr, "silc: %s\n", strerror(errno));
999       return NULL;
1000     }
1001
1002     if (strlen(input) <= 1) {
1003       tcsetattr(fd, TCSANOW, &to_old);
1004       return NULL;
1005     }
1006
1007     if (strchr(input, '\n'))
1008       *strchr(input, '\n') = '\0';
1009
1010     /* Restore old terminfo */
1011     tcsetattr(fd, TCSANOW, &to_old);
1012     signal(SIGINT, SIG_DFL);
1013
1014     ret = silc_calloc(strlen(input), sizeof(char));
1015     memcpy(ret, input, strlen(input));
1016     memset(input, 0, sizeof(input));
1017 #endif /* HAVE_TERMIOS_H */
1018     return ret;
1019   } else {
1020     char input[2048];
1021     int fd;
1022
1023     fd = open("/dev/tty", O_RDONLY);
1024     if (fd < 0) {
1025       fprintf(stderr, "silc: %s\n", strerror(errno));
1026       return NULL;
1027     }
1028
1029     memset(input, 0, sizeof(input));
1030
1031     printf("%s", prompt);
1032     fflush(stdout);
1033
1034     if ((read(fd, input, sizeof(input))) < 0) {
1035       fprintf(stderr, "silc: %s\n", strerror(errno));
1036       return NULL;
1037     }
1038
1039     if (strlen(input) <= 1)
1040       return NULL;
1041
1042     if (strchr(input, '\n'))
1043       *strchr(input, '\n') = '\0';
1044
1045     return strdup(input);
1046   }
1047 #else
1048   return NULL;
1049 #endif /* SILC_UNIX */
1050 }