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   if (mode & SILC_CHANNEL_UMODE_BLOCK_MESSAGES)
656     strncat(string, "b", 1);
657
658   return strdup(string);
659 }
660
661 /* Parses channel user mode and returns it as special mode character. */
662
663 char *silc_client_chumode_char(SilcUInt32 mode)
664 {
665   char string[4];
666
667   if (!mode)
668     return NULL;
669
670   memset(string, 0, sizeof(string));
671
672   if (mode & SILC_CHANNEL_UMODE_CHANFO)
673     strncat(string, "*", 1);
674
675   if (mode & SILC_CHANNEL_UMODE_CHANOP)
676     strncat(string, "@", 1);
677
678   return strdup(string);
679 }
680
681 /* Creates fingerprint from data, usually used with SHA1 digests */
682
683 char *silc_fingerprint(const unsigned char *data, SilcUInt32 data_len)
684 {
685   char fingerprint[64], *cp;
686   int i;
687
688   memset(fingerprint, 0, sizeof(fingerprint));
689   cp = fingerprint;
690   for (i = 0; i < data_len; i++) {
691     snprintf(cp, sizeof(fingerprint), "%02X", data[i]);
692     cp += 2;
693
694     if ((i + 1) % 2 == 0)
695       snprintf(cp++, sizeof(fingerprint), " ");
696
697     if ((i + 1) % 10 == 0)
698       snprintf(cp++, sizeof(fingerprint), " ");
699   }
700   i--;
701   if ((i + 1) % 2 == 0)
702     cp[-2] = 0;
703   if ((i + 1) % 10 == 0)
704     cp[-1] = 0;
705
706   return strdup(fingerprint);
707 }
708
709 /* Return TRUE if the `data' is ASCII string. */
710
711 bool silc_string_is_ascii(const unsigned char *data, SilcUInt32 data_len)
712 {
713   int i;
714
715   for (i = 0; i < data_len; i++) {
716     if (!isascii(data[i]))
717       return FALSE;
718   }
719
720   return TRUE;
721 }
722
723 /* Parses SILC protocol style version string. */
724
725 bool silc_parse_version_string(const char *version,
726                                SilcUInt32 *protocol_version,
727                                char **protocol_version_string,
728                                SilcUInt32 *software_version,
729                                char **software_version_string,
730                                char **vendor_version)
731 {
732   char *cp, buf[32];
733   int maj = 0, min = 0;
734
735   if (!strstr(version, "SILC-"))
736     return FALSE;
737
738   cp = (char *)version + 5;
739   if (!cp)
740     return FALSE;
741
742   /* Take protocol version */
743
744   maj = atoi(cp);
745   cp = strchr(cp, '.');
746   if (cp) {
747     min = atoi(cp + 1);
748     cp++;
749   }
750
751   memset(buf, 0, sizeof(buf));
752   snprintf(buf, sizeof(buf) - 1, "%d%d", maj, min);
753   if (protocol_version)
754     *protocol_version = atoi(buf);
755   memset(buf, 0, sizeof(buf));
756   snprintf(buf, sizeof(buf) - 1, "%d.%d", maj, min);
757   if (protocol_version_string)
758     *protocol_version_string = strdup(buf);
759
760   /* Take software version */
761
762   maj = 0;
763   min = 0;
764   cp = strchr(cp, '-');
765   if (!cp)
766     return FALSE;
767
768   maj = atoi(cp + 1);
769   cp = strchr(cp, '.');
770   if (cp)
771     min = atoi(cp + 1);
772
773   memset(buf, 0, sizeof(buf));
774   snprintf(buf, sizeof(buf) - 1, "%d%d", maj, min);
775   if (software_version)
776     *software_version = atoi(buf);
777   memset(buf, 0, sizeof(buf));
778   snprintf(buf, sizeof(buf) - 1, "%d.%d", maj, min);
779   if (software_version_string)
780     *software_version_string = strdup(buf);
781
782   /* Take vendor string */
783
784   cp++;
785   if (cp) {
786     cp = strchr(cp, '.');
787     if (cp && cp + 1 && vendor_version)
788       *vendor_version = strdup(cp + 1);
789   }
790
791   return TRUE;
792 }
793
794 /* Converts version string x.x into number representation. */
795
796 SilcUInt32 silc_version_to_num(const char *version)
797 {
798   int maj = 0, min = 0;
799   char *cp, buf[32];
800
801   if (!version)
802     return 0;
803
804   cp = (char *)version;
805   maj = atoi(cp);
806   cp = strchr(cp, '.');
807   if (cp)
808     min = atoi(cp + 1);
809
810   memset(buf, 0, sizeof(buf));
811   snprintf(buf, sizeof(buf) - 1, "%d%d", maj, min);
812   return (SilcUInt32)atoi(buf);
813 }
814
815 /* Displays input prompt on command line and takes input data from user */
816
817 char *silc_get_input(const char *prompt, bool echo_off)
818 {
819 #ifdef SILC_UNIX
820   if (echo_off) {
821     char *ret = NULL;
822 #ifdef HAVE_TERMIOS_H
823     char input[2048];
824     int fd;
825     struct termios to;
826     struct termios to_old;
827
828     fd = open("/dev/tty", O_RDONLY);
829     if (fd < 0) {
830       fprintf(stderr, "silc: %s\n", strerror(errno));
831       return NULL;
832     }
833
834     signal(SIGINT, SIG_IGN);
835
836     /* Get terminal info */
837     tcgetattr(fd, &to);
838     to_old = to;
839
840     /* Echo OFF */
841     to.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
842     tcsetattr(fd, TCSANOW, &to);
843
844     memset(input, 0, sizeof(input));
845
846     printf("%s", prompt);
847     fflush(stdout);
848
849     if ((read(fd, input, sizeof(input))) < 0) {
850       fprintf(stderr, "silc: %s\n", strerror(errno));
851       return NULL;
852     }
853
854     if (strlen(input) <= 1) {
855       tcsetattr(fd, TCSANOW, &to_old);
856       return NULL;
857     }
858
859     if (strchr(input, '\n'))
860       *strchr(input, '\n') = '\0';
861
862     /* Restore old terminfo */
863     tcsetattr(fd, TCSANOW, &to_old);
864     signal(SIGINT, SIG_DFL);
865
866     ret = silc_calloc(strlen(input), sizeof(char));
867     memcpy(ret, input, strlen(input));
868     memset(input, 0, sizeof(input));
869 #endif /* HAVE_TERMIOS_H */
870     return ret;
871   } else {
872     char input[2048];
873     int fd;
874
875     fd = open("/dev/tty", O_RDONLY);
876     if (fd < 0) {
877       fprintf(stderr, "silc: %s\n", strerror(errno));
878       return NULL;
879     }
880
881     memset(input, 0, sizeof(input));
882
883     printf("%s", prompt);
884     fflush(stdout);
885
886     if ((read(fd, input, sizeof(input))) < 0) {
887       fprintf(stderr, "silc: %s\n", strerror(errno));
888       return NULL;
889     }
890
891     if (strlen(input) <= 1)
892       return NULL;
893
894     if (strchr(input, '\n'))
895       *strchr(input, '\n') = '\0';
896
897     return strdup(input);
898   }
899 #else
900   return NULL;
901 #endif /* SILC_UNIX */
902 }
903
904 /* Return mode list */
905
906 bool silc_get_mode_list(SilcBuffer mode_list, SilcUInt32 mode_list_count,
907                         SilcUInt32 **list)
908 {
909   int i;
910
911   if (mode_list->len / 4 != mode_list_count)
912     return FALSE;
913
914   *list = silc_calloc(mode_list_count, sizeof(**list));
915
916   for (i = 0; i < mode_list_count; i++) {
917     SILC_GET32_MSB((*list)[i], mode_list->data);
918     silc_buffer_pull(mode_list, 4);
919   }
920
921   silc_buffer_push(mode_list, mode_list->data - mode_list->head);
922
923   return TRUE;
924 }