robodocumented lib/silcutil/silcutil.h.
[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_CIPHER)
773     strncat(string, cipher, strlen(cipher));
774
775   if (mode & SILC_CHANNEL_MODE_HMAC)
776     strncat(string, hmac, strlen(hmac));
777
778   /* Rest of mode is ignored */
779
780   return strdup(string);
781 }
782
783 /* Parses channel user mode mask and returns te mode as string */
784
785 char *silc_client_chumode(SilcUInt32 mode)
786 {
787   char string[4];
788
789   if (!mode)
790     return NULL;
791
792   memset(string, 0, sizeof(string));
793
794   if (mode & SILC_CHANNEL_UMODE_CHANFO)
795     strncat(string, "f", 1);
796
797   if (mode & SILC_CHANNEL_UMODE_CHANOP)
798     strncat(string, "o", 1);
799
800   return strdup(string);
801 }
802
803 /* Parses channel user mode and returns it as special mode character. */
804
805 char *silc_client_chumode_char(SilcUInt32 mode)
806 {
807   char string[4];
808
809   if (!mode)
810     return NULL;
811
812   memset(string, 0, sizeof(string));
813
814   if (mode & SILC_CHANNEL_UMODE_CHANFO)
815     strncat(string, "*", 1);
816
817   if (mode & SILC_CHANNEL_UMODE_CHANOP)
818     strncat(string, "@", 1);
819
820   return strdup(string);
821 }
822
823 /* Creates fingerprint from data, usually used with SHA1 digests */
824
825 char *silc_fingerprint(const unsigned char *data, SilcUInt32 data_len)
826 {
827   char fingerprint[64], *cp;
828   int i;
829
830   memset(fingerprint, 0, sizeof(fingerprint));
831   cp = fingerprint;
832   for (i = 0; i < data_len; i++) {
833     snprintf(cp, sizeof(fingerprint), "%02X", data[i]);
834     cp += 2;
835
836     if ((i + 1) % 2 == 0)
837       snprintf(cp++, sizeof(fingerprint), " ");
838
839     if ((i + 1) % 10 == 0)
840       snprintf(cp++, sizeof(fingerprint), " ");
841   }
842   i--;
843   if ((i + 1) % 2 == 0)
844     cp[-2] = 0;
845   if ((i + 1) % 10 == 0)
846     cp[-1] = 0;
847
848   return strdup(fingerprint);
849 }
850
851 /* Return TRUE if the `data' is ASCII string. */
852
853 bool silc_string_is_ascii(const unsigned char *data, SilcUInt32 data_len)
854 {
855   int i;
856
857   for (i = 0; i < data_len; i++) {
858     if (!isascii(data[i]))
859       return FALSE;
860   }
861
862   return TRUE;
863 }
864
865 /* Parses SILC protocol style version string. */
866
867 bool silc_parse_version_string(const char *version,
868                                SilcUInt32 *protocol_version,
869                                char **protocol_version_string,
870                                SilcUInt32 *software_version,
871                                char **software_version_string,
872                                char **vendor_version)
873 {
874   char *cp, buf[32];
875   int maj = 0, min = 0;
876
877   if (!strstr(version, "SILC-"))
878     return FALSE;
879
880   cp = (char *)version + 5;
881   if (!cp)
882     return FALSE;
883
884   /* Take protocol version */
885
886   maj = atoi(cp);
887   cp = strchr(cp, '.');
888   if (cp) {
889     min = atoi(cp + 1);
890     cp++;
891   }
892
893   memset(buf, 0, sizeof(buf));
894   snprintf(buf, sizeof(buf) - 1, "%d%d", maj, min);
895   if (protocol_version)
896     *protocol_version = atoi(buf);
897   memset(buf, 0, sizeof(buf));
898   snprintf(buf, sizeof(buf) - 1, "%d.%d", maj, min);
899   if (protocol_version_string)
900     *protocol_version_string = strdup(buf);
901
902   /* Take software version */
903
904   maj = 0;
905   min = 0;
906   cp = strchr(cp, '-');
907   if (!cp)
908     return FALSE;
909
910   maj = atoi(cp + 1);
911   cp = strchr(cp, '.');
912   if (cp)
913     min = atoi(cp + 1);
914
915   memset(buf, 0, sizeof(buf));
916   snprintf(buf, sizeof(buf) - 1, "%d%d", maj, min);
917   if (software_version)
918     *software_version = atoi(buf);
919   memset(buf, 0, sizeof(buf));
920   snprintf(buf, sizeof(buf) - 1, "%d.%d", maj, min);
921   if (software_version_string)
922     *software_version_string = strdup(buf);
923
924   /* Take vendor string */
925
926   cp++;
927   if (cp) {
928     cp = strchr(cp, '.');
929     if (cp && cp + 1 && vendor_version)
930       *vendor_version = strdup(cp + 1);
931   }
932
933   return TRUE;
934 }
935
936 /* Converts version string x.x into number representation. */
937
938 SilcUInt32 silc_version_to_num(const char *version)
939 {
940   int maj = 0, min = 0;
941   char *cp, buf[32];
942
943   if (!version)
944     return 0;
945
946   cp = (char *)version;
947   maj = atoi(cp);
948   cp = strchr(cp, '.');
949   if (cp)
950     min = atoi(cp + 1);
951
952   memset(buf, 0, sizeof(buf));
953   snprintf(buf, sizeof(buf) - 1, "%d%d", maj, min);
954   return (SilcUInt32)atoi(buf);
955 }
956
957 /* Displays input prompt on command line and takes input data from user */
958
959 char *silc_get_input(const char *prompt, bool echo_off)
960 {
961 #ifdef SILC_UNIX
962   if (echo_off) {
963     char *ret = NULL;
964 #ifdef HAVE_TERMIOS_H
965     char input[2048];
966     int fd;
967     struct termios to;
968     struct termios to_old;
969
970     fd = open("/dev/tty", O_RDONLY);
971     if (fd < 0) {
972       fprintf(stderr, "silc: %s\n", strerror(errno));
973       return NULL;
974     }
975
976     signal(SIGINT, SIG_IGN);
977
978     /* Get terminal info */
979     tcgetattr(fd, &to);
980     to_old = to;
981
982     /* Echo OFF */
983     to.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
984     tcsetattr(fd, TCSANOW, &to);
985
986     memset(input, 0, sizeof(input));
987
988     printf("%s", prompt);
989     fflush(stdout);
990
991     if ((read(fd, input, sizeof(input))) < 0) {
992       fprintf(stderr, "silc: %s\n", strerror(errno));
993       return NULL;
994     }
995
996     if (strlen(input) <= 1) {
997       tcsetattr(fd, TCSANOW, &to_old);
998       return NULL;
999     }
1000
1001     if (strchr(input, '\n'))
1002       *strchr(input, '\n') = '\0';
1003
1004     /* Restore old terminfo */
1005     tcsetattr(fd, TCSANOW, &to_old);
1006     signal(SIGINT, SIG_DFL);
1007
1008     ret = silc_calloc(strlen(input), sizeof(char));
1009     memcpy(ret, input, strlen(input));
1010     memset(input, 0, sizeof(input));
1011 #endif /* HAVE_TERMIOS_H */
1012     return ret;
1013   } else {
1014     char input[2048];
1015     int fd;
1016
1017     fd = open("/dev/tty", O_RDONLY);
1018     if (fd < 0) {
1019       fprintf(stderr, "silc: %s\n", strerror(errno));
1020       return NULL;
1021     }
1022
1023     memset(input, 0, sizeof(input));
1024
1025     printf("%s", prompt);
1026     fflush(stdout);
1027
1028     if ((read(fd, input, sizeof(input))) < 0) {
1029       fprintf(stderr, "silc: %s\n", strerror(errno));
1030       return NULL;
1031     }
1032
1033     if (strlen(input) <= 1)
1034       return NULL;
1035
1036     if (strchr(input, '\n'))
1037       *strchr(input, '\n') = '\0';
1038
1039     return strdup(input);
1040   }
1041 #else
1042   return NULL;
1043 #endif /* SILC_UNIX */
1044 }