a191f46b0a24e7444577b914daa306791822d64d
[silc.git] / lib / silcutil / silcutil.c
1 /*
2
3   silcutil.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 1997 - 2007 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 /* Converts string to capital characters. */
82
83 SilcBool silc_to_upper(const char *string, char *dest, SilcUInt32 dest_size)
84 {
85   int i;
86
87   if (strlen(string) > dest_size)
88     return FALSE;
89
90   for (i = 0; i < strlen(string); i++)
91     dest[i] = (char)toupper((int)string[i]);
92
93   return TRUE;
94 }
95
96 /* Converts string to lower letter characters. */
97
98 SilcBool silc_to_lower(const char *string, char *dest, SilcUInt32 dest_size)
99 {
100   int i;
101
102   if (strlen(string) > dest_size)
103     return FALSE;
104
105   for (i = 0; i < strlen(string); i++)
106     dest[i] = (char)tolower((int)string[i]);
107
108   return TRUE;
109 }
110
111 /* Parse userfqdn string which is in user@fqdn format. */
112
113 int silc_parse_userfqdn(const char *string,
114                         char *user, SilcUInt32 user_size,
115                         char *fqdn, SilcUInt32 fqdn_size)
116 {
117   SilcUInt32 tlen;
118
119   if (!user && !fqdn)
120     return 0;
121
122   memset(user, 0, user_size);
123   memset(fqdn, 0, fqdn_size);
124
125   if (!string)
126     return 0;
127
128   if (string[0] == '@') {
129     if (user)
130       silc_strncat(user, user_size, string, strlen(string));
131
132     return 1;
133   }
134
135   if (strchr(string, '@')) {
136     tlen = strcspn(string, "@");
137
138     if (user)
139       silc_strncat(user, user_size, string, tlen);
140
141     if (fqdn)
142       silc_strncat(fqdn, fqdn_size, string + tlen + 1,
143                    strlen(string) - tlen - 1);
144
145     return 2;
146   }
147
148   if (user)
149     silc_strncat(user, user_size, string, strlen(string));
150
151   return 1;
152 }
153
154 /* Parses command line. At most `max_args' is taken. Rest of the line
155    will be allocated as the last argument if there are more than `max_args'
156    arguments in the line. Note that the command name is counted as one
157    argument and is saved. */
158
159 void silc_parse_command_line(unsigned char *buffer,
160                              unsigned char ***parsed,
161                              SilcUInt32 **parsed_lens,
162                              SilcUInt32 **parsed_types,
163                              SilcUInt32 *parsed_num,
164                              SilcUInt32 max_args)
165 {
166   int i, len = 0;
167   int argc = 0;
168   const char *cp = (const char *)buffer;
169   char *tmp;
170
171   *parsed = silc_calloc(1, sizeof(**parsed));
172   *parsed_lens = silc_calloc(1, sizeof(**parsed_lens));
173
174   /* Get the command first */
175   len = strcspn(cp, " ");
176   tmp = silc_calloc(strlen(cp) + 1, sizeof(*tmp));
177   if (!tmp)
178     return;
179   silc_to_upper(cp, tmp, strlen(cp));
180   (*parsed)[0] = silc_calloc(len + 1, sizeof(char));
181   memcpy((*parsed)[0], tmp, len);
182   silc_free(tmp);
183   (*parsed_lens)[0] = len;
184   cp += len;
185   while (*cp == ' ')
186     cp++;
187   argc++;
188
189   /* Parse arguments */
190   if (strchr(cp, ' ') || strlen(cp) != 0) {
191     for (i = 1; i < max_args; i++) {
192
193       if (i != max_args - 1)
194         len = strcspn(cp, " ");
195       else
196         len = strlen(cp);
197       while (len && cp[len - 1] == ' ')
198         len--;
199       if (!len)
200         break;
201
202       *parsed = silc_realloc(*parsed, sizeof(**parsed) * (argc + 1));
203       *parsed_lens = silc_realloc(*parsed_lens,
204                                   sizeof(**parsed_lens) * (argc + 1));
205       (*parsed)[argc] = silc_calloc(len + 1, sizeof(char));
206       memcpy((*parsed)[argc], cp, len);
207       (*parsed_lens)[argc] = len;
208       argc++;
209
210       cp += len;
211       if (strlen(cp) == 0)
212         break;
213       else
214         while (*cp == ' ')
215           cp++;
216     }
217   }
218
219   /* Save argument types. Protocol defines all argument types but
220      this implementation makes sure that they are always in correct
221      order hence this simple code. */
222   *parsed_types = silc_calloc(argc, sizeof(**parsed_types));
223   for (i = 0; i < argc; i++)
224     (*parsed_types)[i] = i;
225
226   *parsed_num = argc;
227 }
228
229 /* Formats arguments to a string and returns it after allocating memory
230    for it. It must be remembered to free it later. */
231
232 char *silc_format(char *fmt, ...)
233 {
234   va_list args;
235   char buf[8192];
236
237   memset(buf, 0, sizeof(buf));
238   va_start(args, fmt);
239   silc_vsnprintf(buf, sizeof(buf) - 1, fmt, args);
240   va_end(args);
241
242   return strdup(buf);
243 }
244
245 /* Basic has function to hash strings. May be used with the SilcHashTable.
246    Note that this lowers the characters of the string (with tolower()) so
247    this is used usually with nicknames, channel and server names to provide
248    case insensitive keys. */
249
250 SilcUInt32 silc_hash_string(void *key, void *user_context)
251 {
252   char *s = (char *)key;
253   SilcUInt32 h = 0, g;
254
255   while (*s != '\0') {
256     h = (h << 4) + tolower((int)*s);
257     if ((g = h & 0xf0000000)) {
258       h = h ^ (g >> 24);
259       h = h ^ g;
260     }
261     s++;
262   }
263
264   return h;
265 }
266
267 /* Hash UTF-8 string */
268
269 SilcUInt32 silc_hash_utf8_string(void *key, void *user_context)
270 {
271   unsigned char *s = (unsigned char *)key;
272   SilcUInt32 h = 0, g;
273
274   while (*s != '\0') {
275     h = (h << 4) + *s;
276     if ((g = h & 0xf0000000)) {
277       h = h ^ (g >> 24);
278       h = h ^ g;
279     }
280     s++;
281   }
282
283   return h;
284 }
285
286 /* Basic hash function to hash integers. May be used with the SilcHashTable. */
287
288 SilcUInt32 silc_hash_uint(void *key, void *user_context)
289 {
290   return SILC_PTR_TO_32(key);
291 }
292
293 /* Basic hash funtion to hash pointers. May be used with the SilcHashTable. */
294
295 SilcUInt32 silc_hash_ptr(void *key, void *user_context)
296 {
297   return SILC_PTR_TO_32(key);
298 }
299
300 /* Hash a ID. The `user_context' is the ID type. */
301
302 SilcUInt32 silc_hash_id(void *key, void *user_context)
303 {
304   SilcIdType id_type = (SilcIdType)SILC_PTR_TO_32(user_context);
305   SilcUInt32 h = 0;
306   int i;
307
308   switch (id_type) {
309   case SILC_ID_CLIENT:
310     {
311       SilcClientID *id = (SilcClientID *)key;
312
313       /* The client ID is hashed by hashing the hash of the ID
314          (which is a truncated MD5 hash of the nickname) so that we
315          can access the entry from the cache with both Client ID but
316          with just a hash from the ID as well. */
317       return silc_hash_client_id_hash(id->hash, NULL);
318     }
319     break;
320   case SILC_ID_SERVER:
321     {
322       SilcServerID *id = (SilcServerID *)key;
323
324       h = id->port * id->rnd;
325       for (i = 0; i < id->ip.data_len; i++)
326         h ^= id->ip.data[i];
327
328       return h;
329     }
330     break;
331   case SILC_ID_CHANNEL:
332     {
333       SilcChannelID *id = (SilcChannelID *)key;
334
335       h = id->port * id->rnd;
336       for (i = 0; i < id->ip.data_len; i++)
337         h ^= id->ip.data[i];
338
339       return h;
340     }
341     break;
342   default:
343     break;
344   }
345
346   return h;
347 }
348
349 /* Hash Client ID's hash. */
350
351 SilcUInt32 silc_hash_client_id_hash(void *key, void *user_context)
352 {
353   int i;
354   unsigned char *hash = key;
355   SilcUInt32 h = 0, g;
356
357   for (i = 0; i < CLIENTID_HASH_LEN; i++) {
358     h = (h << 4) + hash[i];
359     if ((g = h & 0xf0000000)) {
360       h = h ^ (g >> 24);
361       h = h ^ g;
362     }
363   }
364
365   return h;
366 }
367
368 /* Hash binary data. The `user_context' is the data length. */
369
370 SilcUInt32 silc_hash_data(void *key, void *user_context)
371 {
372   SilcUInt32 len = SILC_PTR_TO_32(user_context), h = 0;
373   unsigned char *data = (unsigned char *)key;
374   int i;
375
376   h = (data[0] * data[len - 1] + 1) * len;
377   for (i = 0; i < len; i++)
378     h ^= data[i];
379
380   return h;
381 }
382
383 /* Compares two strings. It may be used as SilcHashTable comparison
384    function. */
385
386 SilcBool silc_hash_string_compare(void *key1, void *key2, void *user_context)
387 {
388   return !strcasecmp((char *)key1, (char *)key2);
389 }
390
391 /* Compares two ID's. May be used as SilcHashTable comparison function.
392    The Client ID's compares only the hash of the Client ID not any other
393    part of the Client ID. Other ID's are fully compared. */
394
395 SilcBool silc_hash_id_compare(void *key1, void *key2, void *user_context)
396 {
397   SilcIdType id_type = (SilcIdType)SILC_PTR_TO_32(user_context);
398   return (id_type == SILC_ID_CLIENT ?
399           SILC_ID_COMPARE_HASH((SilcClientID *)key1, (SilcClientID *)key2) :
400           SILC_ID_COMPARE_TYPE(key1, key2, id_type));
401 }
402
403 /* Compares two ID's. Compares full IDs. */
404
405 SilcBool silc_hash_id_compare_full(void *key1, void *key2, void *user_context)
406 {
407   SilcIdType id_type = (SilcIdType)SILC_PTR_TO_32(user_context);
408   return SILC_ID_COMPARE_TYPE(key1, key2, id_type);
409 }
410
411 /* Compare two Client ID's entirely and not just the hash from the ID. */
412
413 SilcBool silc_hash_client_id_compare(void *key1, void *key2,
414                                      void *user_context)
415 {
416   return SILC_ID_COMPARE_TYPE(key1, key2, SILC_ID_CLIENT);
417 }
418
419 /* Compares binary data. May be used as SilcHashTable comparison function. */
420
421 SilcBool silc_hash_data_compare(void *key1, void *key2, void *user_context)
422 {
423   SilcUInt32 len = SILC_PTR_TO_32(user_context);
424   return !memcmp(key1, key2, len);
425 }
426
427 /* Compares UTF-8 string. */
428
429 SilcBool silc_hash_utf8_compare(void *key1, void *key2, void *user_context)
430 {
431   int l1 = strlen((char *)key1);
432   int l2 = strlen((char *)key2);
433   if (l1 != l2)
434     return FALSE;
435   return !memcmp(key1, key2, l2);
436 }
437
438 /* Creates fingerprint from data, usually used with SHA1 digests */
439
440 char *silc_fingerprint(const unsigned char *data, SilcUInt32 data_len)
441 {
442   char fingerprint[64], *cp;
443   int i;
444
445   memset(fingerprint, 0, sizeof(fingerprint));
446   cp = fingerprint;
447   for (i = 0; i < data_len; i++) {
448     silc_snprintf(cp, sizeof(fingerprint), "%02X", data[i]);
449     cp += 2;
450
451     if ((i + 1) % 2 == 0)
452       silc_snprintf(cp++, sizeof(fingerprint), " ");
453
454     if ((i + 1) % 10 == 0)
455       silc_snprintf(cp++, sizeof(fingerprint), " ");
456   }
457   i--;
458   if ((i + 1) % 2 == 0)
459     cp[-2] = 0;
460   if ((i + 1) % 10 == 0)
461     cp[-1] = 0;
462
463   return strdup(fingerprint);
464 }
465
466 /* Return TRUE if the `data' is ASCII string. */
467
468 SilcBool silc_string_is_ascii(const unsigned char *data, SilcUInt32 data_len)
469 {
470   int i;
471
472   for (i = 0; i < data_len; i++) {
473     if (!isascii(data[i]))
474       return FALSE;
475   }
476
477   return TRUE;
478 }
479
480 /* Displays input prompt on command line and takes input data from user */
481
482 char *silc_get_input(const char *prompt, SilcBool echo_off)
483 {
484 #ifdef SILC_UNIX
485   int fd;
486   char input[2048];
487
488   if (echo_off) {
489     char *ret = NULL;
490 #ifdef HAVE_TERMIOS_H
491     struct termios to;
492     struct termios to_old;
493
494     fd = open("/dev/tty", O_RDONLY);
495     if (fd < 0) {
496       fprintf(stderr, "silc: %s\n", strerror(errno));
497       return NULL;
498     }
499
500     signal(SIGINT, SIG_IGN);
501
502     /* Get terminal info */
503     tcgetattr(fd, &to);
504     to_old = to;
505
506     /* Echo OFF, and assure we can prompt and get input */
507     to.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
508     to.c_lflag |= ICANON;
509     to.c_cc[VMIN] = 255;
510     tcsetattr(fd, TCSANOW, &to);
511
512     memset(input, 0, sizeof(input));
513
514     printf("%s", prompt);
515     fflush(stdout);
516
517     if ((read(fd, input, sizeof(input))) < 0) {
518       fprintf(stderr, "silc: %s\n", strerror(errno));
519       tcsetattr(fd, TCSANOW, &to_old);
520       return NULL;
521     }
522
523     if (strlen(input) <= 1) {
524       tcsetattr(fd, TCSANOW, &to_old);
525       return NULL;
526     }
527
528     if (strchr(input, '\n'))
529       *strchr(input, '\n') = '\0';
530
531     /* Restore old terminfo */
532     tcsetattr(fd, TCSANOW, &to_old);
533     signal(SIGINT, SIG_DFL);
534
535     ret = silc_memdup(input, strlen(input));
536     memset(input, 0, sizeof(input));
537 #endif /* HAVE_TERMIOS_H */
538     return ret;
539   } else {
540     fd = open("/dev/tty", O_RDONLY);
541     if (fd < 0) {
542       fprintf(stderr, "silc: %s\n", strerror(errno));
543       return NULL;
544     }
545
546     memset(input, 0, sizeof(input));
547
548     printf("%s", prompt);
549     fflush(stdout);
550
551     if ((read(fd, input, sizeof(input))) < 0) {
552       fprintf(stderr, "silc: %s\n", strerror(errno));
553       return NULL;
554     }
555
556     if (strlen(input) <= 1)
557       return NULL;
558
559     if (strchr(input, '\n'))
560       *strchr(input, '\n') = '\0';
561
562     return strdup(input);
563   }
564 #else
565   return NULL;
566 #endif /* SILC_UNIX */
567 }
568
569 /* Hexdump */
570
571 void silc_hexdump(const unsigned char *data, SilcUInt32 data_len,
572                   FILE *output)
573 {
574   int i, k;
575   int off, pos, count;
576   int len = data_len;
577
578   k = 0;
579   pos = 0;
580   count = 16;
581   off = len % 16;
582   while (1) {
583     if (off) {
584       if ((len - pos) < 16 && (len - pos <= len - off))
585         count = off;
586     } else {
587       if (pos == len)
588         count = 0;
589     }
590     if (off == len)
591       count = len;
592
593     if (count)
594       fprintf(output, "%08X  ", k++ * 16);
595
596     for (i = 0; i < count; i++) {
597       fprintf(output, "%02X ", data[pos + i]);
598
599       if ((i + 1) % 4 == 0)
600         fprintf(output, " ");
601     }
602
603     if (count && count < 16) {
604       int j;
605
606       for (j = 0; j < 16 - count; j++) {
607         fprintf(output, "   ");
608
609         if ((j + count + 1) % 4 == 0)
610           fprintf(output, " ");
611       }
612     }
613
614     for (i = 0; i < count; i++) {
615       char ch;
616
617       if (data[pos] < 32 || data[pos] >= 127)
618         ch = '.';
619       else
620         ch = data[pos];
621
622       fprintf(output, "%c", ch);
623       pos++;
624     }
625
626     if (count)
627       fprintf(output, "\n");
628
629     if (count < 16)
630       break;
631   }
632 }
633
634 /* Convert hex string to data.  Each hex number must have two characters. */
635
636 SilcBool silc_hex2data(const char *hex, unsigned char *data,
637                        SilcUInt32 data_size, SilcUInt32 *ret_data_len)
638 {
639   char *cp = (char *)hex;
640   unsigned char l, h;
641   int i;
642
643   if (data_size < strlen(hex) / 2)
644     return FALSE;
645
646   for (i = 0; i < strlen(hex) / 2; i++) {
647     h = *cp++;
648     l = *cp++;
649
650     h -= h < 'A' ? '0' : 'A' - 10;
651     l -= l < 'A' ? '0' : 'A' - 10;
652
653     data[i] = (h << 4) | (l & 0xf);
654   }
655
656   if (ret_data_len)
657     *ret_data_len = i;
658
659   SILC_LOG_HEXDUMP(("len %d", i), data, i);
660
661   return TRUE;
662 }
663
664 /* Converts binary data to HEX string */
665
666 SilcBool silc_data2hex(const unsigned char *data, SilcUInt32 data_len,
667                        char *hex, SilcUInt32 hex_size)
668 {
669   unsigned char l, h;
670   char *cp = hex;
671   int i;
672
673   if (hex_size - 1 < data_len * 2)
674     return FALSE;
675
676   memset(hex, 0, hex_size);
677
678   for (i = 0; i < data_len; i++) {
679     l = data[i];
680     h = l >> 4;
681     l &= 0xf;
682
683     *cp++ = h + (h > 9 ? 'A' - 10 : '0');
684     *cp++ = l + (l > 9 ? 'A' - 10 : '0');
685   }
686
687   SILC_LOG_DEBUG(("HEX string: '%s'", hex));
688
689   return TRUE;
690 }