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