Fixed CUMODE mode->mode character conversion buffer overflow.
[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     if (strlen(cipher) + strlen(string) < sizeof(string))
658       strncat(string, cipher, strlen(cipher));
659   }
660
661   if (mode & SILC_CHANNEL_MODE_HMAC) {
662     if (strlen(hmac) + strlen(string) < sizeof(string))
663       strncat(string, hmac, strlen(hmac));
664   }
665
666   /* Rest of mode is ignored */
667
668   return strdup(string);
669 }
670
671 /* Parses channel user mode mask and returns te mode as string */
672
673 char *silc_client_chumode(SilcUInt32 mode)
674 {
675   char string[64];
676
677   if (!mode)
678     return NULL;
679
680   memset(string, 0, sizeof(string));
681
682   if (mode & SILC_CHANNEL_UMODE_CHANFO)
683     strncat(string, "f", 1);
684
685   if (mode & SILC_CHANNEL_UMODE_CHANOP)
686     strncat(string, "o", 1);
687
688   if (mode & SILC_CHANNEL_UMODE_BLOCK_MESSAGES)
689     strncat(string, "b", 1);
690
691   if (mode & SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS)
692     strncat(string, "u", 1);
693
694   if (mode & SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS)
695     strncat(string, "r", 1);
696
697   if (mode & SILC_CHANNEL_UMODE_QUIET)
698     strncat(string, "q", 1);
699
700   return strdup(string);
701 }
702
703 /* Parses channel user mode and returns it as special mode character. */
704
705 char *silc_client_chumode_char(SilcUInt32 mode)
706 {
707   char string[64];
708
709   if (!mode)
710     return NULL;
711
712   memset(string, 0, sizeof(string));
713
714   if (mode & SILC_CHANNEL_UMODE_CHANFO)
715     strncat(string, "*", 1);
716
717   if (mode & SILC_CHANNEL_UMODE_CHANOP)
718     strncat(string, "@", 1);
719
720   if (mode & SILC_CHANNEL_UMODE_QUIET)
721     strncat(string, "&", 1);
722
723   return strdup(string);
724 }
725
726 /* Creates fingerprint from data, usually used with SHA1 digests */
727
728 char *silc_fingerprint(const unsigned char *data, SilcUInt32 data_len)
729 {
730   char fingerprint[64], *cp;
731   int i;
732
733   memset(fingerprint, 0, sizeof(fingerprint));
734   cp = fingerprint;
735   for (i = 0; i < data_len; i++) {
736     snprintf(cp, sizeof(fingerprint), "%02X", data[i]);
737     cp += 2;
738
739     if ((i + 1) % 2 == 0)
740       snprintf(cp++, sizeof(fingerprint), " ");
741
742     if ((i + 1) % 10 == 0)
743       snprintf(cp++, sizeof(fingerprint), " ");
744   }
745   i--;
746   if ((i + 1) % 2 == 0)
747     cp[-2] = 0;
748   if ((i + 1) % 10 == 0)
749     cp[-1] = 0;
750
751   return strdup(fingerprint);
752 }
753
754 /* Return TRUE if the `data' is ASCII string. */
755
756 bool silc_string_is_ascii(const unsigned char *data, SilcUInt32 data_len)
757 {
758   int i;
759
760   for (i = 0; i < data_len; i++) {
761     if (!isascii(data[i]))
762       return FALSE;
763   }
764
765   return TRUE;
766 }
767
768 /* Parses SILC protocol style version string. */
769
770 bool silc_parse_version_string(const char *version,
771                                SilcUInt32 *protocol_version,
772                                char **protocol_version_string,
773                                SilcUInt32 *software_version,
774                                char **software_version_string,
775                                char **vendor_version)
776 {
777   char *cp, buf[32];
778   int maj = 0, min = 0;
779
780   if (!strstr(version, "SILC-"))
781     return FALSE;
782
783   cp = (char *)version + 5;
784   if (!cp)
785     return FALSE;
786
787   /* Take protocol version */
788
789   maj = atoi(cp);
790   if (!strchr(cp, '.'))
791     return FALSE;
792   cp = strchr(cp, '.') + 1;
793   if (!cp || !(*cp))
794     return FALSE;
795   min = atoi(cp);
796
797   memset(buf, 0, sizeof(buf));
798   snprintf(buf, sizeof(buf) - 1, "%d%d", maj, min);
799   if (protocol_version)
800     *protocol_version = atoi(buf);
801   memset(buf, 0, sizeof(buf));
802   snprintf(buf, sizeof(buf) - 1, "%d.%d", maj, min);
803   if (protocol_version_string)
804     *protocol_version_string = strdup(buf);
805
806   /* Take software version */
807
808   maj = 0;
809   min = 0;
810   if (!strchr(cp, '-'))
811     return FALSE;
812   cp = strchr(cp, '-') + 1;
813   if (!cp || !(*cp))
814     return FALSE;
815
816   maj = atoi(cp);
817   if (strchr(cp, '.')) {
818     cp = strchr(cp, '.') + 1;
819     if (cp && *cp)
820       min = atoi(cp);
821   }
822
823   memset(buf, 0, sizeof(buf));
824   snprintf(buf, sizeof(buf) - 1, "%d%d", maj, min);
825   if (software_version)
826     *software_version = atoi(buf);
827   memset(buf, 0, sizeof(buf));
828   snprintf(buf, sizeof(buf) - 1, "%d.%d", maj, min);
829   if (software_version_string)
830     *software_version_string = strdup(buf);
831
832   /* Take vendor string */
833
834   if (strchr(cp, '.')) {
835     cp = strchr(cp, '.') + 1;
836     if (cp && *cp && vendor_version)
837       *vendor_version = strdup(cp);
838   }
839
840   return TRUE;
841 }
842
843 /* Converts version string x.x into number representation. */
844
845 SilcUInt32 silc_version_to_num(const char *version)
846 {
847   int maj = 0, min = 0;
848   char *cp, buf[32];
849
850   if (!version)
851     return 0;
852
853   cp = (char *)version;
854   maj = atoi(cp);
855   cp = strchr(cp, '.');
856   if (cp)
857     min = atoi(cp + 1);
858
859   memset(buf, 0, sizeof(buf));
860   snprintf(buf, sizeof(buf) - 1, "%d%d", maj, min);
861   return (SilcUInt32)atoi(buf);
862 }
863
864 /* Displays input prompt on command line and takes input data from user */
865
866 char *silc_get_input(const char *prompt, bool echo_off)
867 {
868 #ifdef SILC_UNIX
869   if (echo_off) {
870     char *ret = NULL;
871 #ifdef HAVE_TERMIOS_H
872     char input[2048];
873     int fd;
874     struct termios to;
875     struct termios to_old;
876
877     fd = open("/dev/tty", O_RDONLY);
878     if (fd < 0) {
879       fprintf(stderr, "silc: %s\n", strerror(errno));
880       return NULL;
881     }
882
883     signal(SIGINT, SIG_IGN);
884
885     /* Get terminal info */
886     tcgetattr(fd, &to);
887     to_old = to;
888
889     /* Echo OFF */
890     to.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
891     tcsetattr(fd, TCSANOW, &to);
892
893     memset(input, 0, sizeof(input));
894
895     printf("%s", prompt);
896     fflush(stdout);
897
898     if ((read(fd, input, sizeof(input))) < 0) {
899       fprintf(stderr, "silc: %s\n", strerror(errno));
900       return NULL;
901     }
902
903     if (strlen(input) <= 1) {
904       tcsetattr(fd, TCSANOW, &to_old);
905       return NULL;
906     }
907
908     if (strchr(input, '\n'))
909       *strchr(input, '\n') = '\0';
910
911     /* Restore old terminfo */
912     tcsetattr(fd, TCSANOW, &to_old);
913     signal(SIGINT, SIG_DFL);
914
915     ret = silc_calloc(strlen(input), sizeof(char));
916     memcpy(ret, input, strlen(input));
917     memset(input, 0, sizeof(input));
918 #endif /* HAVE_TERMIOS_H */
919     return ret;
920   } else {
921     char input[2048];
922     int fd;
923
924     fd = open("/dev/tty", O_RDONLY);
925     if (fd < 0) {
926       fprintf(stderr, "silc: %s\n", strerror(errno));
927       return NULL;
928     }
929
930     memset(input, 0, sizeof(input));
931
932     printf("%s", prompt);
933     fflush(stdout);
934
935     if ((read(fd, input, sizeof(input))) < 0) {
936       fprintf(stderr, "silc: %s\n", strerror(errno));
937       return NULL;
938     }
939
940     if (strlen(input) <= 1)
941       return NULL;
942
943     if (strchr(input, '\n'))
944       *strchr(input, '\n') = '\0';
945
946     return strdup(input);
947   }
948 #else
949   return NULL;
950 #endif /* SILC_UNIX */
951 }
952
953 /* Return mode list */
954
955 bool silc_get_mode_list(SilcBuffer mode_list, SilcUInt32 mode_list_count,
956                         SilcUInt32 **list)
957 {
958   int i;
959
960   if (mode_list->len / 4 != mode_list_count)
961     return FALSE;
962
963   *list = silc_calloc(mode_list_count, sizeof(**list));
964
965   for (i = 0; i < mode_list_count; i++) {
966     SILC_GET32_MSB((*list)[i], mode_list->data);
967     silc_buffer_pull(mode_list, 4);
968   }
969
970   silc_buffer_push(mode_list, mode_list->data - mode_list->head);
971
972   return TRUE;
973 }
974
975 /* Status message structure. Messages are defined below. */
976 typedef struct {
977   SilcStatus status;
978   const char *message;
979 } SilcStatusMessage;
980
981 #define STAT(x) SILC_STATUS_ERR_##x
982 static const SilcStatusMessage silc_status_messages[] = {
983
984   { STAT(NO_SUCH_NICK),      "There was no such nickname" },
985   { STAT(NO_SUCH_CHANNEL),   "There was no such channel" },
986   { STAT(NO_SUCH_SERVER),    "There was no such server" },
987   { STAT(INCOMPLETE_INFORMATION),  "Incomplete registration information" },
988   { STAT(NO_RECIPIENT),      "No recipient given" },
989   { STAT(UNKNOWN_COMMAND),   "Unknown command" },
990   { STAT(WILDCARDS),         "Wilcrads not allowed" },
991   { STAT(NO_CLIENT_ID),      "No Client ID given" },
992   { STAT(NO_CHANNEL_ID),     "No Channel ID given" },
993   { STAT(NO_SERVER_ID),      "No Server ID given" },
994   { STAT(BAD_CLIENT_ID),     "Bad Client ID" },
995   { STAT(BAD_CHANNEL_ID),    "Bad Channel ID" },
996   { STAT(NO_SUCH_CLIENT_ID), "There is no such client" },
997   { STAT(NO_SUCH_CHANNEL_ID),"There is no such channel" },
998   { STAT(NICKNAME_IN_USE),   "Nickname already exists" },
999   { STAT(NOT_ON_CHANNEL),    "You are not on that channel" },
1000   { STAT(USER_NOT_ON_CHANNEL),"They are not on the channel" },
1001   { STAT(USER_ON_CHANNEL),   "User already on the channel" },
1002   { STAT(NOT_REGISTERED),    "You have not registered" },
1003   { STAT(NOT_ENOUGH_PARAMS), "Not enough parameters" },
1004   { STAT(TOO_MANY_PARAMS),   "Too many parameters" },
1005   { STAT(PERM_DENIED),       "Permission denied" },
1006   { STAT(BANNED_FROM_SERVER),"You are not allowed to connect" },
1007   { STAT(BAD_PASSWORD),      "Cannot join channel. Incorrect password" },
1008   { STAT(CHANNEL_IS_FULL),   "Cannot join channel. Channel is full" },
1009   { STAT(NOT_INVITED),     "Cannot join channel. You have not been invited" },
1010   { STAT(BANNED_FROM_CHANNEL), "Cannot join channel. You have been banned" },
1011   { STAT(UNKNOWN_MODE),    "Unknown mode" },
1012   { STAT(NOT_YOU),         "Cannot change mode for other users" },
1013   { STAT(NO_CHANNEL_PRIV), "Permission denied. You are not channel operator" },
1014   { STAT(NO_CHANNEL_FOPRIV),"Permission denied. You are not channel founder" },
1015   { STAT(NO_SERVER_PRIV),  "Permission denied. You are not server operator" },
1016   { STAT(NO_ROUTER_PRIV),  "Permission denied. You are not SILC operator" },
1017   { STAT(BAD_NICKNAME),    "Bad nickname" },
1018   { STAT(BAD_CHANNEL),     "Bad channel name" },
1019   { STAT(AUTH_FAILED),     "Authentication failed" },
1020   { STAT(UNKNOWN_ALGORITHM), "Unsupported algorithm" },
1021   { STAT(NO_SUCH_SERVER_ID), "No such Server ID" },
1022   { STAT(RESOURCE_LIMIT), "No more free resources" },
1023   { STAT(NO_SUCH_SERVICE), "Service doesn't exist" },
1024   { STAT(NOT_AUTHENTICATED), "You have not been authenticated" },
1025   { STAT(BAD_SERVER_ID), "Server ID is not valid" },
1026   { STAT(KEY_EXCHANGE_FAILED), "Key exchange failed" },
1027   { STAT(BAD_VERSION), "Bad version" },
1028
1029   { 0, NULL }
1030 };
1031
1032 /* Returns status message string */
1033
1034 const char *silc_get_status_message(unsigned char status)
1035 {
1036   int i;
1037
1038   for (i = 0; silc_status_messages[i].message; i++) {
1039     if (silc_status_messages[i].status == status)
1040       break;
1041   }
1042
1043   if (silc_status_messages[i].message == NULL)
1044     return "";
1045
1046   return silc_status_messages[i].message;
1047 }
1048
1049 static const char *packet_name[] = {
1050   "NONE",
1051   "DISCONNECT",
1052   "SUCCESS",
1053   "FAILURE",
1054   "REJECT",
1055   "NOTIFY",
1056   "ERROR",
1057   "CHANNEL MESSAGE",
1058   "CHANNEL KEY",
1059   "PRIVATE MESSAGE",
1060   "PRIVATE MESSAGE KEY",
1061   "COMMAND",
1062   "COMMAND REPLY",
1063   "KEY EXCHANGE",
1064   "KEY EXCHANGE 1",
1065   "KEY EXCHANGE 2",
1066   "CONNECTION AUTH REQUEST",
1067   "CONNECTION AUTH",
1068   "NEW ID",
1069   "NEW CLIENT",
1070   "NEW SERVER",
1071   "NEW CHANNEL",
1072   "REKEY",
1073   "REKEY_DONE",
1074   "HEARTBEAT",
1075   "KEY AGREEMENT",
1076   "RESUME ROUTER",
1077   "FTP",
1078   "RESUME CLIENT",
1079 };
1080
1081 /* Returns packet type name */
1082
1083 const char *silc_get_packet_name(unsigned char type)
1084 {
1085   if (type >= SILC_PACKET_MAX)
1086     return "RESERVED";
1087   if (type >= SILC_PACKET_PRIVATE)
1088     return "PRIVATE RANGE";
1089   if (type > (sizeof(packet_name) / sizeof(*packet_name)))
1090     return "UNKNOWN";
1091   return packet_name[type];
1092 }
1093
1094 static const char *command_name[] = {
1095   "NONE",
1096   "WHOIS",
1097   "WHOWAS",
1098   "IDENTIFY",
1099   "NICK",
1100   "LIST",
1101   "TOPIC",
1102   "INVITE",
1103   "QUIT",
1104   "KILL",
1105   "INFO",
1106   "STATS",
1107   "PING",
1108   "OPER",
1109   "JOIN",
1110   "MOTD",
1111   "UMODE",
1112   "CMODE",
1113   "CUMODE",
1114   "KICK",
1115   "BAN",
1116   "DETACH",
1117   "WATCH",
1118   "SILCOPER",
1119   "LEAVE",
1120   "USERS",
1121   "GETKEY",
1122   "SERVICE",
1123 };
1124
1125 /* Returns command name */
1126
1127 const char *silc_get_command_name(unsigned char command)
1128 {
1129   if (command >= SILC_COMMAND_RESERVED)
1130     return "RESERVED";
1131   if (command >= SILC_COMMAND_PRIVATE)
1132     return "PRIVATE RANGE";
1133   if (command > (sizeof(command_name) / sizeof(*command_name)))
1134     return "UNKNOWN";
1135   return command_name[command];
1136 }