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