Merged silc_1_0_branch to trunk.
[silc.git] / lib / silcutil / silcutil.c
1 /*
2
3   silcutil.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 1997 - 2005 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 = silc_memdup(string, strlen(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 /* Hash UTF-8 string */
471
472 SilcUInt32 silc_hash_utf8_string(void *key, void *user_context)
473 {
474   unsigned char *s = (unsigned char *)key;
475   SilcUInt32 h = 0, g;
476
477   while (*s != '\0') {
478     h = (h << 4) + *s;
479     if ((g = h & 0xf0000000)) {
480       h = h ^ (g >> 24);
481       h = h ^ g;
482     }
483     s++;
484   }
485
486   return h;
487 }
488
489 /* Basic hash function to hash integers. May be used with the SilcHashTable. */
490
491 SilcUInt32 silc_hash_uint(void *key, void *user_context)
492 {
493   return *(SilcUInt32 *)key;
494 }
495
496 /* Basic hash funtion to hash pointers. May be used with the SilcHashTable. */
497
498 SilcUInt32 silc_hash_ptr(void *key, void *user_context)
499 {
500   return SILC_PTR_TO_32(key);
501 }
502
503 /* Hash a ID. The `user_context' is the ID type. */
504
505 SilcUInt32 silc_hash_id(void *key, void *user_context)
506 {
507   SilcIdType id_type = (SilcIdType)SILC_PTR_TO_32(user_context);
508   SilcUInt32 h = 0;
509   int i;
510
511   switch (id_type) {
512   case SILC_ID_CLIENT:
513     {
514       SilcClientID *id = (SilcClientID *)key;
515
516       /* The client ID is hashed by hashing the hash of the ID
517          (which is a truncated MD5 hash of the nickname) so that we
518          can access the entry from the cache with both Client ID but
519          with just a hash from the ID as well. */
520       return silc_hash_client_id_hash(id->hash, NULL);
521     }
522     break;
523   case SILC_ID_SERVER:
524     {
525       SilcServerID *id = (SilcServerID *)key;
526
527       h = id->port * id->rnd;
528       for (i = 0; i < id->ip.data_len; i++)
529         h ^= id->ip.data[i];
530
531       return h;
532     }
533     break;
534   case SILC_ID_CHANNEL:
535     {
536       SilcChannelID *id = (SilcChannelID *)key;
537
538       h = id->port * id->rnd;
539       for (i = 0; i < id->ip.data_len; i++)
540         h ^= id->ip.data[i];
541
542       return h;
543     }
544     break;
545   default:
546     break;
547   }
548
549   return h;
550 }
551
552 /* Hash Client ID's hash. */
553
554 SilcUInt32 silc_hash_client_id_hash(void *key, void *user_context)
555 {
556   int i;
557   unsigned char *hash = key;
558   SilcUInt32 h = 0, g;
559
560   for (i = 0; i < CLIENTID_HASH_LEN; i++) {
561     h = (h << 4) + hash[i];
562     if ((g = h & 0xf0000000)) {
563       h = h ^ (g >> 24);
564       h = h ^ g;
565     }
566   }
567
568   return h;
569 }
570
571 /* Hash binary data. The `user_context' is the data length. */
572
573 SilcUInt32 silc_hash_data(void *key, void *user_context)
574 {
575   SilcUInt32 len = SILC_PTR_TO_32(user_context), h = 0;
576   unsigned char *data = (unsigned char *)key;
577   int i;
578
579   h = (data[0] * data[len - 1] + 1) * len;
580   for (i = 0; i < len; i++)
581     h ^= data[i];
582
583   return h;
584 }
585
586 /* Hashed SILC Public key. */
587
588 SilcUInt32 silc_hash_public_key(void *key, void *user_context)
589 {
590   SilcPublicKey pk = (SilcPublicKey)key;
591   return (pk->len + (silc_hash_string(pk->name, NULL) ^
592                      silc_hash_string(pk->identifier, NULL) ^
593                      silc_hash_data(pk->pk, SILC_32_TO_PTR(pk->pk_len))));
594 }
595
596 /* Compares two strings. It may be used as SilcHashTable comparison
597    function. */
598
599 bool silc_hash_string_compare(void *key1, void *key2, void *user_context)
600 {
601   return !strcasecmp((char *)key1, (char *)key2);
602 }
603
604 /* Compares two ID's. May be used as SilcHashTable comparison function.
605    The Client ID's compares only the hash of the Client ID not any other
606    part of the Client ID. Other ID's are fully compared. */
607
608 bool silc_hash_id_compare(void *key1, void *key2, void *user_context)
609 {
610   SilcIdType id_type = (SilcIdType)SILC_PTR_TO_32(user_context);
611   return (id_type == SILC_ID_CLIENT ?
612           SILC_ID_COMPARE_HASH((SilcClientID *)key1, (SilcClientID *)key2) :
613           SILC_ID_COMPARE_TYPE(key1, key2, id_type));
614 }
615
616 /* Compare two Client ID's entirely and not just the hash from the ID. */
617
618 bool silc_hash_client_id_compare(void *key1, void *key2, void *user_context)
619 {
620   return SILC_ID_COMPARE_TYPE(key1, key2, SILC_ID_CLIENT);
621 }
622
623 /* Compares binary data. May be used as SilcHashTable comparison function. */
624
625 bool silc_hash_data_compare(void *key1, void *key2, void *user_context)
626 {
627   SilcUInt32 len = SILC_PTR_TO_32(user_context);
628   return !memcmp(key1, key2, len);
629 }
630
631 /* Compares UTF-8 string. */
632
633 bool silc_hash_utf8_compare(void *key1, void *key2, void *user_context)
634 {
635   int l1 = strlen((char *)key1);
636   int l2 = strlen((char *)key2);
637   if (l1 > l2)
638     l2 = l1;
639   return !memcmp(key1, key2, l2);
640 }
641
642 /* Compares two SILC Public keys. It may be used as SilcHashTable
643    comparison function. */
644
645 bool silc_hash_public_key_compare(void *key1, void *key2, void *user_context)
646 {
647   return silc_pkcs_public_key_compare(key1, key2);
648 }
649
650 /* Parses mode mask and returns the mode as string. */
651
652 char *silc_client_chmode(SilcUInt32 mode, const char *cipher, const char *hmac)
653 {
654   char string[100];
655
656   if (!mode)
657     return NULL;
658
659   memset(string, 0, sizeof(string));
660
661   if (mode & SILC_CHANNEL_MODE_PRIVATE)
662     strncat(string, "p", 1);
663
664   if (mode & SILC_CHANNEL_MODE_SECRET)
665     strncat(string, "s", 1);
666
667   if (mode & SILC_CHANNEL_MODE_PRIVKEY)
668     strncat(string, "k", 1);
669
670   if (mode & SILC_CHANNEL_MODE_INVITE)
671     strncat(string, "i", 1);
672
673   if (mode & SILC_CHANNEL_MODE_TOPIC)
674     strncat(string, "t", 1);
675
676   if (mode & SILC_CHANNEL_MODE_ULIMIT)
677     strncat(string, "l", 1);
678
679   if (mode & SILC_CHANNEL_MODE_PASSPHRASE)
680     strncat(string, "a", 1);
681
682   if (mode & SILC_CHANNEL_MODE_FOUNDER_AUTH)
683     strncat(string, "f", 1);
684
685   if (mode & SILC_CHANNEL_MODE_CHANNEL_AUTH)
686     strncat(string, "C", 1);
687
688   if (mode & SILC_CHANNEL_MODE_SILENCE_USERS)
689     strncat(string, "m", 1);
690
691   if (mode & SILC_CHANNEL_MODE_SILENCE_OPERS)
692     strncat(string, "M", 1);
693
694   if (mode & SILC_CHANNEL_MODE_CIPHER)
695     strncat(string, "c", 1);
696
697   if (mode & SILC_CHANNEL_MODE_HMAC)
698     strncat(string, "h", 1);
699
700   if (mode & SILC_CHANNEL_MODE_CIPHER) {
701     if (strlen(cipher) + strlen(string) + 1< sizeof(string)) {
702       strncat(string, " ", 1);
703       strncat(string, cipher, strlen(cipher));
704     }
705   }
706
707   if (mode & SILC_CHANNEL_MODE_HMAC) {
708     if (strlen(hmac) + strlen(string) + 1< sizeof(string)) {
709       strncat(string, " ", 1);
710       strncat(string, hmac, strlen(hmac));
711     }
712   }
713
714   /* Rest of mode is ignored */
715
716   return strdup(string);
717 }
718
719 /* Parses channel user mode mask and returns te mode as string */
720
721 char *silc_client_chumode(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, "f", 1);
732
733   if (mode & SILC_CHANNEL_UMODE_CHANOP)
734     strncat(string, "o", 1);
735
736   if (mode & SILC_CHANNEL_UMODE_BLOCK_MESSAGES)
737     strncat(string, "b", 1);
738
739   if (mode & SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS)
740     strncat(string, "u", 1);
741
742   if (mode & SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS)
743     strncat(string, "r", 1);
744
745   if (mode & SILC_CHANNEL_UMODE_QUIET)
746     strncat(string, "q", 1);
747
748   return strdup(string);
749 }
750
751 /* Parses channel user mode and returns it as special mode character. */
752
753 char *silc_client_chumode_char(SilcUInt32 mode)
754 {
755   char string[64];
756
757   if (!mode)
758     return NULL;
759
760   memset(string, 0, sizeof(string));
761
762   if (mode & SILC_CHANNEL_UMODE_CHANFO)
763     strncat(string, "*", 1);
764
765   if (mode & SILC_CHANNEL_UMODE_CHANOP)
766     strncat(string, "@", 1);
767
768   if (mode & SILC_CHANNEL_UMODE_QUIET)
769     strncat(string, "&", 1);
770
771   return strdup(string);
772 }
773
774 /* Creates fingerprint from data, usually used with SHA1 digests */
775
776 char *silc_fingerprint(const unsigned char *data, SilcUInt32 data_len)
777 {
778   char fingerprint[64], *cp;
779   int i;
780
781   memset(fingerprint, 0, sizeof(fingerprint));
782   cp = fingerprint;
783   for (i = 0; i < data_len; i++) {
784     snprintf(cp, sizeof(fingerprint), "%02X", data[i]);
785     cp += 2;
786
787     if ((i + 1) % 2 == 0)
788       snprintf(cp++, sizeof(fingerprint), " ");
789
790     if ((i + 1) % 10 == 0)
791       snprintf(cp++, sizeof(fingerprint), " ");
792   }
793   i--;
794   if ((i + 1) % 2 == 0)
795     cp[-2] = 0;
796   if ((i + 1) % 10 == 0)
797     cp[-1] = 0;
798
799   return strdup(fingerprint);
800 }
801
802 /* Return TRUE if the `data' is ASCII string. */
803
804 bool silc_string_is_ascii(const unsigned char *data, SilcUInt32 data_len)
805 {
806   int i;
807
808   for (i = 0; i < data_len; i++) {
809     if (!isascii(data[i]))
810       return FALSE;
811   }
812
813   return TRUE;
814 }
815
816 /* Parses SILC protocol style version string. */
817
818 bool silc_parse_version_string(const char *version,
819                                SilcUInt32 *protocol_version,
820                                char **protocol_version_string,
821                                SilcUInt32 *software_version,
822                                char **software_version_string,
823                                char **vendor_version)
824 {
825   char *cp, buf[32];
826   int maj = 0, min = 0;
827
828   if (!strstr(version, "SILC-"))
829     return FALSE;
830
831   cp = (char *)version + 5;
832   if (!cp)
833     return FALSE;
834
835   /* Take protocol version */
836
837   maj = atoi(cp);
838   if (!strchr(cp, '.'))
839     return FALSE;
840   cp = strchr(cp, '.') + 1;
841   if (!cp || !(*cp))
842     return FALSE;
843   min = atoi(cp);
844
845   memset(buf, 0, sizeof(buf));
846   snprintf(buf, sizeof(buf) - 1, "%d%d", maj, min);
847   if (protocol_version)
848     *protocol_version = atoi(buf);
849   memset(buf, 0, sizeof(buf));
850   snprintf(buf, sizeof(buf) - 1, "%d.%d", maj, min);
851   if (protocol_version_string)
852     *protocol_version_string = strdup(buf);
853
854   /* Take software version */
855
856   maj = 0;
857   min = 0;
858   if (!strchr(cp, '-'))
859     return FALSE;
860   cp = strchr(cp, '-') + 1;
861   if (!cp || !(*cp))
862     return FALSE;
863
864   maj = atoi(cp);
865   if (strchr(cp, '.')) {
866     cp = strchr(cp, '.') + 1;
867     if (cp && *cp)
868       min = atoi(cp);
869   }
870
871   memset(buf, 0, sizeof(buf));
872   snprintf(buf, sizeof(buf) - 1, "%d%d", maj, min);
873   if (software_version)
874     *software_version = atoi(buf);
875   memset(buf, 0, sizeof(buf));
876   snprintf(buf, sizeof(buf) - 1, "%d.%d", maj, min);
877   if (software_version_string)
878     *software_version_string = strdup(buf);
879
880   /* Take vendor string */
881
882   if (strchr(cp, '.')) {
883     cp = strchr(cp, '.') + 1;
884     if (cp && *cp && vendor_version)
885       *vendor_version = strdup(cp);
886   }
887
888   return TRUE;
889 }
890
891 /* Converts version string x.x into number representation. */
892
893 SilcUInt32 silc_version_to_num(const char *version)
894 {
895   int maj = 0, min = 0;
896   char *cp, buf[32];
897
898   if (!version)
899     return 0;
900
901   cp = (char *)version;
902   maj = atoi(cp);
903   cp = strchr(cp, '.');
904   if (cp)
905     min = atoi(cp + 1);
906
907   memset(buf, 0, sizeof(buf));
908   snprintf(buf, sizeof(buf) - 1, "%d%d", maj, min);
909   return (SilcUInt32)atoi(buf);
910 }
911
912 /* Displays input prompt on command line and takes input data from user */
913
914 char *silc_get_input(const char *prompt, bool echo_off)
915 {
916 #ifdef SILC_UNIX
917   int fd;
918   char input[2048];
919
920   if (echo_off) {
921     char *ret = NULL;
922 #ifdef HAVE_TERMIOS_H
923     struct termios to;
924     struct termios to_old;
925
926     fd = open("/dev/tty", O_RDONLY);
927     if (fd < 0) {
928       fprintf(stderr, "silc: %s\n", strerror(errno));
929       return NULL;
930     }
931
932     signal(SIGINT, SIG_IGN);
933
934     /* Get terminal info */
935     tcgetattr(fd, &to);
936     to_old = to;
937
938     /* Echo OFF, and assure we can prompt and get input */
939     to.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
940     to.c_lflag |= ICANON;
941     to.c_cc[VMIN] = 255;
942     tcsetattr(fd, TCSANOW, &to);
943
944     memset(input, 0, sizeof(input));
945
946     printf("%s", prompt);
947     fflush(stdout);
948
949     if ((read(fd, input, sizeof(input))) < 0) {
950       fprintf(stderr, "silc: %s\n", strerror(errno));
951       tcsetattr(fd, TCSANOW, &to_old);
952       return NULL;
953     }
954
955     if (strlen(input) <= 1) {
956       tcsetattr(fd, TCSANOW, &to_old);
957       return NULL;
958     }
959
960     if (strchr(input, '\n'))
961       *strchr(input, '\n') = '\0';
962
963     /* Restore old terminfo */
964     tcsetattr(fd, TCSANOW, &to_old);
965     signal(SIGINT, SIG_DFL);
966
967     ret = silc_memdup(input, strlen(input));
968     memset(input, 0, sizeof(input));
969 #endif /* HAVE_TERMIOS_H */
970     return ret;
971   } else {
972     fd = open("/dev/tty", O_RDONLY);
973     if (fd < 0) {
974       fprintf(stderr, "silc: %s\n", strerror(errno));
975       return NULL;
976     }
977
978     memset(input, 0, sizeof(input));
979
980     printf("%s", prompt);
981     fflush(stdout);
982
983     if ((read(fd, input, sizeof(input))) < 0) {
984       fprintf(stderr, "silc: %s\n", strerror(errno));
985       return NULL;
986     }
987
988     if (strlen(input) <= 1)
989       return NULL;
990
991     if (strchr(input, '\n'))
992       *strchr(input, '\n') = '\0';
993
994     return strdup(input);
995   }
996 #else
997   return NULL;
998 #endif /* SILC_UNIX */
999 }
1000
1001 /* Return mode list */
1002
1003 bool silc_get_mode_list(SilcBuffer mode_list, SilcUInt32 mode_list_count,
1004                         SilcUInt32 **list)
1005 {
1006   int i;
1007
1008   if (mode_list->len / 4 != mode_list_count)
1009     return FALSE;
1010
1011   *list = silc_calloc(mode_list_count, sizeof(**list));
1012
1013   for (i = 0; i < mode_list_count; i++) {
1014     SILC_GET32_MSB((*list)[i], mode_list->data);
1015     silc_buffer_pull(mode_list, 4);
1016   }
1017
1018   silc_buffer_push(mode_list, mode_list->data - mode_list->head);
1019
1020   return TRUE;
1021 }
1022
1023 /* Status message structure. Messages are defined below. */
1024 typedef struct {
1025   SilcStatus status;
1026   const char *message;
1027 } SilcStatusMessage;
1028
1029 #define STAT(x) SILC_STATUS_ERR_##x
1030 static const SilcStatusMessage silc_status_messages[] = {
1031
1032   { STAT(NO_SUCH_NICK),      "There was no such nickname" },
1033   { STAT(NO_SUCH_CHANNEL),   "There was no such channel" },
1034   { STAT(NO_SUCH_SERVER),    "There was no such server" },
1035   { STAT(INCOMPLETE_INFORMATION),  "Incomplete registration information" },
1036   { STAT(NO_RECIPIENT),      "No recipient given" },
1037   { STAT(UNKNOWN_COMMAND),   "Unknown command" },
1038   { STAT(WILDCARDS),         "Wilcrads not allowed" },
1039   { STAT(NO_CLIENT_ID),      "No Client ID given" },
1040   { STAT(NO_CHANNEL_ID),     "No Channel ID given" },
1041   { STAT(NO_SERVER_ID),      "No Server ID given" },
1042   { STAT(BAD_CLIENT_ID),     "Bad Client ID" },
1043   { STAT(BAD_CHANNEL_ID),    "Bad Channel ID" },
1044   { STAT(NO_SUCH_CLIENT_ID), "There is no such client" },
1045   { STAT(NO_SUCH_CHANNEL_ID),"There is no such channel" },
1046   { STAT(NICKNAME_IN_USE),   "Nickname already exists" },
1047   { STAT(NOT_ON_CHANNEL),    "You are not on that channel" },
1048   { STAT(USER_NOT_ON_CHANNEL),"They are not on the channel" },
1049   { STAT(USER_ON_CHANNEL),   "User already on the channel" },
1050   { STAT(NOT_REGISTERED),    "You have not registered" },
1051   { STAT(NOT_ENOUGH_PARAMS), "Not enough parameters" },
1052   { STAT(TOO_MANY_PARAMS),   "Too many parameters" },
1053   { STAT(PERM_DENIED),       "Permission denied" },
1054   { STAT(BANNED_FROM_SERVER),"You are not allowed to connect" },
1055   { STAT(BAD_PASSWORD),      "Cannot join channel. Incorrect password" },
1056   { STAT(CHANNEL_IS_FULL),   "Cannot join channel. Channel is full" },
1057   { STAT(NOT_INVITED),     "Cannot join channel. You have not been invited" },
1058   { STAT(BANNED_FROM_CHANNEL), "Cannot join channel. You have been banned" },
1059   { STAT(UNKNOWN_MODE),    "Unknown mode" },
1060   { STAT(NOT_YOU),         "Cannot change mode for other users" },
1061   { STAT(NO_CHANNEL_PRIV), "Permission denied. You are not channel operator" },
1062   { STAT(NO_CHANNEL_FOPRIV),"Permission denied. You are not channel founder" },
1063   { STAT(NO_SERVER_PRIV),  "Permission denied. You are not server operator" },
1064   { STAT(NO_ROUTER_PRIV),  "Permission denied. You are not SILC operator" },
1065   { STAT(BAD_NICKNAME),    "Bad nickname" },
1066   { STAT(BAD_CHANNEL),     "Bad channel name" },
1067   { STAT(AUTH_FAILED),     "Authentication failed" },
1068   { STAT(UNKNOWN_ALGORITHM), "Unsupported algorithm" },
1069   { STAT(NO_SUCH_SERVER_ID), "No such Server ID" },
1070   { STAT(RESOURCE_LIMIT), "No more free resources" },
1071   { STAT(NO_SUCH_SERVICE), "Service doesn't exist" },
1072   { STAT(NOT_AUTHENTICATED), "You have not been authenticated" },
1073   { STAT(BAD_SERVER_ID), "Server ID is not valid" },
1074   { STAT(KEY_EXCHANGE_FAILED), "Key exchange failed" },
1075   { STAT(BAD_VERSION), "Bad version" },
1076   { STAT(TIMEDOUT), "Service timed out" },
1077   { STAT(UNSUPPORTED_PUBLIC_KEY), "Unsupported public key type" },
1078   { STAT(OPERATION_ALLOWED), "Operation is not allowed" },
1079   { STAT(BAD_SERVER), "Bad server name" },
1080   { STAT(BAD_USERNAME), "Bad user name" },
1081
1082   { 0, NULL }
1083 };
1084
1085 /* Returns status message string */
1086
1087 const char *silc_get_status_message(unsigned char status)
1088 {
1089   int i;
1090
1091   for (i = 0; silc_status_messages[i].message; i++) {
1092     if (silc_status_messages[i].status == status)
1093       break;
1094   }
1095
1096   if (silc_status_messages[i].message == NULL)
1097     return "";
1098
1099   return silc_status_messages[i].message;
1100 }
1101
1102 static const char *packet_name[] = {
1103   "NONE",
1104   "DISCONNECT",
1105   "SUCCESS",
1106   "FAILURE",
1107   "REJECT",
1108   "NOTIFY",
1109   "ERROR",
1110   "CHANNEL MESSAGE",
1111   "CHANNEL KEY",
1112   "PRIVATE MESSAGE",
1113   "PRIVATE MESSAGE KEY",
1114   "COMMAND",
1115   "COMMAND REPLY",
1116   "KEY EXCHANGE",
1117   "KEY EXCHANGE 1",
1118   "KEY EXCHANGE 2",
1119   "CONNECTION AUTH REQUEST",
1120   "CONNECTION AUTH",
1121   "NEW ID",
1122   "NEW CLIENT",
1123   "NEW SERVER",
1124   "NEW CHANNEL",
1125   "REKEY",
1126   "REKEY_DONE",
1127   "HEARTBEAT",
1128   "KEY AGREEMENT",
1129   "RESUME ROUTER",
1130   "FTP",
1131   "RESUME CLIENT",
1132 };
1133
1134 /* Returns packet type name */
1135
1136 const char *silc_get_packet_name(unsigned char type)
1137 {
1138   if (type >= SILC_PACKET_MAX)
1139     return "RESERVED";
1140   if (type >= SILC_PACKET_PRIVATE)
1141     return "PRIVATE RANGE";
1142   if (type > (sizeof(packet_name) / sizeof(*packet_name)))
1143     return "UNKNOWN";
1144   return packet_name[type];
1145 }
1146
1147 static const char *command_name[] = {
1148   "NONE",
1149   "WHOIS",
1150   "WHOWAS",
1151   "IDENTIFY",
1152   "NICK",
1153   "LIST",
1154   "TOPIC",
1155   "INVITE",
1156   "QUIT",
1157   "KILL",
1158   "INFO",
1159   "STATS",
1160   "PING",
1161   "OPER",
1162   "JOIN",
1163   "MOTD",
1164   "UMODE",
1165   "CMODE",
1166   "CUMODE",
1167   "KICK",
1168   "BAN",
1169   "DETACH",
1170   "WATCH",
1171   "SILCOPER",
1172   "LEAVE",
1173   "USERS",
1174   "GETKEY",
1175   "SERVICE",
1176 };
1177
1178 /* Returns command name */
1179
1180 const char *silc_get_command_name(unsigned char command)
1181 {
1182   if (command >= SILC_COMMAND_RESERVED)
1183     return "RESERVED";
1184   if (command >= SILC_COMMAND_PRIVATE)
1185     return "PRIVATE RANGE";
1186   if (command > (sizeof(command_name) / sizeof(*command_name)))
1187     return "UNKNOWN";
1188   return command_name[command];
1189 }
1190
1191 /* Return TRUE if `smaller' is smaller than `bigger'. */
1192
1193 bool silc_compare_timeval(struct timeval *smaller,
1194                           struct timeval *bigger)
1195 {
1196   if ((smaller->tv_sec < bigger->tv_sec) ||
1197       ((smaller->tv_sec == bigger->tv_sec) &&
1198        (smaller->tv_usec < bigger->tv_usec)))
1199     return TRUE;
1200
1201   return FALSE;
1202 }