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