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