Merged from silc_1_0_branch.
[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 time as string.  If the the `timeval' is non-zero that
82    value is returned as string.  If it is zero the current time of the
83    local machine is returned. */
84
85 const char *silc_get_time(SilcUInt32 timeval)
86 {
87   time_t curtime;
88   char *return_time;
89
90   if (!timeval)
91     curtime = time(NULL);
92   else
93     curtime = (time_t)timeval;
94   return_time = ctime(&curtime);
95   return_time[strlen(return_time) - 1] = '\0';
96
97   return (const char *)return_time;
98 }
99
100 /* Converts string to capital characters. */
101
102 bool silc_to_upper(const char *string, char *dest, SilcUInt32 dest_size)
103 {
104   int i;
105
106   if (strlen(string) > dest_size)
107     return FALSE;
108
109   for (i = 0; i < strlen(string); i++)
110     dest[i] = toupper(string[i]);
111
112   return TRUE;
113 }
114
115 /* Converts string to lower letter characters. */
116
117 bool silc_to_lower(const char *string, char *dest, SilcUInt32 dest_size)
118 {
119   int i;
120
121   if (strlen(string) > dest_size)
122     return FALSE;
123
124   for (i = 0; i < strlen(string); i++)
125     dest[i] = tolower(string[i]);
126
127   return TRUE;
128 }
129
130 /* Parse userfqdn string which is in user@fqdn format. */
131
132 bool silc_parse_userfqdn(const char *string, char **left, char **right)
133 {
134   SilcUInt32 tlen;
135
136   if (!string)
137     return FALSE;
138
139   if (string[0] == '@') {
140     if (left)
141       *left = strdup(string);
142     return TRUE;
143   }
144
145   if (strchr(string, '@')) {
146     tlen = strcspn(string, "@");
147
148     if (left) {
149       *left = silc_calloc(tlen + 1, sizeof(char));
150       memcpy(*left, string, tlen);
151     }
152
153     if (right) {
154       *right = silc_calloc((strlen(string) - tlen) + 1, sizeof(char));
155       memcpy(*right, string + tlen + 1, strlen(string) - tlen - 1);
156     }
157   } else {
158     if (left)
159       *left = strdup(string);
160   }
161
162   return TRUE;
163 }
164
165 /* Parses command line. At most `max_args' is taken. Rest of the line
166    will be allocated as the last argument if there are more than `max_args'
167    arguments in the line. Note that the command name is counted as one
168    argument and is saved. */
169
170 void silc_parse_command_line(unsigned char *buffer,
171                              unsigned char ***parsed,
172                              SilcUInt32 **parsed_lens,
173                              SilcUInt32 **parsed_types,
174                              SilcUInt32 *parsed_num,
175                              SilcUInt32 max_args)
176 {
177   int i, len = 0;
178   int argc = 0;
179   const char *cp = buffer;
180   char *tmp;
181
182   *parsed = silc_calloc(1, sizeof(**parsed));
183   *parsed_lens = silc_calloc(1, sizeof(**parsed_lens));
184
185   /* Get the command first */
186   len = strcspn(cp, " ");
187   tmp = silc_calloc(strlen(cp) + 1, sizeof(*tmp));
188   if (!tmp)
189     return;
190   silc_to_upper(cp, tmp, strlen(cp));
191   (*parsed)[0] = silc_calloc(len + 1, sizeof(char));
192   memcpy((*parsed)[0], tmp, len);
193   silc_free(tmp);
194   (*parsed_lens)[0] = len;
195   cp += len;
196   while (*cp == ' ')
197     cp++;
198   argc++;
199
200   /* Parse arguments */
201   if (strchr(cp, ' ') || strlen(cp) != 0) {
202     for (i = 1; i < max_args; i++) {
203
204       if (i != max_args - 1)
205         len = strcspn(cp, " ");
206       else
207         len = strlen(cp);
208       while (len && cp[len - 1] == ' ')
209         len--;
210       if (!len)
211         break;
212
213       *parsed = silc_realloc(*parsed, sizeof(**parsed) * (argc + 1));
214       *parsed_lens = silc_realloc(*parsed_lens,
215                                   sizeof(**parsed_lens) * (argc + 1));
216       (*parsed)[argc] = silc_calloc(len + 1, sizeof(char));
217       memcpy((*parsed)[argc], cp, len);
218       (*parsed_lens)[argc] = len;
219       argc++;
220
221       cp += len;
222       if (strlen(cp) == 0)
223         break;
224       else
225         while (*cp == ' ')
226           cp++;
227     }
228   }
229
230   /* Save argument types. Protocol defines all argument types but
231      this implementation makes sure that they are always in correct
232      order hence this simple code. */
233   *parsed_types = silc_calloc(argc, sizeof(**parsed_types));
234   for (i = 0; i < argc; i++)
235     (*parsed_types)[i] = i;
236
237   *parsed_num = argc;
238 }
239
240 /* Formats arguments to a string and returns it after allocating memory
241    for it. It must be remembered to free it later. */
242
243 char *silc_format(char *fmt, ...)
244 {
245   va_list args;
246   static char buf[8192];
247
248   memset(buf, 0, sizeof(buf));
249   va_start(args, fmt);
250   vsnprintf(buf, sizeof(buf) - 1, fmt, args);
251   va_end(args);
252
253   return strdup(buf);
254 }
255
256 /* Renders ID to suitable to print for example to log file. */
257
258 static char rid[256];
259 #define _PUT_STRING(__d__, __s__)                                       \
260 do {                                                                    \
261   int __sp = sizeof(__d__) - 1 - strlen(__d__);                         \
262   if (__sp < strlen(__s__)) {                                           \
263     if (__sp)                                                           \
264       strncat(__d__, __s__, (sizeof(__d__) - 1) - strlen(__d__));       \
265   } else {                                                              \
266     strncat(__d__, __s__, strlen(__s__));                               \
267   }                                                                     \
268 } while(0)
269
270 char *silc_id_render(void *id, SilcUInt16 type)
271 {
272   char tmp[100];
273   unsigned char tmps[2];
274   char *cp;
275
276   memset(rid, 0, sizeof(rid));
277   switch(type) {
278   case SILC_ID_SERVER:
279     {
280       SilcServerID *server_id = (SilcServerID *)id;
281       if (server_id->ip.data_len > 4) {
282 #ifdef HAVE_IPV6
283         struct sockaddr_in6 ipv6;
284         memset(&ipv6, 0, sizeof(ipv6));
285         ipv6.sin6_family = AF_INET6;
286         memmove(&ipv6.sin6_addr, server_id->ip.data, sizeof(ipv6.sin6_addr));
287         if (!getnameinfo((struct sockaddr *)&ipv6, sizeof(ipv6),
288                          tmp, sizeof(tmp) - 1, NULL, 0, NI_NUMERICHOST))
289           _PUT_STRING(rid, tmp);
290 #endif
291       } else {
292         struct in_addr ipv4;
293         memmove(&ipv4.s_addr, server_id->ip.data, 4);
294         cp = inet_ntoa(ipv4);
295         if (cp)
296           _PUT_STRING(rid, cp);
297       }
298
299       memset(tmp, 0, sizeof(tmp));
300       snprintf(tmp, sizeof(tmp) - 1, ",%d,", ntohs(server_id->port));
301       _PUT_STRING(rid, tmp);
302       SILC_PUT16_MSB(server_id->rnd, tmps);
303       memset(tmp, 0, sizeof(tmp));
304       snprintf(tmp, sizeof(tmp) - 1, "[%02x %02x]", tmps[0], tmps[1]);
305       _PUT_STRING(rid, tmp);
306     }
307     break;
308   case SILC_ID_CLIENT:
309     {
310       SilcClientID *client_id = (SilcClientID *)id;
311       if (client_id->ip.data_len > 4) {
312 #ifdef HAVE_IPV6
313         struct sockaddr_in6 ipv6;
314         memset(&ipv6, 0, sizeof(ipv6));
315         ipv6.sin6_family = AF_INET6;
316         memmove(&ipv6.sin6_addr, client_id->ip.data, sizeof(ipv6.sin6_addr));
317         if (!getnameinfo((struct sockaddr *)&ipv6, sizeof(ipv6),
318                          tmp, sizeof(tmp) - 1, NULL, 0, NI_NUMERICHOST))
319           _PUT_STRING(rid, tmp);
320 #endif
321       } else {
322         struct in_addr ipv4;
323         memmove(&ipv4.s_addr, client_id->ip.data, 4);
324         cp = inet_ntoa(ipv4);
325         if (cp)
326           _PUT_STRING(rid, cp);
327       }
328
329       memset(tmp, 0, sizeof(tmp));
330       snprintf(tmp, sizeof(tmp) - 1, ",%02x,", client_id->rnd);
331       _PUT_STRING(rid, tmp);
332       memset(tmp, 0, sizeof(tmp));
333       snprintf(tmp, sizeof(tmp) - 1, "[%02x %02x %02x %02x...]",
334                client_id->hash[0], client_id->hash[1],
335                client_id->hash[2], client_id->hash[3]);
336       _PUT_STRING(rid, tmp);
337     }
338     break;
339   case SILC_ID_CHANNEL:
340     {
341       SilcChannelID *channel_id = (SilcChannelID *)id;
342       if (channel_id->ip.data_len > 4) {
343 #ifdef HAVE_IPV6
344         struct sockaddr_in6 ipv6;
345         memset(&ipv6, 0, sizeof(ipv6));
346         ipv6.sin6_family = AF_INET6;
347         memmove(&ipv6.sin6_addr, channel_id->ip.data, sizeof(ipv6.sin6_addr));
348         if (!getnameinfo((struct sockaddr *)&ipv6, sizeof(ipv6),
349                          tmp, sizeof(tmp) - 1, NULL, 0, NI_NUMERICHOST))
350           _PUT_STRING(rid, tmp);
351 #endif
352       } else {
353         struct in_addr ipv4;
354         memmove(&ipv4.s_addr, channel_id->ip.data, 4);
355         cp = inet_ntoa(ipv4);
356         if (cp)
357           _PUT_STRING(rid, cp);
358       }
359
360       memset(tmp, 0, sizeof(tmp));
361       snprintf(tmp, sizeof(tmp) - 1, ",%d,", ntohs(channel_id->port));
362       _PUT_STRING(rid, tmp);
363       SILC_PUT16_MSB(channel_id->rnd, tmps);
364       memset(tmp, 0, sizeof(tmp));
365       snprintf(tmp, sizeof(tmp) - 1, "[%02x %02x]", tmps[0], tmps[1]);
366       _PUT_STRING(rid, tmp);
367     }
368     break;
369   }
370
371   return rid;
372 }
373 #undef _PUT_STRING
374
375 /* Compares two strings. Strings may include wildcards '*' and '?'.
376    Returns TRUE if strings match. */
377
378 int silc_string_compare(char *string1, char *string2)
379 {
380   int i;
381   int slen1;
382   int slen2;
383   char *tmpstr1, *tmpstr2;
384
385   if (!string1 || !string2)
386     return FALSE;
387
388   slen1 = strlen(string1);
389   slen2 = strlen(string2);
390
391   /* See if they are same already */
392   if (!strncmp(string1, string2, slen2) && slen2 == slen1)
393     return TRUE;
394
395   if (slen2 < slen1)
396     if (!strchr(string1, '*'))
397       return FALSE;
398
399   /* Take copies of the original strings as we will change them */
400   tmpstr1 = silc_calloc(slen1 + 1, sizeof(char));
401   memcpy(tmpstr1, string1, slen1);
402   tmpstr2 = silc_calloc(slen2 + 1, sizeof(char));
403   memcpy(tmpstr2, string2, slen2);
404
405   for (i = 0; i < slen1; i++) {
406
407     /* * wildcard. Only one * wildcard is possible. */
408     if (tmpstr1[i] == '*')
409       if (!strncmp(tmpstr1, tmpstr2, i)) {
410         memset(tmpstr2, 0, slen2);
411         strncpy(tmpstr2, tmpstr1, i);
412         break;
413       }
414
415     /* ? wildcard */
416     if (tmpstr1[i] == '?') {
417       if (!strncmp(tmpstr1, tmpstr2, i)) {
418         if (!(slen1 < i + 1))
419           if (tmpstr1[i + 1] != '?' &&
420               tmpstr1[i + 1] != tmpstr2[i + 1])
421             continue;
422
423         if (!(slen1 < slen2))
424           tmpstr2[i] = '?';
425       }
426     }
427   }
428
429   /* if using *, remove it */
430   if (strchr(tmpstr1, '*'))
431     *strchr(tmpstr1, '*') = 0;
432
433   if (!strcmp(tmpstr1, tmpstr2)) {
434     memset(tmpstr1, 0, slen1);
435     memset(tmpstr2, 0, slen2);
436     silc_free(tmpstr1);
437     silc_free(tmpstr2);
438     return TRUE;
439   }
440
441   memset(tmpstr1, 0, slen1);
442   memset(tmpstr2, 0, slen2);
443   silc_free(tmpstr1);
444   silc_free(tmpstr2);
445   return FALSE;
446 }
447
448 /* Basic has function to hash strings. May be used with the SilcHashTable.
449    Note that this lowers the characters of the string (with tolower()) so
450    this is used usually with nicknames, channel and server names to provide
451    case insensitive keys. */
452
453 SilcUInt32 silc_hash_string(void *key, void *user_context)
454 {
455   char *s = (char *)key;
456   SilcUInt32 h = 0, g;
457
458   while (*s != '\0') {
459     h = (h << 4) + tolower(*s);
460     if ((g = h & 0xf0000000)) {
461       h = h ^ (g >> 24);
462       h = h ^ g;
463     }
464     s++;
465   }
466
467   return h;
468 }
469
470 /* Basic hash function to hash integers. May be used with the SilcHashTable. */
471
472 SilcUInt32 silc_hash_uint(void *key, void *user_context)
473 {
474   return *(SilcUInt32 *)key;
475 }
476
477 /* Basic hash funtion to hash pointers. May be used with the SilcHashTable. */
478
479 SilcUInt32 silc_hash_ptr(void *key, void *user_context)
480 {
481   return (SilcUInt32)key;
482 }
483
484 /* Hash a ID. The `user_context' is the ID type. */
485
486 SilcUInt32 silc_hash_id(void *key, void *user_context)
487 {
488   SilcIdType id_type = (SilcIdType)(SilcUInt32)user_context;
489   SilcUInt32 h = 0;
490   int i;
491
492   switch (id_type) {
493   case SILC_ID_CLIENT:
494     {
495       SilcClientID *id = (SilcClientID *)key;
496
497       /* The client ID is hashed by hashing the hash of the ID
498          (which is a truncated MD5 hash of the nickname) so that we
499          can access the entry from the cache with both Client ID but
500          with just a hash from the ID as well. */
501       return silc_hash_client_id_hash(id->hash, NULL);
502     }
503     break;
504   case SILC_ID_SERVER:
505     {
506       SilcServerID *id = (SilcServerID *)key;
507
508       h = id->port * id->rnd;
509       for (i = 0; i < id->ip.data_len; i++)
510         h ^= id->ip.data[i];
511
512       return h;
513     }
514     break;
515   case SILC_ID_CHANNEL:
516     {
517       SilcChannelID *id = (SilcChannelID *)key;
518
519       h = id->port * id->rnd;
520       for (i = 0; i < id->ip.data_len; i++)
521         h ^= id->ip.data[i];
522
523       return h;
524     }
525     break;
526   default:
527     break;
528   }
529
530   return h;
531 }
532
533 /* Hash Client ID's hash. */
534
535 SilcUInt32 silc_hash_client_id_hash(void *key, void *user_context)
536 {
537   int i;
538   unsigned char *hash = key;
539   SilcUInt32 h = 0, g;
540
541   for (i = 0; i < CLIENTID_HASH_LEN; i++) {
542     h = (h << 4) + hash[i];
543     if ((g = h & 0xf0000000)) {
544       h = h ^ (g >> 24);
545       h = h ^ g;
546     }
547   }
548
549   return h;
550 }
551
552 /* Hash binary data. The `user_context' is the data length. */
553
554 SilcUInt32 silc_hash_data(void *key, void *user_context)
555 {
556   SilcUInt32 len = (SilcUInt32)user_context, h = 0;
557   unsigned char *data = (unsigned char *)key;
558   int i;
559
560   h = (data[0] * data[len - 1] + 1) * len;
561   for (i = 0; i < len; i++)
562     h ^= data[i];
563
564   return h;
565 }
566
567 /* Hashed SILC Public key. */
568
569 SilcUInt32 silc_hash_public_key(void *key, void *user_context)
570 {
571   SilcPublicKey pk = (SilcPublicKey)key;
572   return (pk->len + silc_hash_string(pk->name, NULL) +
573           silc_hash_string(pk->identifier, NULL) +
574           silc_hash_data(pk->pk, (void *)pk->pk_len));
575 }
576
577 /* Compares two strings. It may be used as SilcHashTable comparison
578    function. */
579
580 bool silc_hash_string_compare(void *key1, void *key2, void *user_context)
581 {
582   return !strcasecmp((char *)key1, (char *)key2);
583 }
584
585 /* Compares two ID's. May be used as SilcHashTable comparison function.
586    The Client ID's compares only the hash of the Client ID not any other
587    part of the Client ID. Other ID's are fully compared. */
588
589 bool silc_hash_id_compare(void *key1, void *key2, void *user_context)
590 {
591   SilcIdType id_type = (SilcIdType)(SilcUInt32)user_context;
592   return (id_type == SILC_ID_CLIENT ?
593           SILC_ID_COMPARE_HASH((SilcClientID *)key1, (SilcClientID *)key2) :
594           SILC_ID_COMPARE_TYPE(key1, key2, id_type));
595 }
596
597 /* Compare two Client ID's entirely and not just the hash from the ID. */
598
599 bool silc_hash_client_id_compare(void *key1, void *key2, void *user_context)
600 {
601   return SILC_ID_COMPARE_TYPE(key1, key2, SILC_ID_CLIENT);
602 }
603
604 /* Compares binary data. May be used as SilcHashTable comparison function. */
605
606 bool silc_hash_data_compare(void *key1, void *key2, void *user_context)
607 {
608   SilcUInt32 len = (SilcUInt32)user_context;
609   return !memcmp(key1, key2, len);
610 }
611
612 /* Compares two SILC Public keys. It may be used as SilcHashTable
613    comparison function. */
614
615 bool silc_hash_public_key_compare(void *key1, void *key2, void *user_context)
616 {
617   return silc_pkcs_public_key_compare(key1, key2);
618 }
619
620 /* Parses mode mask and returns the mode as string. */
621
622 char *silc_client_chmode(SilcUInt32 mode, const char *cipher, const char *hmac)
623 {
624   char string[100];
625
626   if (!mode)
627     return NULL;
628
629   memset(string, 0, sizeof(string));
630
631   if (mode & SILC_CHANNEL_MODE_PRIVATE)
632     strncat(string, "p", 1);
633
634   if (mode & SILC_CHANNEL_MODE_SECRET)
635     strncat(string, "s", 1);
636
637   if (mode & SILC_CHANNEL_MODE_PRIVKEY)
638     strncat(string, "k", 1);
639
640   if (mode & SILC_CHANNEL_MODE_INVITE)
641     strncat(string, "i", 1);
642
643   if (mode & SILC_CHANNEL_MODE_TOPIC)
644     strncat(string, "t", 1);
645
646   if (mode & SILC_CHANNEL_MODE_ULIMIT)
647     strncat(string, "l", 1);
648
649   if (mode & SILC_CHANNEL_MODE_PASSPHRASE)
650     strncat(string, "a", 1);
651
652   if (mode & SILC_CHANNEL_MODE_FOUNDER_AUTH)
653     strncat(string, "f", 1);
654
655   if (mode & SILC_CHANNEL_MODE_SILENCE_USERS)
656     strncat(string, "m", 1);
657
658   if (mode & SILC_CHANNEL_MODE_SILENCE_OPERS)
659     strncat(string, "M", 1);
660   
661   if (mode & SILC_CHANNEL_MODE_CIPHER)
662     strncat(string, "c", 1);
663   
664   if (mode & SILC_CHANNEL_MODE_HMAC)
665     strncat(string, "h", 1);
666
667   if (mode & SILC_CHANNEL_MODE_CIPHER) {
668     if (strlen(cipher) + strlen(string) + 1< sizeof(string)) {
669       strncat(string, " ", 1);
670       strncat(string, cipher, strlen(cipher));
671     }
672   }
673
674   if (mode & SILC_CHANNEL_MODE_HMAC) {
675     if (strlen(hmac) + strlen(string) + 1< sizeof(string)) {
676       strncat(string, " ", 1);
677       strncat(string, hmac, strlen(hmac));
678     }
679   }
680
681   /* Rest of mode is ignored */
682
683   return strdup(string);
684 }
685
686 /* Parses channel user mode mask and returns te mode as string */
687
688 char *silc_client_chumode(SilcUInt32 mode)
689 {
690   char string[64];
691
692   if (!mode)
693     return NULL;
694
695   memset(string, 0, sizeof(string));
696
697   if (mode & SILC_CHANNEL_UMODE_CHANFO)
698     strncat(string, "f", 1);
699
700   if (mode & SILC_CHANNEL_UMODE_CHANOP)
701     strncat(string, "o", 1);
702
703   if (mode & SILC_CHANNEL_UMODE_BLOCK_MESSAGES)
704     strncat(string, "b", 1);
705
706   if (mode & SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS)
707     strncat(string, "u", 1);
708
709   if (mode & SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS)
710     strncat(string, "r", 1);
711
712   if (mode & SILC_CHANNEL_UMODE_QUIET)
713     strncat(string, "q", 1);
714
715   return strdup(string);
716 }
717
718 /* Parses channel user mode and returns it as special mode character. */
719
720 char *silc_client_chumode_char(SilcUInt32 mode)
721 {
722   char string[64];
723
724   if (!mode)
725     return NULL;
726
727   memset(string, 0, sizeof(string));
728
729   if (mode & SILC_CHANNEL_UMODE_CHANFO)
730     strncat(string, "*", 1);
731
732   if (mode & SILC_CHANNEL_UMODE_CHANOP)
733     strncat(string, "@", 1);
734
735   if (mode & SILC_CHANNEL_UMODE_QUIET)
736     strncat(string, "&", 1);
737
738   return strdup(string);
739 }
740
741 /* Creates fingerprint from data, usually used with SHA1 digests */
742
743 char *silc_fingerprint(const unsigned char *data, SilcUInt32 data_len)
744 {
745   char fingerprint[64], *cp;
746   int i;
747
748   memset(fingerprint, 0, sizeof(fingerprint));
749   cp = fingerprint;
750   for (i = 0; i < data_len; i++) {
751     snprintf(cp, sizeof(fingerprint), "%02X", data[i]);
752     cp += 2;
753
754     if ((i + 1) % 2 == 0)
755       snprintf(cp++, sizeof(fingerprint), " ");
756
757     if ((i + 1) % 10 == 0)
758       snprintf(cp++, sizeof(fingerprint), " ");
759   }
760   i--;
761   if ((i + 1) % 2 == 0)
762     cp[-2] = 0;
763   if ((i + 1) % 10 == 0)
764     cp[-1] = 0;
765
766   return strdup(fingerprint);
767 }
768
769 /* Return TRUE if the `data' is ASCII string. */
770
771 bool silc_string_is_ascii(const unsigned char *data, SilcUInt32 data_len)
772 {
773   int i;
774
775   for (i = 0; i < data_len; i++) {
776     if (!isascii(data[i]))
777       return FALSE;
778   }
779
780   return TRUE;
781 }
782
783 /* Parses SILC protocol style version string. */
784
785 bool silc_parse_version_string(const char *version,
786                                SilcUInt32 *protocol_version,
787                                char **protocol_version_string,
788                                SilcUInt32 *software_version,
789                                char **software_version_string,
790                                char **vendor_version)
791 {
792   char *cp, buf[32];
793   int maj = 0, min = 0;
794
795   if (!strstr(version, "SILC-"))
796     return FALSE;
797
798   cp = (char *)version + 5;
799   if (!cp)
800     return FALSE;
801
802   /* Take protocol version */
803
804   maj = atoi(cp);
805   if (!strchr(cp, '.'))
806     return FALSE;
807   cp = strchr(cp, '.') + 1;
808   if (!cp || !(*cp))
809     return FALSE;
810   min = atoi(cp);
811
812   memset(buf, 0, sizeof(buf));
813   snprintf(buf, sizeof(buf) - 1, "%d%d", maj, min);
814   if (protocol_version)
815     *protocol_version = atoi(buf);
816   memset(buf, 0, sizeof(buf));
817   snprintf(buf, sizeof(buf) - 1, "%d.%d", maj, min);
818   if (protocol_version_string)
819     *protocol_version_string = strdup(buf);
820
821   /* Take software version */
822
823   maj = 0;
824   min = 0;
825   if (!strchr(cp, '-'))
826     return FALSE;
827   cp = strchr(cp, '-') + 1;
828   if (!cp || !(*cp))
829     return FALSE;
830
831   maj = atoi(cp);
832   if (strchr(cp, '.')) {
833     cp = strchr(cp, '.') + 1;
834     if (cp && *cp)
835       min = atoi(cp);
836   }
837
838   memset(buf, 0, sizeof(buf));
839   snprintf(buf, sizeof(buf) - 1, "%d%d", maj, min);
840   if (software_version)
841     *software_version = atoi(buf);
842   memset(buf, 0, sizeof(buf));
843   snprintf(buf, sizeof(buf) - 1, "%d.%d", maj, min);
844   if (software_version_string)
845     *software_version_string = strdup(buf);
846
847   /* Take vendor string */
848
849   if (strchr(cp, '.')) {
850     cp = strchr(cp, '.') + 1;
851     if (cp && *cp && vendor_version)
852       *vendor_version = strdup(cp);
853   }
854
855   return TRUE;
856 }
857
858 /* Converts version string x.x into number representation. */
859
860 SilcUInt32 silc_version_to_num(const char *version)
861 {
862   int maj = 0, min = 0;
863   char *cp, buf[32];
864
865   if (!version)
866     return 0;
867
868   cp = (char *)version;
869   maj = atoi(cp);
870   cp = strchr(cp, '.');
871   if (cp)
872     min = atoi(cp + 1);
873
874   memset(buf, 0, sizeof(buf));
875   snprintf(buf, sizeof(buf) - 1, "%d%d", maj, min);
876   return (SilcUInt32)atoi(buf);
877 }
878
879 /* Displays input prompt on command line and takes input data from user */
880
881 char *silc_get_input(const char *prompt, bool echo_off)
882 {
883 #ifdef SILC_UNIX
884   int fd;
885   char input[2048];
886
887   if (echo_off) {
888     char *ret = NULL;
889 #ifdef HAVE_TERMIOS_H
890     struct termios to;
891     struct termios to_old;
892
893     fd = open("/dev/tty", O_RDONLY);
894     if (fd < 0) {
895       fprintf(stderr, "silc: %s\n", strerror(errno));
896       return NULL;
897     }
898
899     signal(SIGINT, SIG_IGN);
900
901     /* Get terminal info */
902     tcgetattr(fd, &to);
903     to_old = to;
904
905     /* Echo OFF, and assure we can prompt and get input */
906     to.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
907     to.c_lflag |= ICANON;
908     to.c_cc[VMIN] = 255;
909     tcsetattr(fd, TCSANOW, &to);
910
911     memset(input, 0, sizeof(input));
912
913     printf("%s", prompt);
914     fflush(stdout);
915
916     if ((read(fd, input, sizeof(input))) < 0) {
917       fprintf(stderr, "silc: %s\n", strerror(errno));
918       tcsetattr(fd, TCSANOW, &to_old);
919       return NULL;
920     }
921
922     if (strlen(input) <= 1) {
923       tcsetattr(fd, TCSANOW, &to_old);
924       return NULL;
925     }
926
927     if (strchr(input, '\n'))
928       *strchr(input, '\n') = '\0';
929
930     /* Restore old terminfo */
931     tcsetattr(fd, TCSANOW, &to_old);
932     signal(SIGINT, SIG_DFL);
933
934     ret = silc_memdup(input, strlen(input));
935     memset(input, 0, sizeof(input));
936 #endif /* HAVE_TERMIOS_H */
937     return ret;
938   } else {
939     fd = open("/dev/tty", O_RDONLY);
940     if (fd < 0) {
941       fprintf(stderr, "silc: %s\n", strerror(errno));
942       return NULL;
943     }
944
945     memset(input, 0, sizeof(input));
946
947     printf("%s", prompt);
948     fflush(stdout);
949
950     if ((read(fd, input, sizeof(input))) < 0) {
951       fprintf(stderr, "silc: %s\n", strerror(errno));
952       return NULL;
953     }
954
955     if (strlen(input) <= 1)
956       return NULL;
957
958     if (strchr(input, '\n'))
959       *strchr(input, '\n') = '\0';
960
961     return strdup(input);
962   }
963 #else
964   return NULL;
965 #endif /* SILC_UNIX */
966 }
967
968 /* Return mode list */
969
970 bool silc_get_mode_list(SilcBuffer mode_list, SilcUInt32 mode_list_count,
971                         SilcUInt32 **list)
972 {
973   int i;
974
975   if (mode_list->len / 4 != mode_list_count)
976     return FALSE;
977
978   *list = silc_calloc(mode_list_count, sizeof(**list));
979
980   for (i = 0; i < mode_list_count; i++) {
981     SILC_GET32_MSB((*list)[i], mode_list->data);
982     silc_buffer_pull(mode_list, 4);
983   }
984
985   silc_buffer_push(mode_list, mode_list->data - mode_list->head);
986
987   return TRUE;
988 }
989
990 /* Status message structure. Messages are defined below. */
991 typedef struct {
992   SilcStatus status;
993   const char *message;
994 } SilcStatusMessage;
995
996 #define STAT(x) SILC_STATUS_ERR_##x
997 static const SilcStatusMessage silc_status_messages[] = {
998
999   { STAT(NO_SUCH_NICK),      "There was no such nickname" },
1000   { STAT(NO_SUCH_CHANNEL),   "There was no such channel" },
1001   { STAT(NO_SUCH_SERVER),    "There was no such server" },
1002   { STAT(INCOMPLETE_INFORMATION),  "Incomplete registration information" },
1003   { STAT(NO_RECIPIENT),      "No recipient given" },
1004   { STAT(UNKNOWN_COMMAND),   "Unknown command" },
1005   { STAT(WILDCARDS),         "Wilcrads not allowed" },
1006   { STAT(NO_CLIENT_ID),      "No Client ID given" },
1007   { STAT(NO_CHANNEL_ID),     "No Channel ID given" },
1008   { STAT(NO_SERVER_ID),      "No Server ID given" },
1009   { STAT(BAD_CLIENT_ID),     "Bad Client ID" },
1010   { STAT(BAD_CHANNEL_ID),    "Bad Channel ID" },
1011   { STAT(NO_SUCH_CLIENT_ID), "There is no such client" },
1012   { STAT(NO_SUCH_CHANNEL_ID),"There is no such channel" },
1013   { STAT(NICKNAME_IN_USE),   "Nickname already exists" },
1014   { STAT(NOT_ON_CHANNEL),    "You are not on that channel" },
1015   { STAT(USER_NOT_ON_CHANNEL),"They are not on the channel" },
1016   { STAT(USER_ON_CHANNEL),   "User already on the channel" },
1017   { STAT(NOT_REGISTERED),    "You have not registered" },
1018   { STAT(NOT_ENOUGH_PARAMS), "Not enough parameters" },
1019   { STAT(TOO_MANY_PARAMS),   "Too many parameters" },
1020   { STAT(PERM_DENIED),       "Permission denied" },
1021   { STAT(BANNED_FROM_SERVER),"You are not allowed to connect" },
1022   { STAT(BAD_PASSWORD),      "Cannot join channel. Incorrect password" },
1023   { STAT(CHANNEL_IS_FULL),   "Cannot join channel. Channel is full" },
1024   { STAT(NOT_INVITED),     "Cannot join channel. You have not been invited" },
1025   { STAT(BANNED_FROM_CHANNEL), "Cannot join channel. You have been banned" },
1026   { STAT(UNKNOWN_MODE),    "Unknown mode" },
1027   { STAT(NOT_YOU),         "Cannot change mode for other users" },
1028   { STAT(NO_CHANNEL_PRIV), "Permission denied. You are not channel operator" },
1029   { STAT(NO_CHANNEL_FOPRIV),"Permission denied. You are not channel founder" },
1030   { STAT(NO_SERVER_PRIV),  "Permission denied. You are not server operator" },
1031   { STAT(NO_ROUTER_PRIV),  "Permission denied. You are not SILC operator" },
1032   { STAT(BAD_NICKNAME),    "Bad nickname" },
1033   { STAT(BAD_CHANNEL),     "Bad channel name" },
1034   { STAT(AUTH_FAILED),     "Authentication failed" },
1035   { STAT(UNKNOWN_ALGORITHM), "Unsupported algorithm" },
1036   { STAT(NO_SUCH_SERVER_ID), "No such Server ID" },
1037   { STAT(RESOURCE_LIMIT), "No more free resources" },
1038   { STAT(NO_SUCH_SERVICE), "Service doesn't exist" },
1039   { STAT(NOT_AUTHENTICATED), "You have not been authenticated" },
1040   { STAT(BAD_SERVER_ID), "Server ID is not valid" },
1041   { STAT(KEY_EXCHANGE_FAILED), "Key exchange failed" },
1042   { STAT(BAD_VERSION), "Bad version" },
1043   { STAT(TIMEDOUT), "Service timed out" },
1044   { STAT(UNSUPPORTED_PUBLIC_KEY), "Unsupported public key type" },
1045   { STAT(OPERATION_ALLOWED), "Operation is not allowed" },
1046
1047   { 0, NULL }
1048 };
1049
1050 /* Returns status message string */
1051
1052 const char *silc_get_status_message(unsigned char status)
1053 {
1054   int i;
1055
1056   for (i = 0; silc_status_messages[i].message; i++) {
1057     if (silc_status_messages[i].status == status)
1058       break;
1059   }
1060
1061   if (silc_status_messages[i].message == NULL)
1062     return "";
1063
1064   return silc_status_messages[i].message;
1065 }
1066
1067 static const char *packet_name[] = {
1068   "NONE",
1069   "DISCONNECT",
1070   "SUCCESS",
1071   "FAILURE",
1072   "REJECT",
1073   "NOTIFY",
1074   "ERROR",
1075   "CHANNEL MESSAGE",
1076   "CHANNEL KEY",
1077   "PRIVATE MESSAGE",
1078   "PRIVATE MESSAGE KEY",
1079   "COMMAND",
1080   "COMMAND REPLY",
1081   "KEY EXCHANGE",
1082   "KEY EXCHANGE 1",
1083   "KEY EXCHANGE 2",
1084   "CONNECTION AUTH REQUEST",
1085   "CONNECTION AUTH",
1086   "NEW ID",
1087   "NEW CLIENT",
1088   "NEW SERVER",
1089   "NEW CHANNEL",
1090   "REKEY",
1091   "REKEY_DONE",
1092   "HEARTBEAT",
1093   "KEY AGREEMENT",
1094   "RESUME ROUTER",
1095   "FTP",
1096   "RESUME CLIENT",
1097 };
1098
1099 /* Returns packet type name */
1100
1101 const char *silc_get_packet_name(unsigned char type)
1102 {
1103   if (type >= SILC_PACKET_MAX)
1104     return "RESERVED";
1105   if (type >= SILC_PACKET_PRIVATE)
1106     return "PRIVATE RANGE";
1107   if (type > (sizeof(packet_name) / sizeof(*packet_name)))
1108     return "UNKNOWN";
1109   return packet_name[type];
1110 }
1111
1112 static const char *command_name[] = {
1113   "NONE",
1114   "WHOIS",
1115   "WHOWAS",
1116   "IDENTIFY",
1117   "NICK",
1118   "LIST",
1119   "TOPIC",
1120   "INVITE",
1121   "QUIT",
1122   "KILL",
1123   "INFO",
1124   "STATS",
1125   "PING",
1126   "OPER",
1127   "JOIN",
1128   "MOTD",
1129   "UMODE",
1130   "CMODE",
1131   "CUMODE",
1132   "KICK",
1133   "BAN",
1134   "DETACH",
1135   "WATCH",
1136   "SILCOPER",
1137   "LEAVE",
1138   "USERS",
1139   "GETKEY",
1140   "SERVICE",
1141 };
1142
1143 /* Returns command name */
1144
1145 const char *silc_get_command_name(unsigned char command)
1146 {
1147   if (command >= SILC_COMMAND_RESERVED)
1148     return "RESERVED";
1149   if (command >= SILC_COMMAND_PRIVATE)
1150     return "PRIVATE RANGE";
1151   if (command > (sizeof(command_name) / sizeof(*command_name)))
1152     return "UNKNOWN";
1153   return command_name[command];
1154 }
1155
1156 /* Return TRUE if `smaller' is smaller than `bigger'. */
1157
1158 bool silc_compare_timeval(struct timeval *smaller, 
1159                           struct timeval *bigger)
1160 {
1161   if ((smaller->tv_sec < bigger->tv_sec) ||
1162       ((smaller->tv_sec == bigger->tv_sec) &&
1163        (smaller->tv_usec < bigger->tv_usec)))
1164     return TRUE;
1165
1166   return FALSE;
1167 }