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