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