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