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