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